• 締切済み

非ブロッキングソケットのrecvについて

現在、Winsock2を使ってあるサーバーのプログラムを作成しています。 WSAEventSelect関数を使って非ブロッキングソケットを扱っているのですが、複数のクライアントからほぼ同時に接続要求などがあった際に 接続を取りこぼしてしまいます。 どなたかアドバイスいただけませんでしょうか。 以下、ソースを示します。プログラムの動作をわかりやすくするためエラー処理などは省略して記載します。(実際には行っています) ちなみに非ブロッキングソケットを使用する理由は以下のとおりです。 ・GUIアプリのワーカースレッドで動作させており、ブロッキングソケットでブロック中にメインスレッドが終了してもワーカースレッドがそれを知る術がなくワーカースレッドを安全に終了させることができないため。 /* 変数の宣言*/ int iRet=-1; SOCKET ListenSock; SOCKET AcceptSock; int iRcvClientLen=0; int iRcvLen=0; char caRcvDat[1024]={0}; WSAEVENT hEvent; DWORD dwResult; WSANETWORKEVENTS events; /* リッスンソケットを作成*/ ListenSock = socket(AF_INET, SOCK_STREAM, 0); /* イベントのクリエイト*/ hEvent = WSACreateEvent(); /* リッスンソケットの設定*/ addr.sin_family = AF_INET; addr.sin_port = htons(PORT_NUM); /*PORT_NUMは定数*/ addr.sin_addr.S_un.S_addr = INADDR_ANY; /* リッスンソケットをバインド*/ iRet = bind(ListenSock, (struct sockaddr *)&addr, sizeof(addr)); /* リッスンソケットでポートを開く*/ iRet = listen(ListenSock, 10); /* クライアント接続待ちの無限ループ*/ while (1) { printf("\n\n/--------- 待機中 ---------/\n\n"); iRcvClientLen = sizeof(client); /* リッスンソケットにACCEPTイベントを設定*/ iRet = WSAEventSelect(ListenSock, hEvent, FD_ACCEPT); /* ACCEPTイベント発生まで待機*/ dwResult = WSAWaitForMultipleEvents(1, &hEvent, FALSE, WSA_INFINITE, FALSE); /* 発生したイベントを解析*/ iRet = WSAEnumNetworkEvents(ListenSock, hEvent, &events); /* イベント変数をリセット*/ WSAResetEvent(hEvent); /* 発生したイベントがACCEPTであれば接続を受け入れる*/ if(events.lNetworkEvents & FD_ACCEPT){ AcceptSock = accept(ListenSock, (struct sockaddr *)&client, &iRcvClientLen); } /* ACCEPTしたソケットにREADイベントを設定しなおす*/ iRet = WSAEventSelect(AcceptSock, hEvent, FD_READ); /* READイベント発生まで待機*/ dwResult = WSAWaitForMultipleEvents(1, &hEvent, FALSE, WSA_INFINITE, FALSE); /* 発生したイベントを解析*/ iRet = WSAEnumNetworkEvents(AcceptSock, hEvent, &events); /* イベント変数をリセット*/ WSAResetEvent(hEvent); /* 発生したイベントがREADであればデータを読み込む*/ if(events.lNetworkEvents & FD_READ){ iRcvLen=recv(AcceptSock, caRcvDat, 1024, 0); /* ・・・以下Recv後の動作・・・*/ } } おそらく、2回目のWSAWaitForMultipleEvents関数で待機している間に接続要求が来たクライアントを取りこぼしているのだと思いますが、 対処方法がわかりません。

みんなの回答

  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.1

WSAEventSelect()使ったことありませんが… # というかサーバ側プログラム書いたことないですが。 疑問点として… WSAResetEvent()をsocket関連の関数をコールする前に実施して問題ないのか? とか… >/* ACCEPTしたソケットにREADイベントを設定しなおす*/ >iRet = WSAEventSelect(AcceptSock, hEvent, FD_READ); イベントを「使い回す」のではなく、ListenSock用とAcceptSock用で別々に持ったらどうなのか? とか…

関連するQ&A

  • Winsock Connnect タイムアウト について

    OS :WinXP SP3 言語:VC++ 2005 Winsock2の通信モジュールを開発中です。 connectのタイムアウト方法をネットで調べて下記の関数を 作成(完全引用)したのですが、上手くいきません。 WSAWaitForMultipleEventsでイベントが発生していない (接続先が存在しない)のですが処理が返ってきてしまいます。 何か間違っているのでしょうか?ご回答、宜しくお願い致します。 ----------------------------------------------------------------BOOL connect2 ( SOCKET aSock, char* dwAddr, int wPort, int nTimeout ) { int liRet, liErr, liRetflag = FALSE; unsigned long lulAddr = inet_addr ( dwAddr ); WSAEVENT hEvent; DWORD dwRet; WSANETWORKEVENTS Events; sockaddr_in Addr; // イベント作成 hEvent = WSACreateEvent(); if ( hEvent == WSA_INVALID_EVENT ) { return FALSE; } // イベント型に(自動的にノンブロッキングになる) liRet = WSAEventSelect( aSock, hEvent, FD_CONNECT ); if( liRet == SOCKET_ERROR ) { WSACloseEvent( hEvent ); return FALSE; } // 接続 ZeroMemory( &Addr, sizeof( Addr ) ); Addr.sin_family = AF_INET; Addr.sin_port = htons( ( unsigned short )wPort ); Addr.sin_addr.s_addr = lulAddr; if ( connect( aSock, ( struct sockaddr* )&Addr, sizeof( Addr ) ) == SOCKET_ERROR ) { liErr = WSAGetLastError(); // WSAEWOULDBLOCKの時はまだ接続されていないので続ける if ( liErr != WSAEWOULDBLOCK ) { goto END; } } *** ↓ここですぐ返ってきてしまい、WSA_WAIT_TIMEOUTにならない *** // イベント発生待機 dwRet = WSAWaitForMultipleEvents( 1, &hEvent, FALSE, 60000, FALSE ); // dwRetがWSA_WAIT_EVENT_0以外でエラー // dwRetがWSA_WAIT_TIMEOUTの時タイムアウト if ( dwRet != WSA_WAIT_EVENT_0 ) { goto END; } // どのイベントが発生したか liRet = WSAEnumNetworkEvents( aSock, hEvent, &Events ); if( liRet == SOCKET_ERROR ) goto END; // Events.iliErrorCode[FD_CONNECT_BIT]が0の場合成功 if( ( Events.lNetworkEvents & FD_CONNECT ) && Events.iErrorCode[FD_CONNECT_BIT] == 0 ) { liRetflag = TRUE; } END: // イベント型を終了 WSAEventSelect( aSock, NULL, 0 ); WSACloseEvent( hEvent ); // ブロッキングに戻す(必要なら) dwRet = 0; ioctlsocket( aSock, FIONBIO, &dwRet ); return liRetflag; } ----------------------------------------------------------------

  • ソケットプログラミングのrecv時にFD_READイベントが終わらない

    現在Winsockを用いているのですが、クライアントからのデータを受信したときにおこるFD_READイベントをWSAWaitForMultipleEventで受け取って処理しているのですが、recvからの戻り値が常に0にも関わらずFD_READイベントが発生し続けます。クライアント側での送信は成功しています。 このような事態が発生する原因としてはどのようなことが考えられますでしょうか?

  • ソケットのclose

    サーバ側のソケットのcloseに関しての質問です。 LISTEN のFDをCLOSEすればACCEPTで発生したFDもなくなるものでしょうか。それとも残ってしまうものでしょうか?

  • winsockの非同期処理について

    winsockで双方向通信のため、非同期処理を行っているのですが、うまくいきません。 クライアント側で、WSAAsyncSelectの処理の後、Connectを呼んでいるのですが、Connectでエラーメッセージを返します。(エラーナンバー: 10035) サーバ側はaccept処理はうまくいっているのですが、accept後うまく処理を返していないためだと思うのですが... で質問は、 1.クライアント側、サーバ側にそれぞれ、WSAAsyncSelect を記載しても問題ないか? クライアント側はConnect前で宣言。 サーバ側はbind前で宣言しています。 2.エラー番号10035は非ブロッキングモードで処理されないためにおきています。 Connectで非ブロッキングモード処理がされていないためだと思うのですが、他に記述しないといけない関数があるのでしょうか? クライアント側: // 非同期処理 if(WSAAsyncSelect(client_s,this->m_hWnd, FM_TCPPROC, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR){ return FALSE; } // コネクト処理 memset(&client,0,sizeof(client)); client.sin_family = AF_INET; client.sin_addr.s_addr = inet_addr(IpAddress); client.sin_port = htons(PORT_NO); if(connect(client_s, (LPSOCKADDR)&client, sizeof(client)) == SOCKET_ERROR){ <-- ここでエラー long ErrNum = WSAGetLastError(); <-- ここでエラーNoがかえる。 return FALSE; }else{ NetFlg = true; } お分かりになる方教えてくださいませ。よろしくお願いします。

  • ソケットプログラミングとスレッドについて

    TCP/IPソケットを用いた通信プログラムを作成しています。その上でacceptする処理を専用スレッドにしており、accept後の受信処理をまた別のスレッドで処理しています。コンソールプログラムにおいてサーバとクライアントを用意しテストをしてみるとうまくいくのですが、MFCプログラムにおいて同じネットワーク処理を行ってみると、サーバ側へのconnectは成功するのですが、クライアント側からsendしたときにサーバ側でrecvの戻り値が必ず0にしかならず切断された状態になってしまいます。これはどういったときに発生すると考えられるでしょうか?ちなみにMFCが絡んでいるかどうかということは特にわかっておりません。 どなたかこのような経験をお持ちであればご教授お願い致します。

  • VC6.0ソケット通信プログラムについて

    お世話になります。 VC6.0でソケット通信の社内の既存のプログラムを改善しております。 クライアントより、サーバにソケットで大量に電文を送信した時に、サーバ側の処理が重くなってしまいます。 NETSTATコマンドで確認したところ、 サーバ側では、CLOSE_WAITが大量に出力されており クライアント側では、FIN_WAIT_2が大量に出力されます。 クライアント側のFIN_WAIT_2は、時間が経つと消えるのですが、サーバ側のCLOSE_WAITは残ってしまっております。 サーバ側のEXEを再起動すると、CLOSE_WAITは消えます。 サーバ側のソケットの受信後の処理に問題があるのではと考えておりますが、何処が問題なのかわからない状態です。 コードを以下に記述致します。 WINAPI K010cListener{ /*Winsock初期化*/ WSAStartup(wVersionRequested,&wsaData); /*SOCKET作成*/ fd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) /*ポートの設定*/ addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_family = AF_INET; addr.sin_port = htons(usPortNo); addrlen = sizeof(addr); /*BIND*/ bind(fd,(struct sockaddr*)&addr,addrlen) /*LISTEN*/ listen(fd,0) while(1){ /*ACCEPT*/ fd2 = accept(fd,(struct sockaddr*)&addr,&addrlen) //データ受信 waitReceiveThread(fd2, &rcv_msg); //DBオープン ConnectDB() //DB登録 AddTable(&rcv_msg) //DBクローズ CloseDB() } /*Winsock終了*/ WSACleanup(); } void waitReceiveThread(SOCKET fd, SockRcvMsg *rcv_msg) { while (1) { /*データ受信待ち*/ rcvSize = recv(fd,rcvBuf,sizeof(rcvBuf),0); if (rcvSize <= 0) { continue; } else { /* 返信電文の準備 */ memset(sndBuf,0x20,sizeof(sndBuf)); memcpy(sndBuf ,"RCMORDER00004800000000",22); /* 返信電文の送信 */ send(fd,(char*)sndBuf,sizeof(sndBuf),0); /* バッファの初期化 */ bufsize=0; memset(buf,'\0',sizeof(buf)); break; /*ループを抜ける*/ } } } 恐れ入りますが、ご教授いただければ幸いです。 宜しくお願い申し上げます。

  • 延々と受信し続けるwinsockのrecv

    以下のサーバープログラムを用いて、 (acceptしたらスレッド作成し、作成が終ったということをクライアントに知らせるために文字列送信後受信待機している) #include <winsock2.h> #include <vector> #include <process.h> #include <algorithm> char *Ver = "0.00"; using namespace std; unsigned __stdcall Patch( void *Sock ); int main() { ----WSADATA wsaData; --------WSAStartup( MAKEWORD(2,0), &wsaData ); ----SOCKET RecvSock = socket( AF_INET, SOCK_STREAM, 0 ); ----SOCKET SendSockBuf; ----vector<SOCKET> SendSock; ----fd_set ConnectFds, SubConnectFds; ----struct sockaddr_in Recv, Send; ----int len = sizeof(Send); ----Recv.sin_family = AF_INET; ----Recv.sin_port = htons(55555); ----Recv.sin_addr.S_un.S_addr = INADDR_ANY; --------bind(RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) ); ----FD_ZERO( &SubConnectFds ); --------FD_SET( RecvSock, &SubConnectFds ); ----listen( RecvSock, 5 ); ----vector<unsigned int> thID; ----vector<HANDLE> hTh; ----struct timeval tv; --------tv.tv_sec = 0; --------tv.tv_usec = 0; ----while(1) ----{ --------memcpy( &ConnectFds, &SubConnectFds, sizeof(fd_set) ); --------select( 0, &ConnectFds, NULL, NULL, &tv ); --------if ( FD_ISSET(RecvSock, &ConnectFds) ) --------{ ------------SendSockBuf = accept(RecvSock, (struct sockaddr *)&Send, &len); ------------if( SendSockBuf != INVALID_SOCKET) ------------{ ----------------SendSock.push_back( SendSockBuf ); ----------------thID.push_back( hTh.size() ); ----------------hTh.push_back( (HANDLE)_beginthreadex(NULL, 0, Patch, &SendSock[hTh.size()], 0, &thID[hTh.size()]) ); ------------} --------} ----} ----closesocket(RecvSock); ----WSACleanup(); ----return 0; } unsigned __stdcall Patch( void *Sock ) { ----SOCKET *SendSock = (SOCKET *)Sock; ----send( *SendSock, Ver, 5, 0 ); ----char Str[5]; ----while( Flag == 0 ) ----{ --------recv( *SendSock, Str, 5, 0 ); ----} ----closesocket( *SendSock ); ----return 0; } 以下のようなクライアントプログラムで文字を送信すると、 #include <WinSock2.h> using namespace std; void main() { ----WSADATA wsaData; --------WSAStartup( MAKEWORD(2,0), &wsaData ); ----SOCKET Sock = socket( AF_INET, SOCK_STREAM, 0 ); ----struct sockaddr_in Addr; --------Addr.sin_family = AF_INET; --------Addr.sin_port = htons(55555); --------Addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); ----connect( Sock, (struct sockaddr *)&Addr, sizeof(Addr) ); ----char str[5]; ----recv( Sock, str, 5, 0 ); ----send( Sock, "0.00", 5, 0); ----shutdown( Sock, 2 ); ----closesocket( Sock ); } クライアントプログラムは終了しているのにサーバープログラムは"0.00"を受信し続けます。 どこがおかしいのか分からないので教えてください

  • c言語のチャットプログラムのsendとrecv

    現在、複数のクライアントからサーバーにメッセージを送りサーバーからクライアントにメッセージを送るというものを作成しているのですが、クライアントからサーバーにはメッセージを送れるのですが、サーバー側からクライアント1人にしか送れず、クライアント全員にメッセージを送信できませんので、よろしければアドバイスをお願いします サーバーのプログラム インクルード省略 #define BUF_LEN 256 typedef struct CLIENT_INFO { char hostname[BUF_LEN]; char ipaddr[BUF_LEN]; int port; time_t last_access; } CLIENT_INFO; CLIENT_INFO client_info[FD_SETSIZE]; int listening_socket; struct sockaddr_in sn; int accept_new_client(int sock){ int len; int new_socket; struct hostent *peer_host; struct sockaddr_in peer_sin; len = sizeof(sn); new_socket = accept(listening_socket, (struct sockaddr *)&sn, &len); if ( new_socket == -1 ){ perror("accept"); exit(1); } if ( new_socket > FD_SETSIZE-1 ){ return -1; } len = sizeof(peer_sin); getpeername(new_socket, (struct sockaddr *)&peer_sin, &len); peer_host = gethostbyaddr((char *)&peer_sin.sin_addr.s_addr, sizeof(peer_sin.sin_addr), AF_INET); strncpy(client_info[new_socket].hostname, peer_host->h_name, sizeof client_info[new_socket].hostname); strncpy(client_info[new_socket].ipaddr, inet_ntoa(peer_sin.sin_addr), sizeof client_info[new_socket].ipaddr); client_info[new_socket].port = ntohs(peer_sin.sin_port); time(&client_info[new_socket].last_access); printf("接続: %s (%s) ポート %d ディスクリプタ %d 番\n", client_info[new_socket].hostname, client_info[new_socket].ipaddr, client_info[new_socket].port, new_socket); return new_socket; } int read_and_reply(int sock){ int read_size; char buf[BUF_LEN]; read_size = read(sock, buf, sizeof(buf)-1); if ( read_size == 0 || read_size == -1 ){ printf("%s (%s) ポート %d ディスクリプタ %d 番からの接続が切れました。\n", client_info[sock].hostname, client_info[sock].ipaddr, client_info[sock].port, sock); close(sock); client_info[sock].last_access = 0; } else { buf[read_size] = '\0'; printf("%s (%s) ポート %d ディスクリプタ %d 番からのメッセージ: %s", client_info[sock].hostname, client_info[sock].ipaddr, client_info[sock].port, sock, buf); write(sock, buf, strlen(buf)); time(&client_info[sock].last_access); } return read_size; } int main(){ fd_set target_fds; fd_set org_target_fds; int sock_optval = 1; int port = 5000;    listening_socket = socket(AF_INET, SOCK_STREAM, 0); if ( setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, &sock_optval, sizeof(sock_optval)) == -1 ){ perror("setsockopt"); exit(1); }  sn.sin_family = AF_INET; sn.sin_port = htons(port); sn.sin_addr.s_addr = htonl(INADDR_ANY); if ( bind(listening_socket, (struct sockaddr *)&sn, sizeof(sn)) < 0 ){ perror("bind"); exit(1); }   if ( listen(listening_socket, SOMAXCONN) == -1 ){ perror("listen"); exit(1); printf("ポート %d を見張ります。\n", port);   FD_ZERO(&org_target_fds); FD_SET(listening_socket, &org_target_fds);   while (1){ int i; time_t now_time; struct timeval waitval; waitval.tv_sec = 2; waitval.tv_usec = 500;  memcpy(&target_fds, &org_target_fds, sizeof(org_target_fds));  select(FD_SETSIZE, &target_fds, NULL, NULL, &waitval);  for ( i=0 ; i<FD_SETSIZE ; i++ ) { if ( FD_ISSET(i, &target_fds) ) { printf("ディスクリプタ %d 番が読み込み可能です。\n", i); if ( i == listening_socket ) { int new_sock; new_sock = accept_new_client(i); if ( new_sock != -1 ) { FD_SET(new_sock, &org_target_fds); } } else {int read_size;  read_size = read_size; read_size = read_and_reply(i); if ( read_size == -1 || read_size == 0 ) { FD_CLR(i, &org_target_fds); } } } } time(&now_time); for ( i=0 ; i<FD_SETSIZE ; i++){ if ( ! FD_ISSET(i, &org_target_fds) ) continue; if ( i == listening_socket ) continue; if ( now_time-60 > client_info[i].last_access ) { close(i); FD_CLR(i, &org_target_fds); } } } close(listening_socket); FD_CLR(i, &org_target_fds); } }

  • ソケット通信でチャットプログラム(unix c言語)

    c言語のソケット通信でチャットプログラムを作り中です 仕様:あるクライアントからメッセージがサーバに送信された時点でサーバは接続されたいる全てのクライアントにメッセージを送信する。 以下のようにクライアントからの接続(accept)がある度に、その返り値であるファイルディスクリプタにに対するreadと全クライアントに対するwriteの処理を行うchild関数をforkで起動します。これだとforkが起動した時点での全てのファイルディスクリプタ(接続されている全クライアント)の情報をchild関数に渡せますが、それ以降増え続けていくファイルディスクリプタの情報をchild関数に渡せないので、最初の方に接続したクライアントからのメッセージをそれ以降接続した他のクライアントに送信できないという状態です。 main() {   socket()   bind()   listen()     while(1){       accept()       if(fork()==0) {        child()       }     } } child() {   select()   if(FD_ISSET()) {     read()     write()   } } ※forkやソケット通信に関してかなり初心者なので、ソースや説明分が意味不明かもしれませんがよろしくお願いします。

  • タイマー関数とマルチスレッドについて

    こんばんわ。 マルチスレッドプログラミングを行なっています。 コンパイラはVC++.NETでC言語を利用しwin32 apiを用いています。 スレッドを_beginthreadex関数でスレッドを2つ生成し、WaitForSingleObject,SetEvent,ResetEvent関数によるイベント同期を利用し、2スレッドの同期をはかっています。 [質問内容] 以下のように、スレッド2でWaitForSingleObject関数にてスレッド2を待機状態にし、スレッド1のSetEvent関数にて待機しているスレッド2を再開させる方法をとっています。 例えば、タイマーをスタート後に待機状態になり、スレッド2が再開後タイマーストップするとした場合、スレッド2が待機状態のときもタイマーを動き続けているのでしょうか? ・スレッド1 SetEvent(hEvent[0]); ・スレッド2 SUSPEND_INTERVAL=20 if(Num%THREAD_SUSPEND_INTERVAL==0){ timestart=timeGetTime();←タイマースタート WaitForSingleObject(hEvent[0],INFINITE); ResetEvent(hEvent[0]); timestop=timeGetTime();←タイマーストップ } よろしくお願いします。