Windows Socket I/O模型之 WSAEventSelect模式

  1. # include < winsock2. h>
  2. # include < ws2tcpip. h>
  3.  
  4. # include "public.h"
  5. # include "resolve.h"
  6.  
  7. typedef SINGLE_LIST_HEADER BuffHeader;
  8. typedef SINGLE_LIST BuffObj;
  9. typedef SINGLE_LIST_HEADER TheadObjHeader;
  10. typedef SINGLE_LIST ThreadObj;
  11. typedef DOUBLE_LIST_HEADER SockObjHeader;
  12. typedef DOUBLE_LIST SockObj;
  13.  
  14. typedef struct _SOCKET_OBJ
  15. {
  16.     SOCKET s; // Socket handle
  17.     HANDLE event; // Event handle
  18.     int listening; // Socket is a listening socket (TCP)
  19.     int closing; // Indicates whether the connection is closing
  20.  
  21.     SOCKADDR_STORAGE addr; // Used for client's remote address
  22.     int addrlen; // Length of the address
  23.  
  24.     BuffHeader buff;
  25.  
  26.     DOUBLE_LIST entry;
  27. } SOCKET_OBJ;
  28.  
  29. typedef struct _THREAD_OBJ
  30. {
  31.     SockObjHeader sockHeader;
  32.  
  33.     HANDLE Event; // Used to signal new clients assigned
  34.                                        // to this thread
  35.     HANDLE Thread;
  36.  
  37.     HANDLE Handles[ MAXIMUM_WAIT_OBJECTS] ; // Array of socket's event handles
  38.  
  39.     CRITICAL_SECTION ThreadCritSec; // Protect access to SOCKET_OBJ lists
  40.  
  41.     ThreadObj entry; // Next thread object in list
  42. } THREAD_OBJ;
  43.  
  44. TheadObjHeader theadObjHeader;
  45.  
  46. SOCKET_OBJ* GetSocketObj( SOCKET s, int listening) {
  47.     SOCKET_OBJ * sockobj = NULL ;
  48.  
  49.     sockobj = ( SOCKET_OBJ* ) HeapAlloc( GetProcessHeap( ) , HEAP_ZERO_MEMORY, sizeof ( SOCKET_OBJ) ) ;
  50.     if ( sockobj = = NULL ) {
  51.         fprintf ( stderr , "HeapAlloc failed./n" ) ;
  52.         ExitProcess( - 1) ;
  53.     }
  54.  
  55.     sockobj- > s = s;
  56.     sockobj- > listening = listening;
  57.     sockobj- > addrlen = sizeof ( sockobj- > addr) ;
  58.  
  59.     sockobj- > event = WSACreateEvent( ) ;
  60.     if ( sockobj- > event = = NULL )
  61.     {
  62.         fprintf ( stderr , "GetSocketObj: WSACreateEvent failed: %d/n" , WSAGetLastError( ) ) ;
  63.         ExitProcess( - 1) ;
  64.     }
  65.  
  66.     InitializeCriticalSection( & sockobj- > buff. SendRecvQueueCritSec) ;
  67.  
  68.     return sockobj;
  69. }
  70.  
  71. THREAD_OBJ * GetThreadObj( ) {
  72.     THREAD_OBJ * thread = NULL ;
  73.  
  74.     thread = ( THREAD_OBJ* ) HeapAlloc( GetProcessHeap( ) , HEAP_ZERO_MEMORY, sizeof ( THREAD_OBJ) ) ;
  75.     if ( thread = = NULL ) {
  76.         fprintf ( stderr , "HeapAllco failed./n" ) ;
  77.         ExitProcess( - 1) ;
  78.     }
  79.  
  80.     thread- > Event = WSACreateEvent( ) ;
  81.     if ( thread- > Event = = NULL ) {
  82.         fprintf ( stderr , "WSACreateEvent failed./n" ) ;
  83.         ExitProcess( - 1) ;
  84.     }
  85.  
  86.     thread- > Handles[ 0] = thread- > Event;
  87.  
  88.     InitializeCriticalSection( & thread- > ThreadCritSec) ;
  89.     InitializeDoubleHead( & thread- > sockHeader) ;
  90.  
  91.     return thread;
  92. }
  93.  
  94. int InsertSocketObj( THREAD_OBJ * thread, SOCKET_OBJ * sockobj) {
  95.     int ret;
  96.  
  97.     EnterCriticalSection( & thread- > ThreadCritSec) ;
  98.  
  99.     if ( thread- > sockHeader. count < MAXIMUM_WAIT_OBJECTS - 1) {
  100.         EnqueueDoubleListHead( & ( thread- > sockHeader) , & ( sockobj- > entry) ) ;
  101.  
  102.         thread- > Handles[ thread- > sockHeader. count ] = sockobj- > event;
  103.  
  104.         ret = NO_ERROR;
  105.     } else {
  106.         ret = SOCKET_ERROR;
  107.     }
  108.  
  109.     LeaveCriticalSection( & thread- > ThreadCritSec) ;
  110.  
  111.     return ret;
  112. }
  113.  
  114. SOCKET_OBJ * FindSocketObj( THREAD_OBJ * thread, int index) {
  115.     SOCKET_OBJ * sockobj = NULL ;
  116.     int i;
  117.  
  118.     EnterCriticalSection( & thread- > ThreadCritSec) ;
  119.  
  120.     SockObj * sptr = ( SockObj * ) GotoNextDoubleList( & thread- > sockHeader, & ( thread- > sockHeader. head) ) ;
  121.     for ( i = 0; i < index; + + i) {
  122.         if ( sptr = = NULL )
  123.         {
  124.             fprintf ( stderr , "FindSocketobj failed./n" ) ;
  125.             ExitProcess( - 1) ;
  126.         }
  127.         sptr = ( SockObj * ) GotoNextDoubleList( & thread- > sockHeader, sptr) ;
  128.     }
  129.  
  130.     sockobj = ( SOCKET_OBJ * ) container_of( SOCKET_OBJ, entry, sptr) ;
  131.  
  132.     LeaveCriticalSection( & thread- > ThreadCritSec) ;
  133.  
  134.     return sockobj;
  135. }
  136.  
  137. void RemoveSocketObj( THREAD_OBJ * thread, SOCKET_OBJ * sock) {
  138.     EnterCriticalSection( & thread- > ThreadCritSec) ;
  139.  
  140.     RemoveDoubleList( & thread- > sockHeader, & sock- > entry) ;
  141.     WSASetEvent( thread- > Event) ;
  142.  
  143.     LeaveCriticalSection( & thread- > ThreadCritSec) ;
  144. }
  145.  
  146. void FreeSocketObj( SOCKET_OBJ * obj) {
  147.     BuffObj * ptr = NULL ;
  148.     BUFFER_OBJ * blk = NULL ;
  149.  
  150.     while ( true ) {
  151.         ptr = DequeueSingleList( & obj- > buff) ;
  152.         if ( ptr = = NULL )
  153.             break ;
  154.  
  155.         blk = ( BUFFER_OBJ * ) container_of( BUFFER_OBJ, next, ptr) ;
  156.         FreeBufferObj( blk) ;
  157.     }
  158.  
  159.     WSACloseEvent( obj- > event) ;
  160.  
  161.     if ( obj- > s ! = INVALID_SOCKET) {
  162.         closesocket( obj- > s) ;
  163.     }
  164.  
  165.     HeapFree( GetProcessHeap( ) , 0, obj) ;
  166. }
  167.  
  168. void RenumberThreadArray( THREAD_OBJ * thread) {
  169.     EnterCriticalSection( & thread- > ThreadCritSec) ;
  170.  
  171.     SOCKET_OBJ * obj = NULL ;
  172.     int i = 0;
  173.  
  174.     SockObj * sptr = NULL ;
  175.     sptr = ( SockObj * ) GotoNextDoubleList( & thread- > sockHeader, & ( thread- > sockHeader. head) ) ;
  176.     while ( sptr) {
  177.         obj = ( SOCKET_OBJ * ) container_of( SOCKET_OBJ, entry, sptr) ;
  178.  
  179.         thread- > Handles[ + + i] = obj- > event;
  180.  
  181.         sptr = ( SockObj * ) GotoNextDoubleList( & thread- > sockHeader, sptr) ;
  182.     }
  183.  
  184.     LeaveCriticalSection( & thread- > ThreadCritSec) ;
  185. }
  186.  
  187. int ReceivePendingData( SOCKET_OBJ * sockobj) {
  188.     BUFFER_OBJ * buffobj= NULL ;
  189.     int rc,
  190.                 ret;
  191.  
  192.     // Get a buffer to receive the data
  193.     buffobj = GetBufferObj( gBufferSize) ;
  194.  
  195.     ret = 0;
  196.  
  197.     if ( gProtocol = = IPPROTO_TCP )
  198.     {
  199.         rc = recv ( sockobj- > s, buffobj- > buf, buffobj- > buflen, 0) ;
  200.     } else {
  201.         fprintf ( stderr , "Tcp failed./n" ) ;
  202.         ExitProcess( - 1) ;
  203.     }
  204.  
  205.     if ( rc = = SOCKET_ERROR) {
  206.         fprintf ( stderr , "recv failed./n" ) ;
  207.         ExitProcess( - 1) ;
  208.     } else if ( rc = = 0) {
  209.         FreeBufferObj( buffobj) ;
  210.  
  211.         sockobj- > closing = TRUE ;
  212.  
  213.         if ( sockobj- > buff. head = = NULL )
  214.         {
  215.             // If no sends are pending, close the socket for good
  216.             closesocket( sockobj- > s) ;
  217.             sockobj- > s = INVALID_SOCKET;
  218.             ret = - 1;
  219.         }
  220.         else
  221.         {
  222.             ret = 0;
  223.         }
  224.     } else {
  225.         buffobj- > buflen = rc;
  226.         EnqueueSingleList( & sockobj- > buff, & buffobj- > next) ;
  227.  
  228.         ret = 1;
  229.     }
  230.  
  231.     return ret;
  232. }
  233.  
  234. int SendPendingData( SOCKET_OBJ * sock) {
  235.     BUFFER_OBJ * bufobj = NULL ;
  236.     BuffObj * entry = NULL ;
  237.     int nleft = 0,
  238.                 idx = 0,
  239.                 ret = 0,
  240.                 rc = 0;
  241.  
  242.     while ( entry = DequeueSingleList( & sock- > buff) ) {
  243.         bufobj = ( BUFFER_OBJ * ) container_of( BUFFER_OBJ, next, entry) ;
  244.  
  245.         if ( gProtocol = = IPPROTO_TCP ) {
  246.             nleft = bufobj- > buflen;
  247.             idx = 0;
  248.  
  249.             while ( nleft > 0) {
  250.                 rc = send ( sock- > s, & ( bufobj- > buf[ idx] ) , nleft, 0) ;
  251.                 if ( rc = = SOCKET_ERROR) {
  252.                     ExitProcess( - 1) ;
  253.                 } else {
  254.                     idx + = rc;
  255.                     nleft - = rc;
  256.                 }
  257.             }
  258.  
  259.             printf ( "send %d./n" , bufobj- > buflen) ;
  260.  
  261.             FreeBufferObj( bufobj) ;
  262.         } else {
  263.             ExitProcess( - 1) ;
  264.         }
  265.     }
  266.  
  267.     if ( ( sock- > buff. head = = NULL ) & & ( sock- > closing = = TRUE ) ) {
  268.         closesocket( sock- > s) ;
  269.         sock- > s = INVALID_SOCKET;
  270.         ret = - 1;
  271.  
  272.         printf ( "Closing Connection./n" ) ;
  273.     }
  274.  
  275.     return ret;
  276. }
  277.  
  278. int HandleIo( THREAD_OBJ * thread, SOCKET_OBJ * sock) {
  279.     WSANETWORKEVENTS nevents;
  280.     int rc;
  281.  
  282.     // Enumerate the events
  283.     rc = WSAEnumNetworkEvents( sock- > s, sock- > event, & nevents) ;
  284.     if ( rc = = SOCKET_ERROR)
  285.     {
  286.         fprintf ( stderr , "HandleIo: WSAEnumNetworkEvents failed: %d/n" , WSAGetLastError( ) ) ;
  287.         return SOCKET_ERROR;
  288.     }
  289.  
  290.     if ( nevents. lNetworkEvents & FD_READ) {
  291.         if ( nevents. iErrorCode[ FD_READ_BIT] = = 0) {
  292.             rc = ReceivePendingData( sock) ;
  293.             if ( rc = = - 1)
  294.             {
  295.                 RemoveSocketObj( thread, sock) ;
  296.                 FreeSocketObj( sock) ;
  297.                 return SOCKET_ERROR;
  298.             }
  299.             rc = SendPendingData( sock) ;
  300.             if ( rc = = - 1)
  301.             {
  302.                 RemoveSocketObj( thread, sock) ;
  303.                 FreeSocketObj( sock) ;
  304.                 return SOCKET_ERROR;
  305.             }
  306.         } else {
  307.             fprintf ( stderr , "HandleIo: FD_READ error %d/n" , nevents. iErrorCode[ FD_READ_BIT] ) ;
  308.             RemoveSocketObj( thread, sock) ;
  309.             FreeSocketObj( sock) ;
  310.             return SOCKET_ERROR;
  311.         }
  312.     }
  313.     if ( nevents. lNetworkEvents & FD_WRITE) {
  314.         if ( nevents. iErrorCode[ FD_WRITE_BIT] = = 0)
  315.         {
  316.             rc = SendPendingData( sock) ;
  317.             if ( rc = = - 1)
  318.             {
  319.                 RemoveSocketObj( thread, sock) ;
  320.                 FreeSocketObj( sock) ;
  321.                 return SOCKET_ERROR;
  322.             }
  323.         }
  324.         else
  325.         {
  326.             fprintf ( stderr , "HandleIo: FD_WRITE error %d/n" , nevents. iErrorCode[ FD_WRITE_BIT] ) ;
  327.             return SOCKET_ERROR;
  328.         }
  329.     }
  330.     if ( nevents. lNetworkEvents & FD_CLOSE) {
  331.         if ( nevents. iErrorCode[ FD_CLOSE_BIT] = = 0)
  332.         {
  333.             // Socket has been indicated as closing so make sure all the data
  334.             // has been read
  335.             printf ( "close./n" ) ;
  336.             while ( 1)
  337.             {
  338.                 rc = ReceivePendingData( sock) ;
  339.                 if ( rc = = - 1)
  340.                 {
  341.                     RemoveSocketObj( thread, sock) ;
  342.                     FreeSocketObj( sock) ;
  343.                     return SOCKET_ERROR;
  344.                 }
  345.                 else if ( rc ! = 0)
  346.                 {
  347.                     continue ;
  348.                 }
  349.                 else
  350.                 {
  351.                     break ;
  352.                 }
  353.             }
  354.             // See if there is any data pending, if so try to send it
  355.             rc = SendPendingData( sock) ;
  356.             if ( rc = = - 1)
  357.             {
  358.                 RemoveSocketObj( thread, sock) ;
  359.                 FreeSocketObj( sock) ;
  360.                 return SOCKET_ERROR;
  361.             }
  362.         }
  363.         else
  364.         {
  365.             fprintf ( stderr , "HandleIo: FD_CLOSE error %d/n" , nevents. iErrorCode[ FD_CLOSE_BIT] ) ;
  366.             RemoveSocketObj( thread, sock) ;
  367.             FreeSocketObj( sock) ;
  368.             return SOCKET_ERROR;
  369.         }
  370.     }
  371.  
  372.     return NO_ERROR;
  373. }
  374.  
  375. DWORD WINAPI ChildThread( LPVOID lpParam) {
  376.     THREAD_OBJ * thread= NULL ;
  377.     SOCKET_OBJ * sptr= NULL ,
  378.                * sockobj= NULL ;
  379.     int index,
  380.                 rc,
  381.                 i;
  382.  
  383.     thread = ( THREAD_OBJ * ) lpParam;
  384.  
  385.     while ( true ) {
  386.         rc = WaitForMultipleObjects( thread- > sockHeader. count + 1, thread- > Handles, FALSE , INFINITE) ;
  387.         if ( rc = = WAIT_FAILED | | rc = = WAIT_TIMEOUT)
  388.         {
  389.             fprintf ( stderr , "ChildThread: WaitForMultipleObjects failed: %d/n" , GetLastError( ) ) ;
  390.             break ;
  391.         } else {
  392.             for ( i = 0; i < thread- > sockHeader. count + 1; i+ + ) {
  393.                 rc = WaitForSingleObject( thread- > Handles[ i] , 0) ;
  394.  
  395.                 if ( rc = = WAIT_FAILED)
  396.                 {
  397.                     fprintf ( stderr , "ChildThread: WaitForSingleObject failed: %d/n" , GetLastError( ) ) ;
  398.                     ExitThread( - 1) ;
  399.                 }
  400.                 else if ( rc = = WAIT_TIMEOUT)
  401.                 {
  402.                     // This event isn't signaled, continue to the next one
  403.                     continue ;
  404.                 }
  405.                 index = i;
  406.  
  407.                 if ( index = = 0)
  408.                 {
  409.                     // If index 0 is signaled then rebuild the array of event
  410.                     // handles to wait on
  411.                     WSAResetEvent( thread- > Handles[ index] ) ;
  412.  
  413.                     RenumberThreadArray( thread) ;
  414.  
  415.                     i = 1;
  416.                 } else {
  417.                     sockobj = FindSocketObj( thread, index- 1) ;
  418.                     if ( sockobj ! = NULL )
  419.                     {
  420.                         if ( HandleIo( thread, sockobj) = = SOCKET_ERROR)
  421.                         {
  422.                             RenumberThreadArray( thread) ;
  423.                         }
  424.                     }
  425.                     else
  426.                     {
  427.                         printf ( "Unable to find socket object!/n" ) ;
  428.                     }
  429.                 }
  430.             }
  431.         }
  432.     }
  433. }
  434.  
  435. void AssignToFreeThread( SOCKET_OBJ * sock) {
  436.     ThreadObj * threadobj = NULL ;
  437.     THREAD_OBJ * thread = NULL ;
  438.  
  439.     threadobj = ( ThreadObj * ) GotoNextSingleList( & theadObjHeader, theadObjHeader. head) ;
  440.     while ( threadobj) {
  441.         thread = ( THREAD_OBJ * ) container_of( THREAD_OBJ, entry, threadobj) ;
  442.  
  443.         if ( InsertSocketObj( thread, sock) ! = SOCKET_ERROR) {
  444.             break ;
  445.         }
  446.         threadobj = ( ThreadObj * ) GotoNextSingleList( & theadObjHeader, threadobj) ;
  447.     }
  448.  
  449.     if ( threadobj = = NULL ) {
  450.         thread = GetThreadObj( ) ;
  451.  
  452.         thread- > Thread = CreateThread( NULL , 0, ChildThread, ( LPVOID) thread, 0, NULL ) ;
  453.         if ( thread- > Thread = = NULL )
  454.         {
  455.             fprintf ( stderr , "AssignToFreeThread: CreateThread failed: %d/n" , GetLastError( ) ) ;
  456.             ExitProcess( - 1) ;
  457.         }
  458.  
  459.         InsertSocketObj( thread, sock) ;
  460.  
  461.         EnqueueSingleList( & theadObjHeader, & thread- > entry) ;
  462.     }
  463.  
  464.     WSASetEvent( thread- > Event) ;
  465. }
  466.  
  467. int _tmain( int argc, _TCHAR* argv[ ] )
  468. {
  469.     WSADATA wsd;
  470.  
  471.     struct addrinfo * res= NULL ,
  472.                     * ptr= NULL ;
  473.  
  474.     THREAD_OBJ * thread= NULL ;
  475.     SOCKET_OBJ * sockobj= NULL ,
  476.                     * newsock= NULL ;
  477.  
  478.     int index,
  479.                      rc;
  480.  
  481.     if ( WSAStartup( MAKEWORD( 2, 2) , & wsd) ! = 0)
  482.     {
  483.         fprintf ( stderr , "unable to load Winsock!/n" ) ;
  484.         return - 1;
  485.     }
  486.  
  487.     res = ResolveAddress( gSrvAddr, gPort, gAddressFamily, gSocketType, gProtocol) ;
  488.     if ( res = = NULL )
  489.     {
  490.         fprintf ( stderr , "ResolveAddress failed to return any addresses!/n" ) ;
  491.         return - 1;
  492.     }
  493.  
  494.     thread = GetThreadObj( ) ;
  495.  
  496.     InitializeCriticalSection( & theadObjHeader. SendRecvQueueCritSec) ;
  497.     theadObjHeader. head = theadObjHeader. tail = NULL ;
  498.  
  499.     ptr = res;
  500.     while ( ptr) {
  501.         sockobj = GetSocketObj( INVALID_SOCKET, ( gProtocol = = IPPROTO_TCP ) ? TRUE : FALSE ) ;
  502.  
  503.         sockobj- > s = socket ( ptr- > ai_family, ptr- > ai_socktype, ptr- > ai_protocol) ;
  504.         if ( sockobj- > s = = INVALID_SOCKET) {
  505.             fprintf ( stderr , "create socket failed./n" ) ;
  506.             ExitProcess( - 1) ;
  507.         }
  508.  
  509.         InsertSocketObj( thread, sockobj) ;
  510.  
  511.         rc = bind ( sockobj- > s, ptr- > ai_addr, ptr- > ai_addrlen) ;
  512.         if ( rc = = SOCKET_ERROR)
  513.         {
  514.             fprintf ( stderr , "bind failed: %d/n" , WSAGetLastError( ) ) ;
  515.             return - 1;
  516.         }
  517.  
  518.         if ( gProtocol = = IPPROTO_TCP ) {
  519.             rc = listen ( sockobj- > s, 200) ;
  520.             if ( rc = = SOCKET_ERROR) {
  521.                 fprintf ( stderr , "listen failed./n" ) ;
  522.                 ExitProcess( - 1) ;
  523.             }
  524.  
  525.             rc = WSAEventSelect( sockobj- > s, sockobj- > event, FD_ACCEPT | FD_CLOSE) ;
  526.             if ( rc = = SOCKET_ERROR) {
  527.                 fprintf ( stderr , "WSAEventSelect failed: %d/n" , WSAGetLastError( ) ) ;
  528.                 ExitProcess( - 1) ;
  529.             }
  530.         }
  531.  
  532.         ptr = ptr- > ai_next;
  533.     }
  534.  
  535.     freeaddrinfo ( res) ;
  536.  
  537.     while ( true ) {
  538.         rc = WaitForMultipleObjects( thread- > sockHeader. count + 1, thread- > Handles, FALSE , 5000) ;
  539.         if ( rc = = WAIT_FAILED) {
  540.             fprintf ( stderr , "WaitForMultipleObjects failed:%d/n" , WSAGetLastError( ) ) ;
  541.             break ;
  542.         } else if ( rc = = WAIT_TIMEOUT) {
  543.             continue ;
  544.         } else {
  545.             index = rc - WAIT_OBJECT_0;
  546.  
  547.             sockobj = FindSocketObj( thread, index - 1) ;
  548.  
  549.             if ( gProtocol = = IPPROTO_TCP ) {
  550.                 SOCKADDR_STORAGE sa;
  551.                 WSANETWORKEVENTS ne;
  552.                 SOCKET sc;
  553.                 int salen;
  554.  
  555.                 rc = WSAEnumNetworkEvents( sockobj- > s, thread- > Handles[ index] , & ne) ;
  556.                 if ( rc = = SOCKET_ERROR) {
  557.                     fprintf ( stderr , "WSAEnumNetworkEvents failed./n" ) ;
  558.                     break ;
  559.                 }
  560.  
  561.                 while ( true ) {
  562.                     sc = INVALID_SOCKET;
  563.                     salen = sizeof ( sa) ;
  564.  
  565.                     sc = accept ( sockobj- > s, ( SOCKADDR * ) & sa, & salen) ;
  566.                     if ( ( sc = = INVALID_SOCKET) & & ( WSAGetLastError( ) ! = WSAEWOULDBLOCK) ) {
  567.                         fprintf ( stderr , "accept failed./n" ) ;
  568.                         break ;
  569.                     } else if ( sc = = INVALID_SOCKET) {
  570.                         continue ;
  571.                     } else {
  572.                         newsock = GetSocketObj( INVALID_SOCKET, FALSE ) ;
  573.  
  574.                         memcpy ( & newsock- > addr, & sa, salen) ;
  575.                         newsock- > addrlen = salen;
  576.                         newsock- > s = sc;
  577.  
  578.                         rc = WSAEventSelect( newsock- > s, newsock- > event, FD_READ | FD_WRITE | FD_CLOSE) ;
  579.                         if ( rc = = SOCKET_ERROR)
  580.                         {
  581.                             fprintf ( stderr , "WSAEventSelect failed: %d/n" , WSAGetLastError( ) ) ;
  582.                             break ;
  583.                         }
  584.  
  585.                         AssignToFreeThread( newsock) ;
  586.                     }
  587.                 }
  588.             }
  589.         }
  590.     }
  591.  
  592.     WSACleanup( ) ;
  593.  
  594.     return 0;
  595. }

 

版权声明: 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值