To get back to basics, today I want to talk about those things about the Internet. I specially found some materials to learn notes. iocp is a classic model in Windows, which can be called all asynchronous communication, so I found a powerful example to learn and analyze. First look at the main API
HANDLE CreateIoCompletionPort(HANDLE fileHandle, / / the connected socket handle, not null
HANDLE ExistingCompletionPort, / / the created completion port is not empty
ULONG_PTR CompletionKey, / / similar to the thread parameter, when binding, the user-defined structure pointer is passed in. When receiving the event, you can know that it is the event on the file handle.
DWORD NumberOfConcurrentThreads / / number of threads for listening
);
Its application is as follows:
typedef struct _IO_CONTEXT { OVERLAPPED m_Overlapped; //Overlapping structure SOCKET mSockClient; //Client socket received WSABUF m_wsaBuf; //Cache variables char m_szBuffer[1024]; //Default cache address, default size is 1024 int m_type; //Operation type, whether to send or receive connection request, etc }IO_CONTEXT,*PIO_CONTEXT; typedef struct _CLIENT_CONTEXT { SOCKET m_socket; //socket sockaddr_in m_addr; //Socket address std::vector<PIO_CONTEXT> mIOContexts; //Cache structure of socket }CLIENT_CONTEXT,PCLIENT_CONTEXT; HANDLE m_hIOCompletionPort = 0; // Handle to completion port 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); } ....
When listening is created, you can start the completion port listening event and start to get the event. Here is a brief introduction.
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytesTransferred,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED * lpOverlapped,
DWORD dwMilliseconds
);
BOOL PostQueueCompletionStatus(
DWORD dwNumberOfBytesTransferred,
ULONG_PTR dwCompletionKey,
LPOVERLAPPED lpOverlapped
);
Its application is as follows:
OVERLAPPED *pOverlapped = NULL; CLIENT_CONTEXT *pSocketContext = NULL; DWORD dwBytesTransfered = 0; BOOL bReturn = GetQueuedCompletionStatus( m_hIOCompletionPort, &dwBytesTransfered, (PULONG_PTR)&pSocketContext, &pOverlapped, INFINITE); ///Idle no events if(!bReturn) { } ////There are io completion events else { IO_CONTEXT* pIoContext = CONTAINING_RECORD(pOverlapped, IO_CONTEXT, m_Overlapped); //Judge whether the connection is broken by the length of bytes transmitted and events, that is, a client is disconnected if(dwBytesTransfered==0 &&(pIOContext->mtype == 1 || pIOContext->mtype == 2)) { //Remove the remote monitoring context list, that is, remove the context of the remote connection. } else { //Process completed events based on event type switch(pIOContext->m_type) { case 0: //There are new remote connection requests coming Deal_AcceptEvent(pSocketContext,pIOContext); break; case 1: //There is a new read completion event coming Deal_ReadEvent(pSocketContext,pIOContext); break; case 2: //There is a new write completion event coming Deal_WriteEvent(pSocketContext,pIOContext); break; } } ....
If you want to receive a remote connection asynchronously, you can use the system defined asynchronous pointer, as follows:
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}}
The above is the function pointer of asynchronous receive connection, which can be used in the program
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); //When receiving a remote connection request, you need to add the received remote end to the completion port monitoring list. The remote data is retrieved from the sent parameters through GetQueuedCompletionStatus, and the IO context is obtained through overlapping 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. First obtain the address information of the connected client // This m [lpfngetacceptexsockaddrs] is awesome~~~~~~ // Not only can get the address information of the client and the local side, but also get the first group of data sent by the client by the way. It's old and powerful 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)); //The received remote context needs to be added to the completion port to listen for messages. CreateIoCompletionPort((HANDLE)pNewClientContext->m_Socket, m_hIOCompletionPort, (DWORD)pNewClientContext, 0); //At this time, listen to remote messages and send asynchronous read events.
There is no division between reading and writing. At this time, we should explain reading and writing together
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
);
For asynchronous read operation, send WSARecv in advance, and wait for the arrival of data. After reading, the data can be processed. For asynchronous write operation, multiple pieces of content can be written at one time. After the write completion event arrives, you can determine whether the write is successful and how many bytes have been written, and then carry out subsequent operations. Look at the application:
Looking back, after receiving the remote connection, you need to send a read operation, receive the message sent remotely, or send asynchronous write directly to the remote.
IO_CONTEXT* pNewIoContext = pNewSocketContext->GetNewIoContext(); //Apply to join vector directly in the context here 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 ); //When a read completion event is received, in addition to processing the received data, it is also necessary to send a read operation for the next receive according to the memory usage
The asynchronous write instance will not be sent for the moment, which is what I understand to complete some port operations. Refer to the code of some big bulls on the Internet, welcome to comment on it big bulls, and make corrections.