API及可伸縮性-AcceptEx

 

對可伸縮的TCP/IP服務器而言,最有用的擴展API也就算AcceptEx了。利用這個函數,服務器可以投遞一個異步調用,該調用將接受下一個傳入的客戶機連接。

  1. BOOL AcceptEx( 
  2.   __in          SOCKET sListenSocket, 
  3.   __in          SOCKET sAcceptSocket, 
  4.   __in          PVOID lpOutputBuffer, 
  5.   __in          DWORD dwReceiveDataLength, 
  6.   __in          DWORD dwLocalAddressLength, 
  7.   __in          DWORD dwRemoteAddressLength, 
  8.   __out         LPDWORD lpdwBytesReceived, 
  9.   __in          LPOVERLAPPED lpOverlapped 
  10. ); 

sListenSocket是監聽套接字,sAcceptSocket是一個有效的,爲綁定的套接字句柄,將被分配到下一個客戶機連接上。因此需在投遞AcceptEx調用前創建客戶機的套接字句柄。因爲套接字創建開銷比較大,這個步驟很必要,如果一個服務器希望儘可能快的處理客戶機連接,它就需要一個已創建的套接字庫,以便把新的連接分配進去。

sAcceptSocket之後緊隨的4個參數相互關聯。lpOutputBuffer參數含有用於客戶機連接的本地或遠程地址,還有一個可選的緩衝區,該緩衝區用來接收來自客戶機的第一個數據塊。dwReceiveDataLength指明所提供的緩衝區需要多少字節數,該緩衝區用來接收客戶機發送的數據。應用程序如果不接收數據,可將其置爲0。dwLocalAddressLength指定套接字地址結構大小,它相當於客戶機套接字的地址族加上16個字節。客戶機套接字連接的本地地址放在lpOutputBuffer參數中緊隨接收數據之後的地方。dwRemoteAddresslength與lpOutputBuffer參數一樣。客戶機連接的遠程地址將被寫入前一個參數中,緊隨接收數據及本地地址之後。應注意, dwReceiveDataLength參數可以是0,但是dwLocalAddressLength和 dwRemoteAddresslength參數均不能爲0。

lpdwBytesReceived指明操作立刻成功時,新建的客戶機連接上所收到的字節數。lpOverlapped用於這個重疊操作的WSAOVERLAPPED結構,該參數是必須的---如果想執行一個阻塞的接收調用,使用accept或WSAAccept就行了。

示例代碼,創建一個IPv4套接字並投遞一個單一的AcceptEx:

  1. SOCKET s,sClient; 
  2. HANDLE hComPort; 
  3. LPFN_ACCEPTEX lpfnAcceptEx = NULL; 
  4. GUID GuidAcceptEx = WSAID_ACCEPTEX; 
  5.  
  6. WSAOVERLAPPEDPLUS ol; 
  7. SOCKADDR_IN salocal; 
  8. DWORD dwBytes; 
  9. char buf[1024]; 
  10. int buflen = 1024; 
  11.  
  12. //創建完成端口 
  13. hComPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,(ULONG_PTR)0,0); 
  14.  
  15. //創建監聽套接字 
  16. s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
  17.  
  18. //將套接字和完成端口關聯起來 
  19. CreateIoCompletionPort((HANDLE)s,hComPort,(ULONG_PTR)0,0); 
  20.   
  21. salocal.sin_family = AF_INET; 
  22. salocal.sin_port = htons(5050); 
  23. salocal.sin_addr.s_addr = htonl(INADDR_ANY); 
  24. bind(s, (SOCKADDR*)&salocal, sizeof(SOCKADDR_IN)); 
  25.  
  26. listen(s, 200); 
  27.  
  28. //加載AcceptEx函數 
  29. WSAIoctl(s, 
  30.          SIO_GET_EXTENSION_FUNCTION_POINTER, 
  31.          &GuidAcceptEx, 
  32.          sizeof(GuidAcceptEx), 
  33.          &lpfnAcceptEx, 
  34.          sizeof(lpfnAcceptEx), 
  35.          &dwBytes, 
  36.          NULL, 
  37.          NULL); 
  38. //爲已接收的連接創建客戶機套接字 
  39. sClient = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
  40.  
  41. //初始化擴展的重疊結構 
  42. memset(&ol, 0 ,sizeof(ol)); 
  43. ol.operation = OP_ACCEPTEX; 
  44. ol.client = sClient; 
  45.  
  46. lpfnAcceptEx(s, 
  47.              sClient, 
  48.              buf, 
  49.              buflen-((sizeof(SOCKADDR_IN)+16)*2), 
  50.              sizeof(SOCKADDR_IN)+16, 
  51.              sizeof(SOCKADDR_IN)+16, 
  52.              &dwBytes, 
  53.              &ol.overlapped);  
  54.  
  55. //在完成函數內調用GetQueuedCompletionStatus函數 
  56. //在AcceptEx操作完成後,將已接收的客戶機套接字和完成端口關聯起來 
  57. ...  

本段代碼演示如何加載AcceptEx函數。

同時注意到,因爲AcceptEx的高性能特性,監聽套接字的套接字屬性不會自動被客戶機套接字繼承,要繼承屬性,服務器必須用SO_UPDATE_ACCEPT_CONTEXT及客戶機套接字句柄調用setsockopt。

另外一點要清楚,如果爲AcceptEx指定了一個接收緩衝區,則重疊操作只有在連接上收到至少一個字節的數據之後才能完成。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章