Winsockを使った非同期的なsend()

解決


へろり  2004-03-01 19:16:45  No: 53231

はじめまして。
初めての質問ですがよろしくお願いします。

現在インターネットプログラムの非同期ソケットについて勉強しています。
そこで表題の件ですが。

いろいろとあちこちのサイトを見ていると、非同期ソケットを使用時、send()関数は直ちに処理を返し、その戻り値は成功時に0。失敗時にはSOCKET_ERRORである。  とあります。

送信バッファに空きが出来たとき、WSAAsyncSelect()関数で指定したウィンドウへFD_WRITEが送られてきます。  このタイミングでsend()関数を呼んでいるのですが、今から送ろうとしているデータの量分、送信バッファに空きがあるのか。  実際に何バイト送信できたのか。  など、send()関数に指定したデータが全て送信されたのかどうかという事が分かりません。

それともsend()関数の第3引数に指定した分だけはきっちり送信して処理を返すのでしょうか?

どなたか分かる方いらっしゃいましたらよろしくお願いします。


九条  2004-03-01 19:52:46  No: 53232

MSDN のsend の説明より
If no error occurs, send returns the total number of bytes sent, which can be less than the number indicated by len for nonblocking sockets.

つーことで、send が成功した場合は送信したバイト数が返ります。
なお、FD_WRITE が送られた場合でも、 WSAEWOULDBLOCK で、send() が
失敗する場合がありますので、注意が必要です。


へろり  2004-03-02 00:45:49  No: 53233

返答ありがとうございます。
send()関数について思い違いをしていたようでお恥ずかしい限りです。
九条さんの返事を受けてFD_WRITEメッセージ取得時に下記のコードで対処したいと思います。

static char buf[1024] = "適当なデータ";
static int size = strlen(buf), wdsiz;

do{
    int ret;

    if((ret = send(socket, &buf[wdsiz], size, 0)) == SOCKET_ERROR){
        if(WSAGetLastError() != WSAEWOULDBLOCK){
            //エラーメッセージの表示
            return;
        }
    }
    wdsiz += ret;
    size -= ret;
}while(size);

buf、size、wdsizの変数はグローバルなものです。
このコードではsend()関数がSOCKET_ERRORを返した場合すぐにリターンして、次のFD_WRITEが飛んでくるのを待ちますが、成功時にはbufの内容を全て送信しようとループします。

今まで見たサイトでは概ね「非同期ソケットにおいて Winsock は、 send()呼出し失敗後に再び書き込みができるようになった時に FD_WRITE メッセージを送ります。」などとエラー時の事は書いてあるのですが、正常終了時の事は書いてありません。

また、サイトに載っているソースを見るとsend()関数がバッファの全てを送信出来なかった場合について、あまり配慮されてないように思えます。

そこで上記のコードについて質問なのですが、send()関数が正常に終了した場合でも、次のFD_WRITEを待たなければいけないのでしょうか?  それとも、上記のように成功する限りループしてもいいのでしょうか?

初歩的な問題を連発するようで申し訳ありませんが、どうぞよろしくお願いします。


九条  2004-03-02 11:37:21  No: 53234

私もよく知っているわけではないのですが、
MSDN WSAEventSelect の FD_WRITE の説明より
an application can assume that sends are possible starting from the first FD_WRITE network event setting and lasting until a send returns WSAEWOULDBLOCK.

とあるので、FD_WRITE がきたら、WSAEWOULDBLOCK がでるまで send を
呼んでも問題ないように思います。

なお、上記コードそのままですと WSAEWOULDBLOCK 時にループを抜けずに
データを出力しようとしてしまうので、その点だけ注意してください。
ついでに、WinSock2 に関しては以下の本がお勧めです。

WinSock2.0プログラミング
 —Window Socket APIによるネットワークプログラミングのすべて

値段がはるのと時代が古いのが難点ですが、私の知っている限り
WinSock2 の非同期通信を詳しく扱った唯一の日本語の本です(^^;;;


へろり  2004-03-03 01:11:25  No: 53235

九条さん。  今までおつきあい頂き本当にありがとうございました。
おかげさまで無事に解決することが出来ました。

> とあるので、FD_WRITE がきたら、WSAEWOULDBLOCK がでるまで send を
> 呼んでも問題ないように思います。

よくよく考えたらそうですよね。  先の発言をした後いろいろといじっていて気づいたのですが、FD_WRITEが飛んでくるタイミング上、一度FD_WRITEを無視してしまったり、FD_WRITEメッセージを受けsend()関数を呼んだがWSAEWOULDBLOCKも返らず、送信バッファにまだ空きがある場合、二度とFD_WRITEは飛んでこない事になります。
そもそも、次のFD_WRITEをひたすら待つと言うことが出来ないわけですね。

> なお、上記コードそのままですと WSAEWOULDBLOCK 時にループを抜けずに
> データを出力しようとしてしまうので、その点だけ注意してください。

うぁ、ものすごいケアレスミス……  穴があったら入りたい気分です(^^;;
SOCKET_ERRORが返ればすぐにリターンとか言っておきながらちっともそうなってませんね。
ご指摘ありがとうございます。

> WinSock2.0プログラミング
>  —Window Socket APIによるネットワークプログラミングのすべて

> 値段がはるのと時代が古いのが難点ですが、私の知っている限り
> WinSock2 の非同期通信を詳しく扱った唯一の日本語の本です(^^;;;

ご紹介いただいた書籍は近いうちにでも、図書館にでも行ってみようと思います。
#この手の本は平気でン千円もしますから……

今回は本当にありがとうございましたm(_ _)m


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加