About the Internet

Keywords: socket Windows

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.

32 original articles published, praised 4, visited 50000+
Private letter follow

Posted by SalientAnimal on Thu, 20 Feb 2020 19:20:34 -0800