This article briefly introduces the various socket I/O models currently supported by windows. If you find any errors, please kindly advise.
I. Select model
Ii. wsaasyncselect Model
Iii. wsaeventselect Model
4. overlapped I/O Event Notification Model
V. overlapped I/O completion routine model
Vi. iocp Model
Old Chen has a daughter who works in a field and cannot come back frequently. She and she contact him by letter. Their mail will be delivered to their mailbox by the postman.
This is very similar to the socket model. The following describes the socket I/O model by taking Lao Chen as an example ~~~
I. Select model
Chen wanted to hear from his daughter. So that he goes downstairs every 10 minutes to check whether he has a daughter's email ~~~~~
In this case, "going downstairs to check the mailbox" and going back to the upstairs has delayed Lao Chen's time, so that he cannot do other work.
The select model is very similar to the Old Chen model in this case: Check again and again... if there is data... receive/send .......
using a thread for select should be a common practice:
procedure tlistenthread. execute;
var
ADDR: tsockaddrin;
fd_read: tfdset;
Timeout: ttimeval;
asock,
mainsock: tsocket;
Len, I: integer;
begin
mainsock: = socket (af_inet, sock_stream, ipproto_tcp);
ADDR. sin_family: = af_inet;
ADDR. sin_port: = htons (5678);
ADDR. sin_addr.s_addr: = htonl (inaddr_any);
BIND (mainsock, @ ADDR, sizeof (ADDR);
listen (mainsock, 5);
While (not terminated) Do
Begin
Fd_zero (fd_read );
Fd_set (mainsock, fd_read );
Timeout. TV _sec: = 0;
Timeout. TV _usec: = 500;
If select (0, @ fd_read, nil, nil, @ timeout)> 0 then // at least one connection waiting for accept
Begin
If fd_isset (mainsock, fd_read) then
Begin
For I: = 0 to fd_read.fd_count-1 do // note, fd_count <= 64, that is, select can only manage up to 64 connections at the same time
Begin
Len: = sizeof (ADDR );
Asock: = accept (mainsock, ADDR, Len );
If asock <> invalid_socket then
... // Create a new thread for asock, and select the thread continuously
End;
End;
End;
End; // while (not self. Terminated)
Shutdown (mainsock, sd_both );
Closesocket (mainsock );
End;
Ii. wsaasyncselect Model
Later, Chen used a new Microsoft mailbox. This kind of mailbox is very advanced. Once there are new letters in the mailbox, Gates will call Chen: Hello, Lord, you have new letters! From then on, Old Chen no longer has to go up and down frequently to check the mailbox, and his teeth do not hurt. You can look at it, blue sky ...... No, Microsoft ~~~~~~~~
This is what the wsaasyncselect model provided by Microsoft means.
The wsaasyncselect model is the most easy-to-use socket I/O model in windows. When this model is used, Windows will notify the application of network events as messages.Program.
First, define a message identifier constant:
Const wm_socket = wm_user + 55;
Then add a function statement to process the message in the private field of the primary form:
Private
Procedure wmsocket (var msg: tmessage); message wm_socket;
Then you can use wsaasyncselect:
VaR
ADDR: tsockaddr;
Sock: tsocket;
Sock: = socket (af_inet, sock_stream, ipproto_tcp );
ADDR. sin_family: = af_inet;
ADDR. sin_port: = htons (5678 );
ADDR. sin_addr.s_addr: = htonl (inaddr_any );
BIND (m_sock, @ ADDR, sizeof (sockaddr ));
Wsaasyncselect (m_sock, handle, wm_socket, fd_accept or fd_close );
Listen (m_sock, 5 );
....
The application can analyze the received wm_socket message to determine which socket generates a network event and the event type:
Procedure tfmmain. wmsocket (var msg: tmessage );
VaR
Sock: tsocket;
ADDR: tsockaddrin;
Addrlen: integer;
Buf: array [0 .. 4095] of char;
Begin
// MSG wparam is the socket handle that generates network events, and lparam contains the Event Type
Case wsagetselectevent (msg. lparam)
Fd_accept:
Begin
Addrlen: = sizeof (ADDR );
Sock: = accept (msg. wparam, ADDR, addrlen );
If sock <> invalid_socket then
Wsaasyncselect (sock, handle, wm_socket, fd_read or fd_write or fd_close );
End;
Fd_close: closesocket (msg. wparam );
Fd_read: Recv (msg. wparam, Buf [0], 4096, 0 );
Fd_write :;
End;
End;
Iii. wsaeventselect Model
Later, Microsoft's mailbox became very popular. The number of people who bought Microsoft Mail was counted as one million ...... as a result, Gates calls the customer 24 hours a day, causing a sore waist and backache ~~~~~~
Microsoft improved their mailbox: by adding an additional device to the customer's home, the device monitors the customer's mailbox, whenever new mail comes, this device will send a "new letter arrives" to remind old Chen to receive the mail. Gates can finally go to bed.
Use the same thread:
Procedure tlistenthread. Execute;
VaR
Hevent: wsaevent;
RET: integer;
Ne: twsanetworkevents;
Sock: tsocket;
ADR: tsockaddrin;
Smsg: string;
Index,
Eventtotal: DWORD;
Eventarray: array [0 .. WSA_MAXIMUM_WAIT_EVENTS-1] of wsaevent;
Begin
... Socket... bind...
Hevent: = wsacreateevent ();
Wsaeventselect (listensock, hevent, fd_accept or fd_close );
... Listen...
While (not terminated) Do
Begin
Index: = wsawaitformultipleevents (eventtotal, @ eventarray [0], false, wsa_infinite, false );
Fillchar (ne, sizeof (NE), 0 );
Wsaenumnetworkevents (sockarray [Index-WSA_WAIT_EVENT_0], eventarray [Index-WSA_WAIT_EVENT_0], @ ne );
If (ne. lnetworkevents and fd_accept)> 0 then
Begin
If ne. ierrorcode [fd_accept_bit] <> 0 then
Continue;
RET: = sizeof (ADR );
Sock: = accept (sockarray [Index-WSA_WAIT_EVENT_0], ADR, RET );
If eventtotal> WSA_MAXIMUM_WAIT_EVENTS-1 then // here wsa_maximum_wait_events is also 64
Begin
Closesocket (sock );
Continue;
End;
Hevent: = wsacreateevent ();
Wsaeventselect (sock, hevent, fd_read or fd_write or fd_close );
Sockarray [eventtotal]: = sock;
Eventarray [eventtotal]: = hevent;
INC (eventtotal );
End;
If (ne. lnetworkevents and fd_read)> 0 then
Begin
If ne. ierrorcode [fd_read_bit] <> 0 then
Continue;
Fillchar (recvbuf [0], pack_size_receive, 0 );
RET: = Recv (sockarray [Index-WSA_WAIT_EVENT_0], recvbuf [0], pack_size_receive, 0 );
......
End;
End;
End;
4. overlapped I/O Event Notification Model
Later, Microsoft found through a survey that Chen did not like sending and receiving letters from the upper and lower floors, because the upper and lower floors were a waste of time. So Microsoft improved their mailbox again. The new mailbox uses more advanced technologies. As long as the user tells Microsoft the number of its home on the floor, the new mailbox will send the mail directly to the user's home and then tell the user, your letter has been placed in your home! Old Chen is very happy because he no longer needs to send and receive emails in person!
The overlapped I/o Event Notification model is very similar to the wsaeventselect model in implementation. The main difference is "overlapped". The overlapped model allows applications to use the overlapping data structure (wsaoverlapped ), one or more Winsock I/O requests are shipped at a time. After these submitted requests are completed, the application will receive a notification. What does it mean? That is to say, if you want to receive data from a socket, you only need to tell the system that the system receives data for you, and all you need to do is provide a buffer zone for the system ~~~~~
The listen thread and the wsaeventselect model are identical, while the Recv/send thread is completely different:
Procedure toverlapthread. Execute;
VaR
Dwtemp: DWORD;
RET: integer;
Index: DWORD;
Begin
......
While (not terminated) Do
Begin
Index: = wsawaitformultipleevents (flinks. Count, @ flinks. events [0], false, recv_time_out, false );
Dec (index, wsa_wait_event_0 );
If index> WSA_MAXIMUM_WAIT_EVENTS-1 then // timeout or other errors
Continue;
Wsaresetevent (flinks. events [Index]);
Wsagetoverlappedresult (flinks. Sockets [Index], flinks. poverlaps [Index], @ dwtemp, false, flinks. pdwflags [Index] ^ );
If dwtemp = 0 then // The connection is closed.
Begin
......
Continue;
End else
Begin
Fmmain. listbox1.items. Add (flinks. pbufs [Index] ^. BUF );
End;
// Initialize the buffer
Flinks. pdwflags [Index] ^: = 0;
Fillchar (flinks. poverlaps [Index] ^, sizeof (wsaoverlapped), 0 );
Flinks. poverlaps [Index] ^. hevent: = flinks. events [Index];
Fillchar (flinks. pbufs [Index] ^. Buf ^, buffer_size, 0 );
// Send a request to receive data
Wsarecv (flinks. sockets [Index], flinks. pbufs [Index], 1, flinks. pdwrecvd [Index] ^, flinks. pdwflags [Index] ^, flinks. poverlaps [Index], nil );
End;
End;
V. overlapped I/O completion routine model
After Chen receives a new letter, the general procedure is: open the envelope, pull out the letter paper, read the letter, and reply to the letter ...... to further reduce the burden on users, Microsoft has developed a new technology: as long as you tell Microsoft the operation steps on the mail, Microsoft Mail will follow these steps to process the mail, you no longer need to personally split/read/reply! Chen has finally lived a petty asset!
Overlapped I/O completion routine requires the user to provide a callback function. When a new network event occurs, the system will execute this function:
Procedure workerroutine (const dwerror, cbtransferred: DWORD; const
Lpoverlapped: lpwsaoverlapped; const dwflags: DWORD); stdcall;
Then the system is told to use the workerroutine function to process the received data:
Wsarecv (m_socket, @ fbuf, 1, dwtemp, dwflag, @ m_overlap, workerroutine );
Then there is nothing, and the system has done everything for you! Microsoft is considerate!
While (not terminated) Do // This is what the Recv/send thread is going to do... nothing to do !!!
Begin
If sleepex (recv_time_out, true) = wait_io_completion then //
Begin
;
End else
Begin
Continue;
End;
End;
Vi. iocp Model
Microsoft mailbox seems perfect, and Chen is also very satisfied. But in some big companies, the situation is completely different! These large companies have tens of thousands of mailboxes, and hundreds of mails need to be processed every second, so that Microsoft mailboxes often crash due to overload! Need to restart! Microsoft had to launch a killer ......
Microsoft assigned a super robot named "completion port" to every big company to process those letters!
"The Windows NT team noticed that the performance of these applications was not as high as expected. In particular, processing many concurrent client requests means that they run in the system in multiple threads concurrently. Because all these threads are runable [not suspended or waiting for something to happen], Microsoft realizes that the NT kernel spends too much time converting the context of the running thread [Context], threads do not get much CPU time to do their work. You may also feel that the bottleneck of the parallel model is that it creates a new thread for each customer request. Creating a thread has a lower overhead than creating a process, but it is far from having no overhead. Let's imagine that if n threads are enabled in advance to hold [blocking] them there, all user requests can be delivered to a message queue. Then the n threads extract messages from the Message Queue one by one and process them. You can avoid opening threads for every user request. This not only reduces thread resources, but also improves thread utilization. Theoretically, it is quite good. How can Microsoft not take it into consideration when I come up with questions that can be raised by general users? "----- From nonocast's understanding I/O completion port
Let's take a look at the implementation of the iocp model:
// Create a complete port
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, nil, 0 );
// Number of CPUs created * 2 + 2 threads
For I: = 1 to Si. dwnumberofprocessors * 2 + 2 do
Begin
Athread: = trecvsendthread. Create (false );
Athread. completport: = fcompletport; // tell this thread that you are going to access data through this iocp.
End;
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:
Procedure trecvsendthread. Execute;
VaR
......
Begin
While (not self. Terminated) Do
Begin
// Query the iocp status (whether the data read/write operation is complete)
Getqueuedcompletionstatus (completport, bytestransd, completkey, poverlapped (pperiodat), time_out );
If bytestransd <> 0 then
...; // Data read/write operation completed
// Send another read data request
Wsarecv (completkey, @ (pperiodat ^. bufdata), 1, bytesrecv, flags, @ (pperiodat ^. overlap), nil );
End;
End;
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.