Let's explain how to use the completed port from Microsoft.
If you have used programming, you will naturally know what is going on. You will not read it again at a time and learn it without him or herself.
Luxury talks about efficiency and so on, it's just a coincidence.
This is an example in the console. It is an echo server,
After running, it will listen on port 5150. Once a port is connected, it sends data to the Service port, and it returns the data to that port until the connection is interrupted.
The complete port can be understood as a channel or pipe, which is not much different from the pipeline, but can be implemented asynchronously,
You can use getqueuedcompletionstatus to check whether data is generated at the end of the pipe, this pipe writes or reads data through a custom structure.
The complete port is actually the handle that identifies the data block,
// See the example below
# I nclude <winsock2.h>
# I nclude <windows. h>
# I nclude <stdio. h>
# Define Port 5150
# Define data_bufsize 8192
# Pragma comment (Lib, "ws2_32 ")
Typedef struct // This is the data filling, which is a custom data structure of the data.
// It is not much different from the wm_data, but it is an overlapped structure of the old fortress,
{
Overlapped;
Wsabuf databuf;
Char buffer [data_bufsize];
DWORD bytessend; // Number of sent bytes
DWORD bytesrecv;
} Per_io_operation_data, * lpper_io_operation_data;
Typedef struct
{
Socket socket;
} Per_handle_data, * lpper_handle_data;
DWORD winapi serverworkerthread (lpvoid completionportid );
Void main (void)
{
Sockaddr_in internetaddr;
Socket listen;
Socket accept;
Handle completionport;
System_info systeminfo;
Lpper_handle_data perhandledata;
Lpper_io_operation_data periodata;
Int I;
DWORD recvbytes;
DWORD flags;
DWORD threadid;
Wsadata;
DWORD ret;
If (ret = wsastartup (0x0202, & wsadata ))! = 0)
{
Printf ("wsastartup failed with error % d/N", RET );
Return;
}
//
// Set up the port twice. This is the first call. Why? I want to ask you
//
If (completionport = createiocompletionport (invalid_handle_value, null, 0, 0) = NULL)
{
Printf ("createiocompletionport failed with error: % d/N", getlasterror ());
Return;
}
// The old sub-API.
Getsysteminfo (& systeminfo );
// If two CPUs are found, open a double thread.
For (I = 0; I <systeminfo. dwnumberofprocessors * 2; I ++)
{
Handle threadhandle;
//
// The finished port is mounted to the thread, just as the pipe connects the two ends of Data filling and reading,
//
If (threadhandle = createthread (null, 0, serverworkerthread, completionport,
0, & threadid) = NULL)
{
Printf ("createthread () failed with error % d/N", getlasterror ());
Return;
}
Closehandle (threadhandle );
}
//
// Start a listener socket.
//
If (Listen = wsasocket (af_inet, sock_stream, 0, null, 0,
Wsa_flag_overlapped) = invalid_socket)
{
Printf ("wsasocket () failed with error % d/N", wsagetlasterror ());
Return;
}
Internetaddr. sin_family = af_inet;
Internetaddr. sin_addr.s_addr = htonl (inaddr_any );
Internetaddr. sin_port = htons (port );
If (BIND (Listen, (psockaddr) & internetaddr, sizeof (internetaddr) = socket_error)
{
Printf ("BIND () failed with error % d/N", wsagetlasterror ());
Return;
}
If (Listen, 5) = socket_error)
{
Printf ("Listen () failed with error % d/N", wsagetlasterror ());
Return;
}
//
// When the listener port is opened, the loop starts here. Once a socket is connected, wsaaccept creates a socket,
// The socket is connected to the completed port again,
//
// Hey, The createxxx function is called the second time after the port is completed. Why is it more profound for people to think about,
// This set of paths has to come twice,
// Port completionport and accept socket are mounted,
//
While (true)
{
// Wait until the main thread runs here, but the thread starts working,
If (accept = wsaaccept (Listen, null, 0) = socket_error)
{
Printf ("wsaaccept () failed with error % d/N", wsagetlasterror ());
Return;
}
If (perhandledata = (lpper_handle_data) globalalloc (gptr, sizeof (per_handle_data) = NULL)
{
Printf ("globalalloc () failed with error % d/N", getlasterror ());
Return;
}
Perhandledata-> socket = accept;
//
// Connect this end to the complete port completionport
// Just like you have connected the funnel to the pipe port and you are about to fill in data
//
If (createiocompletionport (handle) Accept, completionport, (DWORD) perhandledata,
0) = NULL)
{
Printf ("createiocompletionport failed with error % d/N", getlasterror ());
Return;
}
//
// Clear the data structure of the pipe and prepare to fill it with data
//
If (periodata = (lpper_io_operation_data) globalalloc (gptr, sizeof (per_io_operation_data) = NULL)
{
Printf ("globalalloc () failed with error % d/N", getlasterror ());
Return;
}
Zeromemory (& (periodata-> overlapped), sizeof (overlapped ));
Periodata-> bytessend = 0;
Periodata-> bytesrecv = 0;
Periodata-> databuf. Len = data_bufsize;
Periodata-> databuf. Buf = periodata-> buffer;
Flags = 0;
//
// When accept receives the data, it is put into periodata, and periodata is retrieved through the function in the thread,
//
If (wsarecv (accept, & (periodata-> databuf), 1, & recvbytes, & flags,
& (Periodata-> overlapped), null) = socket_error)
{
If (wsagetlasterror ()! = Error_io_pending)
{
Printf ("wsarecv () failed with error % d/N", wsagetlasterror ());
Return;
}
}
}
}
//
// Once the thread is called, it will always loop in it,
// Note that the input port is the complete port, which is used to retrieve the data in the pipe.
//
DWORD winapi serverworkerthread (lpvoid completionportid)
{
Handle completionport = (handle) completionportid;
DWORD bytestransferred;
Lpoverlapped overlapped;
Lpper_handle_data perhandledata;
Lpper_io_operation_data periodata;
DWORD sendbytes, recvbytes;
DWORD flags;
While (true)
{
//
// Check the data Buf area of the completed port. Is the data available?
// The description of this function parameter is required,
// Periodata is the data flowing from the pipe,
// Perhandledata is also taken from the pipe. When is it inserted,
// When createiocompletionport is created for 2nd times
//
If (getqueuedcompletionstatus (completionport, & bytestransferred,
(Lpdword) & perhandledata, (lpoverlapped *) & periodata, infinite) = 0)
{
Printf ("getqueuedcompletionstatus failed with error % d/N", getlasterror ());
Return 0;
}
// Check if data transfer is complete
If (bytestransferred = 0)
{
Printf ("Closing socket % d/N", perhandledata-> socket );
If (closesocket (perhandledata-> socket) = socket_error)
{
Printf ("closesocket () failed with error % d/N", wsagetlasterror ());
Return 0;
}
Globalfree (perhandledata );
Globalfree (periodata );
Continue;
}
//
// Check if there is data in the pipe? = 0. That is the data you just received.
//
If (periodata-> bytesrecv = 0)
{
Periodata-> bytesrecv = bytestransferred;
Periodata-> bytessend = 0;
}
Else,
{
Periodata-> bytessend + = bytestransferred;
}
//
// Is the data not sent? Continue sending
//
If (periodata-> bytesrecv> periodata-> bytessend)
{
Zeromemory (& (periodata-> overlapped), sizeof (overlapped); // clear 0 to prepare for sending
Periodata-> databuf. Buf = periodata-> buffer + periodata-> bytessend;
Periodata-> databuf. Len = periodata-> bytesrecv-periodata-> bytessend;
// One byte sends data out
If (wsasend (perhandledata-> socket, & (periodata-> databuf), 1, & sendbytes, 0,
& (Periodata-> overlapped), null) = socket_error)
{
If (wsagetlasterror ()! = Error_io_pending)
{
Printf ("wsasend () failed with error % d/N", wsagetlasterror ());
Return 0;
}
}
}
Else
{
Periodata-> bytesrecv = 0;
Flags = 0;
Zeromemory (& (periodata-> overlapped), sizeof (overlapped ));
Periodata-> databuf. Len = data_bufsize;
Periodata-> databuf. Buf = periodata-> buffer;
If (wsarecv (perhandledata-> socket, & (periodata-> databuf), 1, & recvbytes, & flags,
& (Periodata-> overlapped), null) = socket_error)
{
If (wsagetlasterror ()! = Error_io_pending)
{
Printf ("wsarecv () failed with error % d/N", wsagetlasterror ());
Return 0;
}
}
}
}
}