windows完成端口(二)

系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六)

1

不知道你是否记得前面中说过每消耗一个预先准备客户端的socket,就要补上一个。这个代码现在看来就应该放在连接成功事件里面了:

DWORD ThreadFunction()  
{  
    OVERLAPPED           *pOverlapped = NULL;  
    PER_SOCKET_CONTEXT   *pSocketContext = NULL;  
    DWORD                dwBytesTransfered = 0;  

    BOOL bReturn = GetQueuedCompletionStatus(m_hIOCompletionPort, 
                                             &dwBytesTransfered, 
                                             (PULONG_PTR)&pSocketContext,
                                             &pOverlapped, INFINITE);  

    if (((SOME_STRUCT*)pSocketContext)->s == 侦听socket句柄)  
    {  
        /*连接成功后可以做以下事情:  
         1. 获取对端和本端的ip地址和端口号,即AcceptEx的
             第三个参数lpOutputBuffer中拿(这一步,不是必须)  
         2. 如果对端连接成功后会发数据过来,则可以从初始化时
             调用AcceptEx准备的缓冲区里面拿到,即AcceptEx的
              第三个参数lpOutputBuffer中拿(这一步不是必须)  

         3. 再次调用AcceptEx补充一个sAcceptSocket
           (这一步是必须的)*/  
    }  
    //普通客户端socket收发数据  
    else  
    {  
        //通过pOverlapped结构得到pIOContext  
        PER_IO_CONTEXT* pIOContext = (PER_IO_CONTEXT*)pOverlapped;    
        if (pIOContext->Type == 收)  
        {  
            //做一些操作2,比如解析数据  
        }  
        else if (pIOContext->Type == 发)  
        {  
            //做一些操作3,比如显示一条数据发送成功信息  
        }  
    }  

}  

上面连接成功后的伪码,第1步和第2步不是必须的,而第3步是必须的,如果不及时补充的话,等连接数多于准备的socket,可能就会发生故障了。

因为两端的地址信息和对端发过来的第一组数据都在同一个缓冲区里面,再次看下AcceptEx函数签名吧:

BOOL AcceptEx(  
  _In_  SOCKET       sListenSocket,  
  _In_  SOCKET       sAcceptSocket,  
  _In_  PVOID        lpOutputBuffer,  
  _In_  DWORD        dwReceiveDataLength,  
  _In_  DWORD        dwLocalAddressLength,  
  _In_  DWORD        dwRemoteAddressLength,  
  _Out_ LPDWORD      lpdwBytesReceived,  
  _In_  LPOVERLAPPED lpOverlapped  
);  

虽然可以根据dwReceiveDataLength、dwLocalAddressLength、dwRemoteAddressLength、lpdwBytesReceived这几个参数计算出来,但是微软提供了一个函数来帮我们做这个解析动作: GetAcceptExSockaddrs 同理,这个函数最好也要通过WSAIoctl函数来动态获取:

// 获取GetAcceptExSockAddrs函数指针,也是同理  
if(SOCKET_ERROR == WSAIoctl(  
    m_pListenContext->m_Socket,   
    SIO_GET_EXTENSION_FUNCTION_POINTER,   
    &GuidGetAcceptExSockAddrs,  
    sizeof(GuidGetAcceptExSockAddrs),   
    &m_lpfnGetAcceptExSockAddrs,   
    sizeof(m_lpfnGetAcceptExSockAddrs),     
    &dwBytes,   
    NULL,   
    NULL))    
{    
    this->_ShowMessage(_T("WSAIoctl 未能获取GuidGetAcceptExSockAddrs函数指针。错误代码: %d\n"), 
                          WSAGetLastError());  
    this->_DeInitialize();  
    return false;   
}  

然后使用返回的函数指针来使用函数GetAcceptExSockaddrs。解析地址信息和第一组数据的代码如下:

<pre code_snippet_id="2472609" 
snippet_file_name="blog_20170706_24_1671088" 
name="code"
class="cpp">SOCKADDR_IN* ClientAddr = NULL;  
SOCKADDR_IN* LocalAddr = NULL;    
int remoteLen = sizeof(SOCKADDR_IN);
int localLen = sizeof(SOCKADDR_IN);    

///////////////////////////////////////////////////////////////////////////  
// 1. 首先取得连入客户端的地址信息  
// 这个 m_lpfnGetAcceptExSockAddrs 不得了啊~~~~~~  
// 不但可以取得客户端和本地端的地址信息,
//还能顺便取出客户端发来的第一组数据,老强大了...  
this->m_lpfnGetAcceptExSockAddrs(pIoContext->m_wsaBuf.buf,
                                 pIoContext->m_wsaBuf.len - ((sizeof(SOCKADDR_IN)+16)*2),    
                                 sizeof(SOCKADDR_IN)+16, 
                                 sizeof(SOCKADDR_IN)+16,
                                 (LPSOCKADDR*)&LocalAddr,
                                 &localLen,
                                 (LPSOCKADDR*)&ClientAddr, 
                                 &remoteLen);    

this->_ShowMessage( _T("客户端 %s:%d 连入."), 
                   inet_ntoa(ClientAddr->sin_addr), 
                   ntohs(ClientAddr->sin_port) );  
this->_ShowMessage( _T("客户额 %s:%d 信息:%s."),
                    inet_ntoa(ClientAddr->sin_addr),
                    ntohs(ClientAddr->sin_port),
                    pIoContext->m_wsaBuf.buf );
  </pre><br><br>  

2

以上介绍的是接收新连接成功后的处理,那收数据和发数据的准备工作在哪里做呢?(收取第一组数据可以在调用AcceptEx的地方做)。这个就仁者见仁,智者见智了。比如可以在新连接接收成功之后,立即准备给对端发数据;或者在收到对端数据的时候准备给对端发数据;在发送数据完成后准备收对端数据。伪码如下:

DWORD ThreadFunction()  
{  
    OVERLAPPED           *pOverlapped = NULL;  
    PER_SOCKET_CONTEXT   *pSocketContext = NULL;  
    DWORD                dwBytesTransfered = 0;  

    BOOL bReturn = GetQueuedCompletionStatus(m_hIOCompletionPort, 
                                             &dwBytesTransfered,
                                             (PULONG_PTR)&pSocketContext,
                                             &pOverlapped, INFINITE);  

    if (((SOME_STRUCT*)pSocketContext)->s == 侦听socket句柄)  
    {  
        /*连接成功后可以做以下事情:  
        1. 获取对端和本端的ip地址和端口号,
           即AcceptEx的第三个参数lpOutputBuffer中拿(这一步,不是必须)  
        2. 如果对端连接成功后会发数据过来,则可以从初始化时
           调用AcceptEx准备的缓冲区里面拿到,即AcceptEx的
           第三个参数lpOutputBuffer中拿(这一步不是必须)  

        3. 再次调用AcceptEx补充一个sAcceptSocket
          (这一步是必须的)  

        4. 调用WSASend准备发送数据工作或调用WSARecv准备接收数据工作
           (这一步,不是必须)*/  
    }  
    //普通客户端socket收发数据  
    else  
    {  
        //通过pOverlapped结构得到pIOContext  
        PER_IO_CONTEXT* pIOContext = (PER_IO_CONTEXT*)pOverlapped;    
        if (pIOContext->Type == 收)  
        {             
            //解析收到的数据
            //(这一步,不是必须)  
            //调用WSASend准备发送数据工作
            //(比如应答客户端)(这一步,不是必须)  
            //继续调用WSARecv准备收取数据工作
            //(这一步,不是必须)  
        }  
        else if (pIOContext->Type == 发)  
        {  
            //调用WSARecv准备收取数据工作(这一步,不是必须)  
        }  
    }  

}  

由于公众号文章字数有限,您可以接着阅读下一篇:《windows完成端口(三)》 系列目录 windows完成端口(一) windows完成端口(二) windows完成端口(三) windows完成端口(四) windows完成端口(五) windows完成端口(六)

原文发布于微信公众号 - 高性能服务器开发(easyserverdev)

原文发表时间:2018-04-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏圣杰的专栏

Asp.net mvc 知多少(十)

本系列主要翻译自《ASP.NET MVC Interview Questions and Answers 》- By Shailendra Chauhan,想...

21210
来自专栏恰童鞋骚年

.NET Core微服务之基于IdentityServer建立授权与验证服务(续)

上一篇我们基于IdentityServer4建立了一个AuthorizationServer,并且继承了QuickStartUI,能够成功获取Token了。这一...

2935
来自专栏FreeBuf

远程RPC溢出EXP编写实战之MS06-040

0x01 前言 MS06-040算是个比较老的洞了,在当年影响十分之广,基本上Microsoft大部分操作系统都受到了影响,威力不亚于17年爆出的”永恒之蓝”漏...

30510
来自专栏数据和云

监控工具:Oracle 12c Cluster Health Monitor 详解

? 戴明明(Dave) Oracle ACE-A,ACOUG核心成员,宝存科技数据库方案架构师 Dave也是CSDN 认证专家,超过7年的DBA经验,擅长O...

4229
来自专栏Urahara Blog

Windows上传并执行恶意代码的N种姿势

5322
来自专栏圣杰的专栏

Asp.net mvc 知多少(一)

本系列主要翻译自《ASP.NET MVC Interview Questions and Answers 》- By Shailendra Chauhan,想...

2807
来自专栏QQ音乐技术团队的专栏

Android点九图总结以及在聊天气泡中的使用

点九图的本质实际上是在图片的四周各增加了1px的像素,并使用纯黑的线进行标记,其它的与原图没有任何区别。

8663
来自专栏MasiMaro 的技术博文

socket模型处理多个客户端

最近学完了简单的socket编程,发现其实socket的网络编程其实并没有什么难度,只是简单的函数调用,记住客户端与服务端的步骤,写起来基本没有什么问题。 ...

2752
来自专栏24K纯开源

OpenProcess打开进程返回错误的问题

问题描述       项目中需要做一个小功能:能够查看系统中当前正在运行的进程的内存信息,如内存块类型、分配状态、访问权限等。如下图所示: ?       需要...

44710
来自专栏Kubernetes

cluster-proportional-autoscaler源码分析及如何解决KubeDNS性能瓶颈

Author: xidianwangtao@gmail.com 工作机制 cluster-proportional-autoscaler是kubernetes的...

57910

扫码关注云+社区

领取腾讯云代金券