zoukankan      html  css  js  c++  java
  • winsock编程IOCP模型实现代码

    winsock编程IOCP模型实现代码

      话不多说,上代码。借鉴《windows核心编程》部分源码和CSDN小猪部分代码。

      stdafx.h依赖头文件:

     1 #include <iostream>
     2 #include <WinSock2.h>
     3 #include <MSWSock.h>
     4 #include <vector>
     5 #include "Singleton.h"
     6 #include "IOCPWrapper.h"
     7 #include "OverlappedIOInfo.h"
     8 #include "TaskSvc.h"
     9 
    10 using namespace std;

      其中,TaskSvc.h、Singleton.h源码可以在我的blog里面找到。

      IOCPWrapper.h源码:

     1 /******************************************************************************
     2 Module:  IOCP.h
     3 Notices: Copyright (c) 2007 Jeffrey Richter & Christophe Nasarre
     4 Purpose: This class wraps an I/O Completion Port.
     5 Revise:    IOCP封装类,由《windows核心编程》第10章示例程序源码改编所得
     6 ******************************************************************************/
     7 #pragma once   
     8 
     9 class CIOCP 
    10 {
    11 public:
    12    CIOCP(int nMaxConcurrency = -1)
    13    {
    14        m_hIOCP = NULL; 
    15        if (nMaxConcurrency != -1)
    16            Create(nMaxConcurrency);
    17    }
    18    ~CIOCP()
    19    {
    20        if (m_hIOCP != NULL) 
    21            VERIFY(CloseHandle(m_hIOCP)); 
    22    }
    23 
    24    //关闭IOCP
    25    BOOL Close()
    26    {
    27        BOOL bResult = CloseHandle(m_hIOCP);
    28        m_hIOCP = NULL;
    29        return(bResult);
    30    }
    31 
    32    //创建IOCP,nMaxConcurrency指定最大线程并发数量,0默认为cpu数量
    33    BOOL Create(int nMaxConcurrency = 0)
    34    {
    35        m_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, nMaxConcurrency);
    36        ASSERT(m_hIOCP != NULL);
    37        return(m_hIOCP != NULL);
    38    }
    39 
    40    //为设备(文件、socket、邮件槽、管道等)关联一个IOCP
    41    BOOL AssociateDevice(HANDLE hDevice, ULONG_PTR CompKey)
    42    {
    43        BOOL fOk = (CreateIoCompletionPort(hDevice, m_hIOCP, CompKey, 0) == m_hIOCP);
    44        ASSERT(fOk);
    45        return(fOk);
    46    }
    47 
    48    //为socket关联一个IOCP
    49    BOOL AssociateSocket(SOCKET hSocket, ULONG_PTR CompKey)
    50    {
    51        return(AssociateDevice((HANDLE) hSocket, CompKey));
    52    }
    53 
    54    //为iocp传递事件通知
    55    BOOL PostStatus(ULONG_PTR CompKey, DWORD dwNumBytes = 0,OVERLAPPED* po = NULL)
    56    {
    57        BOOL fOk = PostQueuedCompletionStatus(m_hIOCP, dwNumBytes, CompKey, po);
    58        ASSERT(fOk);
    59        return(fOk);
    60    }
    61 
    62    //从IO完成队列中获取事件通知。IO完成队列无事件时,该函数将阻塞
    63    BOOL GetStatus(ULONG_PTR* pCompKey, PDWORD pdwNumBytes,OVERLAPPED** ppo, DWORD dwMilliseconds = INFINITE)
    64    {
    65        return(GetQueuedCompletionStatus(m_hIOCP, pdwNumBytes,pCompKey, ppo, dwMilliseconds));
    66    }
    67 
    68    //获取IOCP对象
    69    const HANDLE  GetIOCP()
    70    {
    71        return m_hIOCP;
    72    }
    73 private:
    74     //IOCP句柄
    75    HANDLE m_hIOCP;
    76 };
    77 
    78 ///////////////////////////////// End of File /////////////////////////////////

    OverlappedIOInfo.h源码

     1 /******************************************************************************
     2 Module:  OverlappedIOInfo.h
     3 Notices: Copyright (c) 20161201  whg
     4 Purpose:
     5 IOCP网络编程模型中,需要用到GetQueuedCompletionStatus函数获取已完成事件。
     6 但该函数的返回参数无socket或buffer的描述信息。
     7 
     8 一个简单的解决办法,创建一个新的结构,该结构第一个参数是OVERLAPPED。
     9 由于AcceptEx、WSASend等重叠IO操作传入的是Overlapped结构体的地址,调用AcceptEx等重叠IO操作,
    10 在Overlapped结构体后面开辟新的空间,写入socket或buffer的信息,即可将socket或buffer的信息由
    11 GetQueuedCompletionStatus带回。
    12 
    13 参考《windows核心编程》和CSDN PiggyXP
    14 ******************************************************************************/
    15 
    16 #pragma once
    17 
    18 #define MAXBUF 8*1024
    19 
    20 enum IOOperType{
    21     TYPE_ACP,            //accept事件到达,有新连接请求    
    22     TYPE_RECV,            //数据接收事件
    23     TYPE_SEND,            //数据发送事件
    24     TYPE_CLOSE,            //关闭事件
    25     TYPE_NO_OPER
    26 };
    27 
    28 class COverlappedIOInfo :    public OVERLAPPED
    29 {
    30 public:
    31     COverlappedIOInfo(void)
    32     {
    33         m_sSock = INVALID_SOCKET;
    34         ResetOverlapped();
    35         ResetRecvBuffer();
    36         ResetSendBuffer();
    37     }
    38     ~COverlappedIOInfo(void)
    39     {
    40         if (m_sSock != INVALID_SOCKET)
    41         {
    42             closesocket(m_sSock);
    43             m_sSock = INVALID_SOCKET;
    44         }
    45     }
    46     void ResetOverlapped()
    47     {
    48         Internal = InternalHigh = 0;   
    49         Offset = OffsetHigh = 0;   
    50         hEvent = NULL;
    51     }
    52     void ResetRecvBuffer()
    53     {
    54         ZeroMemory(m_cRecvBuf,MAXBUF);
    55         m_recvBuf.buf = m_cRecvBuf;
    56         m_recvBuf.len = MAXBUF;
    57     }
    58     void ResetSendBuffer()
    59     {
    60         ZeroMemory(m_cSendBuf,MAXBUF);
    61         m_sendBuf.buf = m_cSendBuf;
    62         m_sendBuf.len = MAXBUF;
    63     }
    64 public:
    65     //套接字
    66     SOCKET        m_sSock;            
    67     //接收缓冲区,用于AcceptEx、WSARecv操作
    68     WSABUF        m_recvBuf;            
    69     char        m_cRecvBuf[MAXBUF];
    70     //发送缓冲区,用于WSASend操作
    71     WSABUF        m_sendBuf;
    72     char        m_cSendBuf[MAXBUF];    
    73     //对端地址
    74     sockaddr_in    m_addr;                
    75 };

    server.h

     1 #pragma once
     2 
     3 
     4 class CServer:public CTaskSvc
     5 {
     6 #define ACCEPT_SOCKET_NUM  10
     7 
     8 public:
     9     CServer(void);
    10     ~CServer(void);
    11     bool    StartListen(unsigned short port,std::string ip);
    12 
    13 protected:
    14     virtual void svc();
    15 
    16 private:
    17     //启动CPU*2个线程,返回已启动线程个数
    18     UINT    StartThreadPull();
    19     //获取AcceptEx和GetAcceptExSockaddrs函数指针
    20     bool    GetLPFNAcceptEXAndGetAcceptSockAddrs();
    21     //利用AcceptEx监听accept请求
    22     bool    PostAccept(COverlappedIOInfo* ol);
    23     //处理accept请求,NumberOfBytes=0表示没有收到第一帧数据,>0表示收到第一帧数据
    24     bool    DoAccept(COverlappedIOInfo* ol,DWORD NumberOfBytes=0);
    25     //投递recv请求
    26     bool    PostRecv(COverlappedIOInfo* ol);
    27     //处理recv请求
    28     bool    DoRecv(COverlappedIOInfo* ol);
    29     //从已连接socket列表中移除socket及释放空间
    30     bool    DeleteLink(SOCKET s);
    31     //释放3个部分步骤:
    32     //1:清空IOCP线程队列,退出线程
    33     //2: 清空等待accept的套接字m_vecAcps
    34     //3: 清空已连接的套接字m_vecContInfo并清空缓存
    35     void    CloseServer();
    36 private:
    37     //winsock版本类型
    38     WSAData                        m_wsaData;
    39     //端口监听套接字
    40     SOCKET                        m_sListen;
    41     //等待accept的套接字,这些套接字是没有使用过的,数量为ACCEPT_SOCKET_NUM。同时会有10个套接字等待accept
    42     std::vector<SOCKET>            m_vecAcps;            
    43     //已建立连接的信息,每个结构含有一个套接字、发送缓冲和接收缓冲,以及对端地址
    44     std::vector<COverlappedIOInfo*>    m_vecContInfo;    
    45     //操作vector的互斥访问锁
    46     CThreadLockCs                m_lsc;                    
    47     //IOCP封装类
    48     CIOCP                        m_iocp;        
    49     //AcceptEx函数指针
    50     LPFN_ACCEPTEX                m_lpfnAcceptEx;        
    51     //GetAcceptSockAddrs函数指针
    52     LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptSockAddrs;
    53 };
    54 
    55 typedef CSingleton<CServer> SERVER;

    server.cpp

      1 #include "StdAfx.h"
      2 #include "Server.h"
      3 
      4 CServer::CServer(void)
      5 {
      6     m_lpfnAcceptEx = NULL;
      7     m_lpfnGetAcceptSockAddrs = NULL;
      8     WSAStartup(MAKEWORD(2,2),&m_wsaData);
      9 }
     10 
     11 CServer::~CServer(void)
     12 {
     13     CloseServer();
     14     WSACleanup();
     15 }
     16 
     17 bool CServer::StartListen(unsigned short port,std::string ip)
     18 {
     19     //listen socket需要将accept操作投递到完成端口,因此,listen socket属性必须有重叠IO
     20     m_sListen = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
     21     if(m_sListen == INVALID_SOCKET)
     22     {
     23         cout<<"WSASocket create socket error"<<endl;
     24         return false;
     25     }
     26     //创建并设置IOCP并发线程数量
     27     if (m_iocp.Create() == FALSE)
     28     {
     29         cout<<"IOCP create error,error code "<<WSAGetLastError()<<endl;
     30         return false;
     31     }
     32     //将listen socket绑定至iocp
     33     if (!m_iocp.AssociateSocket(m_sListen,TYPE_ACP))
     34     {
     35         cout<<"iocp Associate listen Socket error"<<endl;
     36         return false;
     37     }
     38     sockaddr_in service;
     39     service.sin_family = AF_INET;
     40     service.sin_port = htons(port);
     41     if (ip.empty())
     42     {
     43         service.sin_addr.s_addr = INADDR_ANY;
     44     }
     45     else
     46     {
     47         service.sin_addr.s_addr = inet_addr(ip.c_str());
     48     }
     49 
     50     if (bind(m_sListen,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
     51     {
     52         cout<<"bind() error,error code "<<WSAGetLastError()<<endl;
     53         return false;
     54     }
     55     cout<<"bind ok!"<<endl;
     56 
     57     if (listen(m_sListen,SOMAXCONN) == SOCKET_ERROR)
     58     {
     59         cout<<"listen() error,error code "<<WSAGetLastError()<<endl;
     60         return false;
     61     }
     62     cout<<"listen ok!"<<endl;
     63     //启动工作者线程
     64     int threadnum = StartThreadPull();
     65     cout<<"启动工作者线程,num="<<threadnum<<endl;
     66     //获取AcceptEx和GetAcceptSockAddrs函数指针
     67     if (!GetLPFNAcceptEXAndGetAcceptSockAddrs())
     68     {
     69         return false;
     70     }
     71     //创建10个acceptex
     72     for (int i=0;i<ACCEPT_SOCKET_NUM;i++)
     73     {
     74         //用accept
     75         COverlappedIOInfo* ol = new COverlappedIOInfo;
     76         if (!PostAccept(ol))
     77         {
     78             delete ol;
     79             return false;
     80         }
     81     }
     82 }
     83 
     84 bool CServer::GetLPFNAcceptEXAndGetAcceptSockAddrs()
     85 {
     86     DWORD BytesReturned = 0;
     87     //获取AcceptEx函数指针
     88     GUID GuidAcceptEx = WSAID_ACCEPTEX;
     89     if (SOCKET_ERROR == WSAIoctl(
     90         m_sListen,
     91         SIO_GET_EXTENSION_FUNCTION_POINTER,
     92         &GuidAcceptEx,
     93         sizeof(GuidAcceptEx),
     94         &m_lpfnAcceptEx,
     95         sizeof(m_lpfnAcceptEx),
     96         &BytesReturned,
     97         NULL,NULL))
     98     {
     99         cout<<"WSAIoctl get AcceptEx function error,error code "<<WSAGetLastError()<<endl;
    100         return false;
    101     }
    102     
    103     //获取GetAcceptexSockAddrs函数指针
    104     GUID GuidGetAcceptexSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; 
    105     if (SOCKET_ERROR == WSAIoctl(
    106         m_sListen,
    107         SIO_GET_EXTENSION_FUNCTION_POINTER,
    108         &GuidGetAcceptexSockAddrs,
    109         sizeof(GuidGetAcceptexSockAddrs),
    110         &m_lpfnGetAcceptSockAddrs,
    111         sizeof(m_lpfnGetAcceptSockAddrs),
    112         &BytesReturned,
    113         NULL,NULL))
    114     {
    115         cout<<"WSAIoctl get GetAcceptexSockAddrs function error,error code "<<WSAGetLastError()<<endl;
    116         return false;
    117     }
    118     return true;
    119 }
    120 
    121 bool CServer::PostAccept(COverlappedIOInfo* ol)
    122 {
    123     if (m_lpfnAcceptEx == NULL)
    124     {
    125         cout << "m_lpfnAcceptEx is NULL"<<endl;
    126         return false;
    127     }
    128     SOCKET s = ol->m_sSock;
    129     ol->ResetRecvBuffer();
    130     ol->ResetOverlapped();
    131     ol->ResetSendBuffer();
    132     ol->m_sSock = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,WSA_FLAG_OVERLAPPED);
    133     if (ol->m_sSock == INVALID_SOCKET)
    134     {
    135         cout<<"WSASocket error ,error code "<<WSAGetLastError()<<endl;
    136         return false;
    137     }
    138     //这里建立的socket用来和对端建立连接,终会加入m_vecContInfo列表
    139     //调用acceptex将accept socket绑定至完成端口,并开始进行事件监听
    140     //这里需要传递Overlapped,new一个COverlappedIOInfo
    141     //AcceptEx是m_listen的监听事件,m_listen已经绑定了完成端口;虽然ol->m_sSock已经创建,
    142     //但未使用,现在不必为ol->m_sSock绑定完成端口。在AcceptEx事件发生后,再为ol->m_sSock绑定IOCP
    143     DWORD byteReceived = 0;
    144     if (FALSE == m_lpfnAcceptEx(
    145         m_sListen,
    146         ol->m_sSock,
    147         ol->m_recvBuf.buf,
    148         ol->m_recvBuf.len - (sizeof(SOCKADDR_IN)+16)*2,
    149         sizeof(SOCKADDR_IN)+16,
    150         sizeof(SOCKADDR_IN)+16,
    151         &byteReceived,
    152         ol))
    153     {
    154         DWORD res = WSAGetLastError();
    155         if (ERROR_IO_PENDING != res)
    156         {
    157             cout<<"AcceptEx error , error code "<<res<<endl;
    158             return false;
    159         }
    160     }
    161     std::vector<SOCKET>::iterator iter = m_vecAcps.begin();
    162     for (;iter != m_vecAcps.end(); iter++)
    163     {
    164         if (*iter == s)
    165         {
    166             *iter = ol->m_sSock;
    167         }
    168     }
    169     if (iter == m_vecAcps.end())
    170     {
    171         m_vecAcps.push_back(ol->m_sSock);
    172     }
    173     return true;
    174 }
    175 
    176 bool CServer::DoAccept(COverlappedIOInfo* ol,DWORD NumberOfBytes)
    177 {
    178     //分支用于获取远端地址。
    179     //如果接收TYPE_ACP同时收到第一帧数据,则第一帧数据内包含远端地址。
    180     //如果没有收到第一帧数据,则通过getpeername获取远端地址
    181     SOCKADDR_IN* ClientAddr = NULL;
    182     int remoteLen = sizeof(SOCKADDR_IN);
    183     if (NumberOfBytes > 0)
    184     {
    185         //接受的数据分成3部分,第1部分是客户端发来的数据,第2部分是本地地址,第3部分是远端地址。
    186         if (m_lpfnGetAcceptSockAddrs)
    187         {
    188             SOCKADDR_IN* LocalAddr = NULL;  
    189             int localLen = sizeof(SOCKADDR_IN);  
    190             m_lpfnGetAcceptSockAddrs(
    191                 ol->m_recvBuf.buf,
    192                 ol->m_recvBuf.len - (sizeof(SOCKADDR_IN)+16)*2,
    193                 sizeof(SOCKADDR_IN)+16,
    194                 sizeof(SOCKADDR_IN)+16,
    195                 (LPSOCKADDR*)&LocalAddr,
    196                 &localLen,
    197                 (LPSOCKADDR*)&ClientAddr,
    198                 &remoteLen);
    199             cout<<"收到新的连接请求,ip="<<inet_ntoa(ClientAddr->sin_addr)<<",port="<<ClientAddr->sin_port<<
    200                 "数据为:"<<ol->m_recvBuf.buf<<endl;
    201         }
    202     }
    203     else if (NumberOfBytes==0)
    204     {
    205         //未收到第一帧数据
    206         if (SOCKET_ERROR ==getpeername(ol->m_sSock,(sockaddr*)ClientAddr,&remoteLen))
    207         {
    208             cout<<"getpeername error,error code "<<WSAGetLastError()<<endl;
    209         }
    210         else
    211         {
    212             cout<<"收到新的连接请求,ip="<<inet_ntoa(ClientAddr->sin_addr)<<",port="<<ClientAddr->sin_port<<endl;
    213         }
    214     }
    215 
    216     COverlappedIOInfo* pol = new COverlappedIOInfo;
    217     pol->m_sSock = ol->m_sSock;
    218     pol->m_addr =  *ClientAddr;
    219     //服务端只收取recv,同时监听recv和send可用设计位偏移,用或运算实现
    220     if (m_iocp.AssociateSocket(pol->m_sSock,TYPE_RECV))
    221     {
    222         PostRecv(pol);
    223 
    224         m_vecContInfo.push_back(pol);
    225     }
    226     else
    227     {
    228         delete pol;
    229         return false;
    230     }
    231 
    232     return true;
    233 }
    234 
    235 bool CServer::DoRecv(COverlappedIOInfo* ol)
    236 {
    237     cout<<"收到客户端数据:ip="<<inet_ntoa(ol->m_addr.sin_addr)<<",port="<<ol->m_addr.sin_port<<
    238         ";内容="<<ol->m_recvBuf.buf<<endl;
    239     return true;
    240 }
    241 
    242 bool CServer::PostRecv(COverlappedIOInfo* ol)
    243 {
    244     DWORD BytesRecvd = 0;
    245     DWORD dwFlags = 0;
    246     ol->ResetOverlapped();
    247     ol->ResetRecvBuffer();
    248     int recvnum = WSARecv(ol->m_sSock,&ol->m_recvBuf,1,&BytesRecvd,&dwFlags,(OVERLAPPED*)ol,NULL);
    249     if (recvnum != 0)
    250     {
    251         int res = WSAGetLastError();
    252         if (WSA_IO_PENDING != res)
    253         {
    254             cout<<"WSARecv error,error code "<<res<<endl;
    255         }
    256     }
    257     return true;
    258 }
    259 
    260 UINT CServer::StartThreadPull()
    261 {
    262     //获取系统cpu个数启动线程
    263     SYSTEM_INFO si;
    264     GetSystemInfo(&si);
    265     //启动cpu数量*2个线程
    266     return Activate(si.dwNumberOfProcessors * 2);
    267 }
    268 
    269 bool CServer::DeleteLink(SOCKET s)
    270 {
    271     m_lsc.lock();
    272     std::vector<COverlappedIOInfo*>::iterator iter = m_vecContInfo.begin();
    273     for (;iter!=m_vecContInfo.end();iter++)
    274     {
    275         if (s == (*iter)->m_sSock)
    276         {
    277             COverlappedIOInfo* ol = *iter;
    278             closesocket(s);
    279             m_vecContInfo.erase(iter);
    280             delete ol;
    281             break;
    282         }
    283     }
    284     m_lsc.unlock();
    285     return true;
    286 }
    287 
    288 void CServer::svc()
    289 {
    290     while (true)
    291     {
    292         DWORD  NumberOfBytes = 0;
    293         unsigned long CompletionKey = 0;
    294         OVERLAPPED*    ol = NULL;
    295         if (FALSE != GetQueuedCompletionStatus(m_iocp.GetIOCP(),&NumberOfBytes,&CompletionKey,&ol,WSA_INFINITE))
    296         {
    297             if (CompletionKey == TYPE_CLOSE)
    298             {
    299                 break;
    300             }
    301             if (NumberOfBytes == 0 && (CompletionKey==TYPE_RECV || CompletionKey==TYPE_SEND))
    302             {
    303                 //客户端断开连接
    304                 cout<<"客户端断开连接,ip="<<inet_ntoa(olinfo->m_addr.sin_addr)<<",port="<<olinfo->m_addr.sin_port<<endl;
    305                 DeleteLink(olinfo->m_sSock);
    306                 continue;
    307             }
    308             COverlappedIOInfo* olinfo = (COverlappedIOInfo*)ol;
    309             switch (CompletionKey)
    310             {
    311             case TYPE_ACP:
    312                 {
    313                     DoAccept(olinfo,NumberOfBytes);
    314                     PostAccept(olinfo);
    315                 }
    316                 break;
    317             case TYPE_RECV:
    318                 {
    319                     DoRecv(olinfo);
    320                     PostRecv(olinfo);
    321                 }
    322                 break;
    323             case TYPE_SEND:
    324                 {
    325                 }
    326                 break;
    327             default:
    328                 break;
    329             }
    330         }
    331         else
    332         {
    333             int res = WSAGetLastError();
    334             switch(res)
    335             {
    336             case ERROR_NETNAME_DELETED:
    337                 {
    338                     COverlappedIOInfo* olinfo = (COverlappedIOInfo*)ol;
    339                     if (olinfo)
    340                     {
    341                         cout<<"客户端异常退出,ip="<<inet_ntoa(olinfo->m_addr.sin_addr)<<",port="<<olinfo->m_addr.sin_port<<endl;
    342                         DeleteLink(olinfo->m_sSock);
    343                     }
    344                 }
    345                 break;
    346             default:
    347                 cout<<"workthread GetQueuedCompletionStatus error,error code "<<WSAGetLastError()<<endl;
    348                 break;
    349             }
    350             continue;
    351         }
    352     }
    353     cout<<"workthread stop"<<endl;
    354 }
    355 
    356 void CServer::CloseServer()
    357 {
    358     //1:清空IOCP线程队列,退出线程,有多少个线程发送多少个PostQueuedCompletionStatus信息
    359     int threadnum = GetThreadsNum();
    360     for (int i=0;i<threadnum;i++)
    361     {
    362         if (FALSE == m_iocp.PostStatus(TYPE_CLOSE))
    363         {
    364             cout<<"PostQueuedCompletionStatus error,error code "<<WSAGetLastError()<<endl;
    365         }
    366     }
    367     //2:清空等待accept的套接字m_vecAcps
    368     std::vector<SOCKET>::iterator iter = m_vecAcps.begin();
    369     for (;iter != m_vecAcps.end();iter++)
    370     {
    371         SOCKET s = *iter;
    372         closesocket(s);
    373     }
    374     m_vecAcps.clear();
    375     //3:清空已连接的套接字m_vecContInfo并清空缓存
    376     std::vector<COverlappedIOInfo*>::iterator iter2 = m_vecContInfo.begin();
    377     for (;iter2 != m_vecContInfo.end();iter2++)
    378     {
    379         COverlappedIOInfo* ol = *iter2;
    380         closesocket(ol->m_sSock);
    381         iter2 = m_vecContInfo.erase(iter2);
    382         delete ol;
    383     }
    384     m_vecContInfo.clear();
    385 }

    调用方法,控制台程序main函数内加入

    1 SERVER::Instance()->StartListen(8828,"127.0.0.1");
    2 int outId;
    3 cin>>outId;
    4 if (outId == 0)
    5 {
    6     SERVER::Close();
    7 }

    输入0结束服务程序。

    测试结果1,接收数据

    测试结果2,客户端断开连接

      

  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/hgwang/p/6126735.html
Copyright © 2011-2022 走看看