首页 > 代码库 > 问题解决——WSAAsyncSelect模型不触发FD_CLOSE

问题解决——WSAAsyncSelect模型不触发FD_CLOSE

==================================声明==================================

本文原创,转载在正文中显要的注明作者和出处,并保证文章的完整性。

未经作者同意请勿修改(包括本声明),保留法律追究的权利。

本文不定期修正完善,为保证内容正确,建议移步原文处阅读。

本文链接:http://www.cnblogs.com/wlsandwho/p/4228894.html

=======================================================================

 最近有个同学辞职了,在QQ空间里说要北上找工作,问何为,答曰,“年前辞职,年后上班”。

这样真的好吗?

=======================================================================

 最近在写一个小东西,需要用到非阻塞模式的套接字,考虑到用的MFC界面而且信息量不是很大很长很吓人,就选用了WSAAsyncSelect模型。

=======================================================================

考虑到1对N的情形,在每次accept后都维护一下map<socket,ClientInfo>的存储结构,自然的,每当客户端正常关闭都要再次更新这个结构。

=======================================================================

“每当客户端正常关闭”,会调用shutdown和closesocket,于是需要处理FD_CLOSE。

=======================================================================

可怕的是,FD_CLOSE的分支没有触发。

=======================================================================

那么问题来了,究竟为何没有触发?

我看了下监听后的WSAAsyncSelect:

1 WSAAsyncSelect(m_sockListen,hWnd,m_wMsgServer,FD_ACCEPT|FD_CLOSE)

又看了下接受连接后的WSAAsyncSelect

1 WSAAsyncSelect(sockClient, hWnd, m_wMsgServer, FD_READ|FD_WRITE|FD_CLOSE)

感觉没什么问题啊,好奇怪。

=======================================================================

稍微看了下自己的代码后,决定搜一下网上的资料,自然的无果。

只好对照《Windows网络编程技术》的源码看看有什么忽略的地方。

贴一下书本的源码先:(要用UNICODE还要稍微改改。)

  1 // Module Name: asyncselect.cpp  2 //  3 // Description:  4 //  5 //    This sample illustrates how to develop a simple echo server Winsock  6 //    application using the WSAAsyncSelect() I/O model. This sample is  7 //    implemented as a console-style application (to reduce the programming  8 //    complexity of writing a real Windows application) and simply prints  9 //    messages when connections are established and removed from the server. 10 //    The application listens for TCP connections on port 5150 and accepts them 11 //    as they arrive. When this application receives data from a client, it 12 //    simply echos (this is why we call it an echo server) the data back in 13 //    it‘s original form until the client closes the connection. 14 // 15 //    Since the WSAAsyncSelect I/O model requires an application to manage 16 //    window messages when network event occur, this application creates 17 //    a window for the I/O model only. The window stays hidden during the 18 //    entire execution of this application. 19 // 20 // Compile: 21 // 22 //    cl -o asyncselect asyncselect.cpp ws2_32.lib user32.lib gdi32.lib 23 // 24 // Command Line Options: 25 // 26 //    asyncselect.exe  27 // 28 //    Note: There are no command line options for this sample. 29  30 #include <winsock2.h> 31 #include <windows.h> 32 #include <stdio.h> 33 #include <conio.h> 34  35 #define PORT 5150 36 #define DATA_BUFSIZE 8192 37  38 typedef struct _SOCKET_INFORMATION { 39    BOOL RecvPosted; 40    CHAR Buffer[DATA_BUFSIZE]; 41    WSABUF DataBuf; 42    SOCKET Socket; 43    DWORD BytesSEND; 44    DWORD BytesRECV; 45    _SOCKET_INFORMATION *Next; 46 } SOCKET_INFORMATION, * LPSOCKET_INFORMATION; 47  48 #define WM_SOCKET (WM_USER + 1) 49  50 void CreateSocketInformation(SOCKET s); 51 LPSOCKET_INFORMATION GetSocketInformation(SOCKET s); 52 void FreeSocketInformation(SOCKET s); 53  54 HWND MakeWorkerWindow(void); 55 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 56  57 LPSOCKET_INFORMATION SocketInfoList; 58  59 void main(void) 60 { 61    MSG msg; 62    DWORD Ret; 63    SOCKET Listen; 64    SOCKADDR_IN InternetAddr; 65    HWND Window; 66    WSADATA wsaData; 67  68    if ((Window = MakeWorkerWindow()) == NULL) 69       return; 70  71    // Prepare echo server 72  73    if ((Ret = WSAStartup(0x0202, &wsaData)) != 0) 74    { 75       printf("WSAStartup failed with error %d\n", Ret); 76       return; 77    } 78  79    if ((Listen = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 80    { 81       printf("socket() failed with error %d\n", WSAGetLastError()); 82       return; 83    }  84  85    WSAAsyncSelect(Listen, Window, WM_SOCKET, FD_ACCEPT|FD_CLOSE); 86  87    InternetAddr.sin_family = AF_INET; 88    InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); 89    InternetAddr.sin_port = htons(PORT); 90  91    if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) 92    { 93       printf("bind() failed with error %d\n", WSAGetLastError()); 94       return; 95    } 96  97    if (listen(Listen, 5)) 98    { 99       printf("listen() failed with error %d\n", WSAGetLastError());100       return;101    }102              103    // Translate and dispatch window messages for the application thread104 105    while(Ret = GetMessage(&msg, NULL, 0, 0))106    {107       if (Ret == -1)108       {109          printf("GetMessage() failed with error %d\n", GetLastError());110          return;111       }112 113       TranslateMessage(&msg);114       DispatchMessage(&msg);115    }116 }117 118 119 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)120 {121    SOCKET Accept;122    LPSOCKET_INFORMATION SocketInfo;123    DWORD RecvBytes, SendBytes;124    DWORD Flags;125 126    if (uMsg == WM_SOCKET)127    {128       if (WSAGETSELECTERROR(lParam))129       {130          printf("Socket failed with error %d\n", WSAGETSELECTERROR(lParam));131          FreeSocketInformation(wParam);132       } 133       else134       {135          switch(WSAGETSELECTEVENT(lParam))136          {137             case FD_ACCEPT:138 139                if ((Accept = accept(wParam, NULL, NULL)) == INVALID_SOCKET)140                {        141                   printf("accept() failed with error %d\n", WSAGetLastError());142                   break;143                }144 145                // Create a socket information structure to associate with the146                // socket for processing I/O.147 148                CreateSocketInformation(Accept);149 150                printf("Socket number %d connected\n", Accept);151 152                WSAAsyncSelect(Accept, hwnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE);153 154                break;155 156             case FD_READ:157 158                SocketInfo = GetSocketInformation(wParam);159 160                // Read data only if the receive buffer is empty.161 162                if (SocketInfo->BytesRECV != 0)163                {164                   SocketInfo->RecvPosted = TRUE;165                   return 0;166                }167                else168                {169                   SocketInfo->DataBuf.buf = SocketInfo->Buffer;170                   SocketInfo->DataBuf.len = DATA_BUFSIZE;171 172                   Flags = 0;173                   if (WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &RecvBytes,174                      &Flags, NULL, NULL) == SOCKET_ERROR)175                   {176                      if (WSAGetLastError() != WSAEWOULDBLOCK)177                      {178                         printf("WSARecv() failed with error %d\n", WSAGetLastError());179                         FreeSocketInformation(wParam);180                         return 0;181                      }182                   } 183                   else // No error so update the byte count184                   {185                      SocketInfo->BytesRECV = RecvBytes;186                   }187                }188 189                // DO NOT BREAK HERE SINCE WE GOT A SUCCESSFUL RECV. Go ahead190                // and begin writing data to the client.191 192             case FD_WRITE:               193 194                SocketInfo = GetSocketInformation(wParam);195 196                if (SocketInfo->BytesRECV > SocketInfo->BytesSEND)197                {198                   SocketInfo->DataBuf.buf = SocketInfo->Buffer + SocketInfo->BytesSEND;199                   SocketInfo->DataBuf.len = SocketInfo->BytesRECV - SocketInfo->BytesSEND;200 201                   if (WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf), 1, &SendBytes, 0,202                      NULL, NULL) == SOCKET_ERROR)203                   {204                      if (WSAGetLastError() != WSAEWOULDBLOCK)205                      {206                         printf("WSASend() failed with error %d\n", WSAGetLastError());207                         FreeSocketInformation(wParam);208                         return 0;209                      }210                   } 211                   else // No error so update the byte count212                   {213                      SocketInfo->BytesSEND += SendBytes;214                   }215                }216 217                if (SocketInfo->BytesSEND == SocketInfo->BytesRECV)218                {219                   SocketInfo->BytesSEND = 0;220                   SocketInfo->BytesRECV = 0;221 222                   // If a RECV occurred during our SENDs then we need to post an FD_READ223                   // notification on the socket.224 225                   if (SocketInfo->RecvPosted == TRUE)226                   {227                      SocketInfo->RecvPosted = FALSE;228                      PostMessage(hwnd, WM_SOCKET, wParam, FD_READ);229                   }230                }231 232                break;233 234             case FD_CLOSE:235 236                printf("Closing socket %d\n", wParam);237                FreeSocketInformation(wParam);238 239                break;240          }241       }242       return 0;243    }244 245    return DefWindowProc(hwnd, uMsg, wParam, lParam);246 }247 248 249 void CreateSocketInformation(SOCKET s)250 {251    LPSOCKET_INFORMATION SI;252 253    if ((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR,254       sizeof(SOCKET_INFORMATION))) == NULL)255    {256       printf("GlobalAlloc() failed with error %d\n", GetLastError());257       return;258    }259 260    // Prepare SocketInfo structure for use.261 262    SI->Socket = s;263    SI->RecvPosted = FALSE;264    SI->BytesSEND = 0;265    SI->BytesRECV = 0;266 267    SI->Next = SocketInfoList;268 269    SocketInfoList = SI;270 }271 272 LPSOCKET_INFORMATION GetSocketInformation(SOCKET s)273 {274    SOCKET_INFORMATION *SI = SocketInfoList;275 276    while(SI)277    {278       if (SI->Socket == s)279          return SI;280 281       SI = SI->Next;282    }283 284    return NULL;285 }286 287 void FreeSocketInformation(SOCKET s)288 {289    SOCKET_INFORMATION *SI = SocketInfoList;290    SOCKET_INFORMATION *PrevSI = NULL;291 292    while(SI)293    {294       if (SI->Socket == s)295       {296          if (PrevSI)297             PrevSI->Next = SI->Next;298          else299             SocketInfoList = SI->Next;300 301          closesocket(SI->Socket);302          GlobalFree(SI);303          return;304       }305 306       PrevSI = SI;307       SI = SI->Next;308    }309 }310 311 HWND MakeWorkerWindow(void)312 {313    WNDCLASS wndclass;314    CHAR *ProviderClass = "AsyncSelect";315    HWND Window;316 317    wndclass.style = CS_HREDRAW | CS_VREDRAW;318    wndclass.lpfnWndProc = (WNDPROC)WindowProc;319    wndclass.cbClsExtra = 0;320    wndclass.cbWndExtra = 0;321    wndclass.hInstance = NULL;322    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);323    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);324    wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);325    wndclass.lpszMenuName = NULL;326    wndclass.lpszClassName = ProviderClass;327 328    if (RegisterClass(&wndclass) == 0)329    {330       printf("RegisterClass() failed with error %d\n", GetLastError());331       return NULL;332    }333 334    // Create a window.335 336    if ((Window = CreateWindow(337       ProviderClass,338       "",339       WS_OVERLAPPEDWINDOW,340       CW_USEDEFAULT,341       CW_USEDEFAULT,342       CW_USEDEFAULT,343       CW_USEDEFAULT,344       NULL,345       NULL,346       NULL,347       NULL)) == NULL)348    {349       printf("CreateWindow() failed with error %d\n", GetLastError());350       return NULL;351    }352 353    return Window;354 }

注意到代码中实现了FD_WRITE,于是屏蔽了一下该分支代码块的具体内容。这样一来,跟我的代码在逻辑上的差别就是:我没有实现FD_WRITE分支。

考虑到MSDN的文档太多了,今又是周五,不想细看,就试了下在自己代码中加上了对FD_WRITE_BIT的处理——空代码块。

1     case FD_WRITE:2         {3 4         }5         break;

居然成功了。

 

后来又尝试了多次,发现,在WSAAsyncSelect中列出了哪个FD_XXXX,就要实现哪个,对于不想实现的,不要列出。

 

========================这难道就是传说中的,“如果爱 请深爱”?========================

 

 

 

 

 

 

 

================================耻辱墙===================================

http://www.cnblogs.com/wlsandwho/p/4206472.html

问题解决——WSAAsyncSelect模型不触发FD_CLOSE