Iocp model Summary (Summary)

Source: Internet
Author: User

I will repeat the old iocp code and have been playing with other things recently. I will review it from time to time, with a lot of benefits.

Iocp (I/O completion port, I/O completion port) is the best I/O model. It is a mechanism by which applications use thread pools to process asynchronous I/O requests. When processing multiple concurrent asynchronous I/O requests, the previous model was to create a thread to respond to requests when receiving requests. In this way, many threads run in parallel in the system. These threads are all runable. The Windows Kernel spends a lot of time performing context switching on the thread, and does not spend much time running the thread. The overhead of creating new threads is relatively large, resulting in low efficiency.

The call procedure is as follows:
Abstract A general process of processing the Port:
1: create a complete port.
2: Create a thread.
3: thread a cyclically calls the getqueuedcompletionstatus () function to obtain the IO operation result. This function is a blocking function.
4: The accept is called in the main thread loop to wait for the client to connect.
5: after the new connection is established, the accept in the main thread associates the new socket handle with the createiocompletionport to the completion port, and then sends out an asynchronous wsasend or wsarecv call because it is an asynchronous function, wsasend/wsarecv will return immediately. The actual operation to send or receive data is performed by the Windows system.
6: The main thread continues the next loop, blocking the accept and waiting for client connection.
7. Complete wsasend or wsarecv operations in windows and send the result to the completion port.
8: getqueuedcompletionstatus () in thread a is returned immediately, and the result of wsasend/wsarecv is obtained from the completion port.
9: process the data in thread a (if the processing process is very time-consuming, it needs to be processed by a new thread), and then issue wsasend/wsarecv, and continue the next Loop Blocking in getqueuedcompletionstatus () here.
In the final words, the port model is summarized in one sentence:
We keep sending asynchronous wsasend/wsarecv Io operations. The specific Io processing process is completed by the Windows system. After the Windows system completes the actual Io processing, send the result to the completion port (if multiple I/O operations are completed, the completion port is lined up in a queue ). In another thread, we constantly retrieve the IO operation results from the completed port, and then issue the wsasend/wsarecv Io operation as needed.

The iocp model enables n threads to store online thread pools and hold them. Then, all user requests are shipped to a completion port, and N worker threads obtain and process user messages from the completion port one by one. This avoids opening a thread for each user. This reduces thread resources and improves thread utilization.

How is the port model implemented? First, create a complete port (: createiocompletioport ()). Then create one or more worker threads and specify them to read data on the completion port. Then we associate the socket handle of the remote connection with this port (Use: createiocompletionport ()). Everything is OK.

What do working threads do? First, call the: getqueuedcompletionstatus () function to wait for the completion of I/O on all sockets associated with the completion port. Then determine the type of I/O completed. Generally, there are three types of I/O, op_accept, op_read, and op_wirte. After reading data in the data buffer, we can ship one or more I/O of the same type (: acceptex (),: wsarecv (),:: wsasend ()). We can process the read data as needed.

Therefore, we need a custom per-I/O Data Structure with overlapped (overlapping I/O) structure as the first field.

Typedef struct _ per_io_data
{
Overlapped ol; // overlapping I/O structure
Char Buf [buffer_size]; // data buffer
Int noperationtype; // I/O operation type
# Define op_read 1
# Define op_write 2
# Define op_accept 3
} Per_io_data, * pper_io_data;

Forcibly convert a per_io_data structure into an overlapped structure and pass it to the: getqueuedcompletionstatus () function. The noperationtype of the returned per_io_data structure is the type of the I/O operation. Of course, these types are all set when I/O requests are shipped.

The framework of such an iocp server will come out. Of course, to be a good iocp server, there are still many issues to consider, such as memory resource management, method of accepting connections, malicious customer connections, and package re-sorting. The above are my personal understandings and opinions on the iocp model, which need to be improved. For more information about how to use Winsock APIs, see msdn.


Supplement the implementation of the iocp model:

// Create a complete port
Handle fcompletport = createiocompletionport (invalid_handle_value, 0, 0 );

// Accept the remote connection and bind the socket handle of the connection to the newly created iocp
Aconnect = accept (flistensock, ADDR, Len );
Createiocompletionport (aconnect, fcompletport, null, 0 );

// Number of CPUs created * 2 + 2 threads
System_info Si;
Getsysteminfo (& Si );
For (INT I = 1; Si. dwnumberofprocessors * 2 + 2; I ++)
{
Athread = trecvsendthread. Create (false );
Athread. completport = fcompletport; // tell this thread that you are going to access data through this iocp.
}

OK, that's simple. What we need to do is to create an iocp, bind the remote connection socket handle to the iocp we just created, and finally create n threads, and tell the n threads to access the data to the iocp.

Let's take a look at what the trecvsendthread is doing:

Void trecvsendthread. Execute (...)
{
While (! Self. Terminated)
{
// Query the iocp status (whether the data read/write operation is complete)
Getqueuedcompletionstatus (completport, bytestransd, completkey, poverlapped (pperiodat), time_out );

If (bytestransd! = 0 ).......
...; // Data read/write operation completed

// Send another read data request
Wsarecv (completkey, & (pperiodat-> bufdata), 1, bytesrecv, flags, & (pperiodat-> overlap), null );
}
}

The read/write thread simply checks whether iocp has completed the read/write operations we ship. If yes, it then delivers a new read/write request.
It should be noted that all the trecvsendthreads we created are accessing the same iocp (because we only created one iocp), and we didn't use the critical section! Won't there be conflicts? Do you have to worry about synchronization?
This is exactly the secret of iocp. Iocp is not a common object and does not need to consider thread security issues. It will automatically allocate the thread to access it: if a thread a is accessing a socket, the access request of thread B will be allocated to another socket. All of this is automatically distributed by the system, so we don't need to ask.

Instance:

Simple implementation, suitable for getting started with iocp
Reference: Windows network and communication program design

 

/*************************************** ***************************
*
*
* File name: iocpheader. h
* Abstract: iocp definition file
*
* Prepared by: Jeson Yang
* Completion date:
*
* Replaced version:
* Original
* Completion date:
*
**************************************** **************************/

# Ifndef _ iocpheader_h_20080916 _
# DEFINE _ iocpheader_h_20080916 _

# Include <winsock2.h>
# Include <windows. h>

# Define buffer_size 1024

/*************************************** ***************************
* Per_handle data
**************************************** ***************************/
Typedef struct _ per_handle_data
{
Socket s; // corresponding socket handle
Sockaddr_in ADDR; // address of the other party

} Per_handle_data, * pper_handle_data;

/*************************************** ***************************
* Per_io data
**************************************** ***************************/
Typedef struct _ per_io_data
{
Overlapped ol; // overlapping structure
Char Buf [buffer_size]; // data buffer
Int noperationtype; // Operation Type

# Define op_read 1
# Define op_write 2
# Define op_accept 3

} Per_io_data, * pper_io_data;

# Endif

/*************************************** ***************************
*
*
* File name: Main. cpp
* Abstract: iocp demo
*
* Version: 1.0
* Prepared by: Jeson Yang
* Completion date:
*
* Replaced version:
* Original
* Completion date:
*
**************************************** **************************/

# Include <iostream>
# Include <string>
# Include "iocpheader. H"
Using namespace STD;

DWORD winapi serverthread (lpvoid lpparam );

Int main (INT argc, char * argv [])
{
//////////////////////////////////////// //////////////////////////////////
Wsadata;

If (0! = Wsastartup (makeword (2, 2), & wsadata ))
{
Printf ("using % s (Status: % s) \ n", wsadata. szdescription, wsadata. szsystemstatus );
Printf ("with API versions: % d. % d to % d. % d ",
Lobyte (wsadata. wversion), hibyte (wsadata. wversion ),
Lobyte (wsadata. whighversion), hibyte (wsadata. whighversion ));

Return-1;
}
Else
{
Printf ("Windows Sockets 2.2 Startup \ n ");
}
//////////////////////////////////////// //////////////////////////////////

Int nport = 20055;

// Create a port object
// Create a working thread to process events that have completed the port object
Handle hiocp =: createiocompletionport (invalid_handle_value, 0, 0 );
: Createthread (null, 0, serverthread, (lpvoid) hiocp, 0, 0 );

// Create a listening socket, bind the local port, and start listening
Socket slisten =: socket (af_inet,-sock_stream, 0 );

Sockaddr_in ADDR;
ADDR. sin_family = af_inet;
ADDR. sin_port =: htons (nport );
ADDR. sin_addr.s_un.s_addr = inaddr_any;
: BIND (slisten, (sockaddr *) & ADDR, sizeof (ADDR ));
: Listen (slisten, 5 );

Printf ("iocp Demo Start... \ n ");

// Process incoming requests cyclically
While (true)
{
// Wait for accepting pending connection requests
Sockaddr_in saremote;
Int nremotelen = sizeof (saremote );
Socket sremote =: accept (slisten, (sockaddr *) & saremote, & nremotelen );

// After receiving the new connection, create a per_handle data for it and associate them with the completion port object.
Pper_handle_data pperhandle = (pper_handle_data): globalalloc (gptr, sizeof (pper_handle_data ));
If (pperhandle = NULL)
{
Break;
}

Pperhandle-> S = sremote;
Memcpy (& pperhandle-> ADDR, & saremote, nremotelen );

: Createiocompletionport (handle) pperhandle-> S, hiocp, (DWORD) pperhandle, 0 );

// Deliver an accepted request
Pper_io_data piodata = (pper_io_data): globalalloc (gptr, sizeof (pper_io_data ));
If (piodata = NULL)
{
Break;
}

Piodata-> noperationtype = op_read;
Wsabuf Buf;
Buf. Buf = piodata-> Buf;
Buf. Len = buffer_size;

DWORD dwrecv = 0;
DWORD dwflags = 0;

: Wsarecv (pperhandle-> S, & Buf, 1, & dwrecv, & dwflags, & piodata-> ol, null );

}

//////////////////////////////////////// //////////////////////////////////
Error_proc:
Wsacleanup ();
//////////////////////////////////////// //////////////////////////////////

Return 0;
}

/*************************************** ***************************
* Function Introduction: the thread that handles the completed port object event
* Input parameters:
* Output parameters:
* Return value:
**************************************** ***************************/
DWORD winapi serverthread (lpvoid lpparam)
{
Handle hiocp = (handle) lpparam;
If (hiocp = NULL)
{
Return-1;
}

DWORD dwtrans = 0;
Pper_handle_data pperhandle;
Pper_io_data pperio;

While (true)
{
// Wait for I/O to be completed on all sockets associated with the completion port
Bool Bret =: getqueuedcompletionstatus (hiocp, & dwtrans, (lpdword) & pperhandle, (lpoverlapped *) & pperio, wsa_infinite );
If (! Bret) // Error
{
: Closesocket (pperhandle-> S );
: Globalfree (pperhandle );
: Globalfree (pperio );

Cout <"error" <Endl;
Continue;
}

// The socket is disabled by the other party
If (dwtrans = 0 & (pperio-> noperationtype = op_read | pperio-> noperationtype & nb-SP; = op_write ))
{
: Closesocket (pperhandle-> S );
: Globalfree (pperhandle );
: Globalfree (pperio );

Cout <"client closed" <Endl;
Continue;
}

Switch (pperio-> noperationtype)
{
Case op_read: // complete a request
{
Pperio-> Buf [dwtrans] = '\ 0 ';
Printf ("% s \ n", pperio-> BUF );

// Continue delivery acceptance
Wsabuf Buf;
Buf. Buf = pperio-> Buf;
Buf. Len = buffer_size;
Pperio-> noperationtype = op_read;

DWORD dwrecv = 0;
DWORD dwflags = 0;

: Wsarecv (pperhandle-> S, & Buf, 1, & dwrecv, & dwflags, & pperio-> ol, null );

}
Break;
Case op_write:
Case op_accept:
Break;

}

}

Return 0;
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.