关于网络的那些事

重拾基础,今天要来说一下关于网络的那些事,特意找了一些资料来学习笔记。iocp是Windows中比较经典的一个模型,这个模型堪称全异步通信,所以找了实例强悍的一位大牛来学习分析。先看主要的API

HANDLE CreateIoCompletionPort(HANDLE fileHandle, //连入的套接字句柄,没有置空

                                                        HANDLE ExistingCompletionPort,  //已经创建的完成端口,没有置空

                                                        ULONG_PTR CompletionKey,  //类似于线程参数,绑定的时候传入自定义的结构体指针,此时收到事件时,可以知道是那个文件句柄上的事件。

                                                        DWORD NumberOfConcurrentThreads  //用于监听的线程数

                                                        );

其应用如下:

typedef struct _IO_CONTEXT
{
    OVERLAPPED m_Overlapped;        //重叠结构
    SOCKET     mSockClient;         //接收的客户端套接字
    WSABUF     m_wsaBuf;            //缓存变量
    char       m_szBuffer[1024];    //默认缓存地址,默认大小为1024
    int        m_type;              //操作类型,是发送接收还是收到连接请求等
}IO_CONTEXT,*PIO_CONTEXT;
typedef struct _CLIENT_CONTEXT
{
    SOCKET m_socket;                       //套接字
    sockaddr_in m_addr;                    //套接字地址   
    std::vector<PIO_CONTEXT> mIOContexts;  //套接字的缓存结构
}CLIENT_CONTEXT,PCLIENT_CONTEXT;
HANDLE                       m_hIOCompletionPort = 0;           // 完成端口的句柄
CLIENT_CONTEXT               *m_pListenSocket;
m_pListenSocket = new CLIENT_CONTEXT;
m_pListenSocket->m_socket = WSASocket(AF_INET,SOCK_STREAM,0,NULL,WSA_FLAG_OVERLAPPED);

m_hIOCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );
if(m_hIOCompletionPort != 0)
{
    CreateIoCompletionPort((HANDLE)m_pListenSocket->m_socket,m_hIOCompletionPort,(ULONG_PTR)m_pListenSocket,0);
}
....

当创建了监听之后,可以开启完成端口监听事件,开始获取事件,下面简要说以下。

BOOL GetQueuedCompletionStatus(

                              HANDLE CompletionPort,
                              LPDWORD lpNumberOfBytesTransferred,
                              PULONG_PTR lpCompletionKey,
                              LPOVERLAPPED * lpOverlapped,
                              DWORD dwMilliseconds

);

BOOL PostQueueCompletionStatus(

                          DWORD dwNumberOfBytesTransferred,
                          ULONG_PTR dwCompletionKey,
                          LPOVERLAPPED lpOverlapped

);

其应用如下:

OVERLAPPED           *pOverlapped = NULL;
CLIENT_CONTEXT   *pSocketContext = NULL;
DWORD                dwBytesTransfered = 0;
BOOL bReturn = GetQueuedCompletionStatus(
			m_hIOCompletionPort,
			&dwBytesTransfered,
			(PULONG_PTR)&pSocketContext,
			&pOverlapped,
			INFINITE);
///空闲没有事件
if(!bReturn)
{
}
有io完成事件
else
{
    IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, IO_CONTEXT, m_Overlapped);
    //通过传输的字节长度以及事件判断连接是否断开了,即某一个客户端断开连接
    if(dwBytesTransfered==0 &&(pIOContext->mtype == 1 || pIOContext->mtype == 2))
    {
        //移除出远程监控上下文列表,即远程连接的上下文去掉。
    }
    else
    {
        //根据事件类型处理完成的事件
        switch(pIOContext->m_type)
        {
        case 0:  //有新的远程连接请求到来
            Deal_AcceptEvent(pSocketContext,pIOContext);
            break;
        case 1:  //有新的读完成事件到来
            Deal_ReadEvent(pSocketContext,pIOContext);
            break;
        case 2:  //有新的写完成事件到来
            Deal_WriteEvent(pSocketContext,pIOContext);
            break;
    }
}

....

如果接收远程连接也要异步的话,可以使用系统定义的异步指针,下面讲一下:

BOOL (*LPFN_ACCEPTEX)(SOCKET sListenSocket,

                                              SOCKET sAcceptSocket,

                                              PVOID lpOutputBuffer,

                                              DWORD dwReceiveDataLength,

                                              DWORD dwLocalAddressLength,

                                              DWORD dwRemoteAddressLength,

                                              LPDWORD lpdwBytesReceived,

                                              LPOVERLAPPED lpOverlapped)

#define WSAID_ACCEPTEX \
        {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}

void  (* LPFN_GETACCEPTEXSOCKADDRS)(
                                                 PVOID lpOutputBuffer,
                                                 DWORD dwReceiveDataLength,
                                                 DWORD dwLocalAddressLength,
                                                 DWORD dwRemoteAddressLength,
                                                 struct sockaddr **LocalSockaddr,
                                                  LPINT LocalSockaddrLength,
                                                  struct sockaddr **RemoteSockaddr,
                                                  LPINT RemoteSockaddrLength
    );

#define WSAID_GETACCEPTEXSOCKADDRS \
        {0xb5367df2,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}

以上是异步接收连接的函数指针,程序中可以这样用

LPFN_ACCEPTEX                m_lpfnAcceptEx = nullptr;                
LPFN_GETACCEPTEXSOCKADDRS    m_lpfnGetAcceptExSockAddrs=nullptr;
GUID GuidAcceptEx = WSAID_ACCEPTEX;  
GUID GuidGetAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS; 
DWORD dwBytes = 0;
WSAIoctl(m_pListenSocket->m_socket,SIO_GET_EXTENSION_FUNCTION_POINTER,&GuidAcceptEx,
    sizeof(GuidAcceptEx),&m_lpfnAcceptEx,sizeof(m_lpfnAcceptEx),&dwBytes,nullptr,nullptr);

WSAIoctl(m_pListenSocket->m_socket,SIO_GET_EXTENSION_FUNCTION_POINTER,&GuidGetAcceptExSockAddrs,    sizeof(GuidGetAcceptExSockAddrs),&m_lpfnGetAcceptExSockAddrs,sizeof(m_lpfnGetAcceptExSockAddrs),&dwBytes,nullptr,nullptr);
IO_CONTEXT *pIOContext = new IO_CONTEXT;
m_pListenSocket->mIOContexts.push_back(pIOContext);
pIOContext->m_type = 0; //accept_posted;
WSABUF *p_wbuf = &pIOContext->m_wsaBuf;
OVERLAPPED *p_ol = &pIOContext->m_Overlapped;
m_lpfnAcceptEx(m_pListenSocket->m_socket,pAcceptIoContext->m_socket,p_wbuf->buf,p_wbuf->len - ((sizeof(sockaddr_in)+16)*2),sizeof(sockaddr_in) + 16,sizeof(sockaddr_in) + 16,&dwBytes,p_ol);



//当收到远程连接请求时,需要将收到的远端加入到完成端口监控列表中,远端的数据从发送的参数中通过GetQueuedCompletionStatus取回,通过重叠io获取io上下文
//PER_SOCKET_CONTEXT   *pSocketContext = NULL;
//PER_IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, PER_IO_CONTEXT, m_Overlapped);
CLIENT_CONTEXT *pNewClientContext = new CLIENT_CONTEXT;
pNewClientContext->m_socket = pIoContext->mSockClient;

SOCKADDR_IN* ClientAddr = NULL;
	SOCKADDR_IN* LocalAddr = NULL;  
	int remoteLen = sizeof(SOCKADDR_IN), localLen = sizeof(SOCKADDR_IN);  

	///
	// 1. 首先取得连入客户端的地址信息
	// 这个 m_lpfnGetAcceptExSockAddrs 不得了啊~~~~~~
	// 不但可以取得客户端和本地端的地址信息,还能顺便取出客户端发来的第一组数据,老强大了...
	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);  
memcpy(&(pNewClientContext->m_addr),ClientAddr,sizeof(sockaddr_in));

//需要将收到的远程上下文加入完成端口监听消息。
CreateIoCompletionPort((HANDLE)pNewClientContext->m_Socket, m_hIOCompletionPort, (DWORD)pNewClientContext, 0);

//此时监听远程消息,发送异步读事件。

读写不分家,此时共同阐述读和写

int WSARecv(

                    SOCKET s,
                    LPWSABUF lpBuffers,
                    DWORD dwBufferCount,
                    LPDWORD lpNumberOfBytesRecvd,
                    LPDWORD lpFlags,
                    LPWSAOVERLAPPED lpOverlapped,
                    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

);

int WSASend(

                       SOCKET s,
                       LPWSABUF lpBuffers,
                       DWORD dwBufferCount,
                       LPDWORD lpNumberOfBytesSent,
                       DWORD dwFlags,
                       LPWSAOVERLAPPED lpOverlapped,
                       LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

);

异步读操作的话,提前发出WSARecv,然后等待有数据到来,读完成之后就可以对数据进行处理,异步写操作的话,可以一次写多块内容,写完成事件到来之后,可以确定是否写成功了,写了多少字节,据此进行后续操作。看应用:

回看上文,当收到远程连接之后,此时需要发送一个读操作,接收远程发过来的消息,或者直接给远程发送异步写。

IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext();  //此处在上下文中申请直接加入vector
pNewIoContext->m_type       = 1;
pNewIoContext->m_sockClient   = pNewSocketContext->m_socket;
DWORD dwFlags = 0;
DWORD dwBytes = 0;
WSABUF *p_wbuf   = &pNewIoContext->m_wsaBuf;
OVERLAPPED *p_ol = &pNewIoContext->m_Overlapped;
int nBytesRecv = WSARecv( pNewIoContext->m_sockAccept, p_wbuf, 1, &dwBytes, &dwFlags, p_ol, NULL );
//当收到读完成事件的时候,除了处理收到的数据之外,还要根据内存使用情况,为下次接收发送读操作

异步写的实例暂时先不发了,这就是我理解的完成端口的一些操作。参考了网上一些大牛的代码,欢迎各位it大牛品评,并做出修正意见。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值