메뉴 건너뛰기

공사박사

CONSTRUCTION
& with PLEASURE.

즐거운 공사를 위해 공사박사가 함께 합니다.

SATISFACATION
TOGETHER

공급자&수요자 모두가 만족합니다.

OVER 10 YEARS
MASTERS

10년 이상의 장인들과 함께 합니다.

ONE MORE
VISIT & MEET

한번 더 방문해서 만나고 싶은 인연을 만들어갑니다.

?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
?

단축키

Prev이전 문서

Next다음 문서

크게 작게 위로 아래로 댓글로 가기 인쇄
Extra Form
모집종류 antoniettacerda@gmail.com
작업종류 21,3685,46265
\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [ And in order to make friends more intuitive experience to complete the use of the port, this article comes with a detailed comment on the use of MFC prepared by the graphical interface sample code.

pc sync via usb meansmy original intention is to write a copy of the Internet can find the most complete on the completion of the port teaching documents, and let the Socket programming a little understanding of the people can understand, can learn how to come Use the port to complete such an excellent network programming model, but because of my level of limited, do not know whether my original intention to achieve, but still hope that you need friends can like.

Related Reading----

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 1) Hand to teach you Fun SOCKET Model: Overlap I / O

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 2) Hand in hand to teach you Fun SOCKET model: Completion routine (Completion

Routine

Because of space reasons, this article assumes that you are already familiar with the basic principles of using TCP for TCP / IP programming, and also master the multi-threaded programming technology, too basic concept I do not mention here, and online information should be everywhere.

This document embodies the author's efforts, such as to reprint, please specify the original author and source, thank you!.... But the code is not copyright, you can easily spread the use of welcome to improve, especially very welcome to help me find Bug friends to better benefit everyone. ^ _ ^

the article length is very long, basically related to the completion of all aspects related to the port, one can not see the points can be divided several times, the middle of attention to rest, the rest of the time, Good body is the biggest cost of our programmer!

and I have forgotten to ask, because my level is limited, although I have repeatedly amended several times, but the article and the sample code, there are certainly I did not find the error and Flaw, I hope you must point out, Paizhuan, spray me, I can Hold live, but must point out that I will be amended in time, because I do not want the text of the error spread throughout the Internet, scourge everyone.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; OK, Let's go! Have fun!

table of Contents:

1. Complete the advantages of the port

2. Complete the demo of the port program

3. Complete the port related concepts

4. Complete the basic flow of the port

5. Complete the use of the port

6. Practical application should pay attention to the place

one. Complete the advantages of the port

I want to write or want to write C / S mode network server side of the friends, should be more or less heard the completion of the port name it, I would like to write, Completion of the port will take full advantage of the Windows kernel for I / O scheduling, is used for C / S communication mode, the best performance of the network communication model, no one; and even its performance close to the communication model are not.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 2. What is the difference between completing port and other network communication?

(1) First of all, if the use of 'synchronous' way to communicate, then the way to say that the synchronization is that all operations are completed in a thread in the order of completion, The shortcomings of this is obvious: because the synchronization of communication operations will block from the same thread of any other operation, only after the completion of this operation, the follow-up operation can be completed; one of the most obvious example is that we in the MFC interface Code, the direct use of blocking Socket call code, the entire interface will be blocked because there is no response! So we have to have a communication for each Socket to establish a thread, more trouble? This is not pit father it? So to write high-performance server program, requiring communication must be asynchronous.

(2) Readers must know that you can use the 'synchronous communication (blocking communication) + multi-threaded' approach to improve (1) the situation, so good, think Now, we finally realized that the server side in each client connected, have to start a new thread and the client to communicate, how many clients, you need to start the number of threads, right; but because of These threads are running, so the system had to run between all the threads of the context of the switch, we are nothing to feel, but the CPU is painful, because the thread switch is quite a waste of CPU time, if the customer The end of the thread too much, which will make the CPU are busy to switch the thread, and there is no time to implement the thread body, so the efficiency is very low,

(3) And Microsoft proposed to complete the port model of the original intention is to solve this 'one-thread-per-client' shortcomings, it makes full use of the kernel object Of the scheduling, only the use of a small number of threads to deal with all the client and the communication, eliminating the unnecessary thread context switch, the maximum increase in the performance of network communications, this magical effect is how to achieve the following see below.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 3. Completion of the port is widely used in various high-performance server programs, such as the famous Apache ... If you want to write the server side need to deal with concurrent clients There are hundreds of thousands of connections, then it is not tangled, that is it.

two. Complete the demo of the port program

First of all, we first look at the completion of the port in the author's PC running performance, the author's PC configuration is as follows: First,

In general, i7 2600 + 16GB memory, I take this PC as a server, simply carried out the following test, through the Client generated 30,000 concurrent threads connected to the server at the same time.. , And then each thread every 3 seconds to send a data, sent a total of 3 times, and then observe the server-side CPU and memory occupancy.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; As shown in Figure 2, is the client 30,000 concurrent threads send a total of 90,000 data log

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Figure 3 is a log view of the server after receiving 3 million concurrent threads and 3 threads of each thread

The most critical is Figure 4, Figure 4 is the server side to receive 28000 concurrent threads, the CPU occupancy rate screenshot, the use of the software is the famous Process Explorer, Because it is relatively accurate and accurate than the built-in task manager.

we can find a surprising result, using the port to complete the Server program (blue horizontal line shown) occupied by the CPU was 3.82%, and then, The whole process of running the peak is not more than 4%, is quite leisurely ... ... Oh, yes, this is still running in the Debug environment, if the use of Release mode, the performance will certainly be higher, in addition to In addition, the display of information on the UI is also a big impact on the performance of Chengdu.

Contrary to the use of a number of concurrent threads Client program (purple horizontal line shown) actually occupied the CPU up to 11.53%, even more than the Server program several times ... ...

In fact, no matter what kind of network operation model, for the memory footprint are similar, the real difference is that the CPU occupancy, other network models need more Of the CPU power to support the same connection data.

Although this is far from the server limit stress test, but also can be seen from the completion of the strength of the port, and this way than purely by the multi-threaded approach.... nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; To achieve concurrent resource occupancy rate is much lower.

three. Complete the port related concepts

Before we start coding, let's talk about some of the concepts related to port completion. If you do not have the patience to read the text of this section, you can also do this. Skip this section to go directly to the next section of the specific implementation of the part, but this section involves the basic concept you still need to understand, and you also know why there are so many network programming mode , Have to use such a complex and difficult to understand the completion of the port? The Will also be firm you continue to learn the confidence of ^ _ ^

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 3.1 Asynchronous communication mechanism and several implementations

We learned from the previous text that high-performance server programs use asynchronous communication mechanisms are required.

As for the concept of asynchronous, in order to facilitate the understanding of the text behind, here is still a simple description of the following:

Asynchronous communication is when we deal with external I / O devices, we all know that the I / O of the external device and the CPU is just a turtle speed. , Such as hard disk read and write, network communications, etc., we do not need to wait for our own thread inside the I / O operation to complete the follow-up code, but the request to the device driver to handle their own Threads can continue to do other more important things, the general process as shown below:

I can see from the figure a very obvious parallel operation process, and 'synchronization' of the communication is in the network operation, the main thread to hang , The main thread to wait for the completion of network operations in order to continue the implementation of the follow-up code, that is to end the implementation of the main thread, to the end of the implementation of network operations, is not so parallel;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 quot; Asynchronous \u0026 quot; mode is undoubtedly more efficient than \u0026 quot; blocking mode \u0026 quot; multithreading \u0026 quot; mode, which is why the former is called \u0026 quot; asynchronous \u0026 quot; 'Because the need to wait for network operations to complete and then perform other operations.

There are several ways to implement asynchronous mechanisms in Windows, and the difference between them is that the last step in Figure 1 'Notifies the application of processing network data' because the operating system calls the device driver to receive Data operations are the same, the key is how to inform the application to get the data. The specific difference between them I have here to say a few points, the text a little more, if not interested in the study of friends can skip the next side of this paragraph, does not affect :)

The device kernel object uses the device kernel object to coordinate the sending of data and the reception of data coordination, that is, by setting the state of the device kernel object, the device kernel object, the device kernel object, Device to receive data is completed, immediately trigger the kernel object, and then let the data received the thread received a notice, but this way too primitive, the thread to receive data in order to know whether the kernel object is triggered, or kept hanging Wait, it is simply no use Well, too low, there are wood there? So here is not mentioned, and if readers do not understand how the matter do not have to get to the bottom, in short, no use.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (2) \u0026 nbsp; event kernel object, using the event kernel object to achieve I / O operation to complete the notification, in fact, this way is actually when I wrote the article To the 'event-based overlapping I / O model', the link here, this mechanism is much more advanced, you can wait for multiple I / O operations to complete, to achieve real asynchronous, but the shortcomings are obvious, Since waiting for Event with WaitForMultipleObjects (), it will be limited by 64 Event wait caps, but this is not to say that we can only handle Socket from 64 clients, but this is a wait on a device kernel object 64% of the event kernel object, that is, we are in a thread, you can simultaneously monitor the completion of 64 overlapping I / O operations, of course, we can also use multiple threads to meet the infinite number of overlapping I / O The demand, such as if you want to support the 30,000 connections, you have to need more than 500 threads ... with too much trouble people feel uncomfortable;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (3) Use APC (Asynchronous Procedure Call) to complete, this is what I mentioned earlier in the article 'based on the completion of the overlap / O model ', the link here, the advantage of this approach is to get rid of the event notification mode based on the limit of 64 events, but there are some shortcomings, that is, the request of the thread must have to deal with their own request to receive, Even if the thread sent a lot of requests to send or receive data, but other threads are idle ..., the thread still have to deal with their own to send these requests, no one to help ... there is a load balancing problem , Obviously the performance has not been optimized.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (4) \u0026 nbsp; complete the port, needless to say that we all know, the final finale is to use the completion of the port, compared to the above several mechanisms to complete the port approach is this : In advance to open several threads, you have a few CPU I will open a few, the first is to avoid the thread of the switch, because the thread want to be implemented when there is always available CPU resources, and then let these threads waiting, Wait until the user comes to the request, put these requests are added to a public message queue to go, and then a few good threads on the line one by one from the message queue to remove the message and be processed, this way is very Elegant implementation of the asynchronous communication and load balancing problem, because it provides a mechanism to use several threads 'fair' to deal with input and output from multiple clients, and if the thread will be done when the system will be Hang, will not take the CPU cycle, quite a perfect solution, is not it? Oh, yes, this key as the exchange of the message queue is to complete the port.

After comparing, familiar with the network programming friends may ask, why not mention WSAAsyncSelect or WSAEventSelect these two asynchronous models, for these two models, I Do not know how to achieve its internal, but this one must not use the Overlapped mechanism, it can not be counted as a real asynchronous, it may be their own internal maintenance of a message queue, in short, although the two models to achieve the asynchronous But it can not be done asynchronously, and this is a clear indication of the problem, I think it is necessary to achieve the internal and complete port is very different, and complete the port is very kind, because it is the first user data received back after the Notify the user to take it directly, and WSAAsyncSelect and WSAEventSelect the flow will only receive the arrival of data notification, but only by the application itself to another recv data, the performance gap is even more obvious.

Finally, my suggestion is that if you want to use overlapping I / O based on event notifications and overlapping I / O based on the completion of the routine, if it is not particularly necessary, \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Do not use it, because these two methods not only use and understand it is not simple, but also the performance of the obvious bottleneck, why not try to use the port to do it?

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 3.2 Overlapping Structure (OVERLAPPED)

We have learned from the previous section that, to achieve asynchronous communication, we must use a very coquettish I / O data structure, called the overlapping structure 'Overlapped' and ' , Windows all the asynchronous communication is based on it, complete the port is no exception.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Why is it called Overlapped? Jeffrey Richter's explanation is because 'the time to execute the I / O request and the time at which the thread performs other tasks is overlapped', and from this name we may also see the original intention of overlapping structures, The details of my here is not much explanation, put it as and other kernel objects, do not need to get to know the mechanism, as long as you can use, and want to know more overlapping structure of the internal friends, please read Jeffrey Richter Of the 'Windows via C / C ++' 5th page 292 pages, if there is no chance, you can also just overturn my previous written Overlapped things, but written relatively simple ... ...

I want to explain that this overlapping structure is a core data structure implemented by the asynchronous communication mechanism because you see the code behind you will find that almost All of the network operations such as send / receive, etc., will be replaced with WSASend () and WSARecv (), the parameters will be accompanied by an overlapping structure, which is why? Because the overlapping structure we can understand is a network operation ID number, that is to say we have to use overlapping I / O provided by the asynchronous mechanism, then each network operation must have a unique ID number, because into the system kernel , Inside the black light Xiahuo, do not understand what the above situation, a see the overlap I / O call came in, it will use its asynchronous mechanism, and the operating system can only rely on the overlapping structure with the ID number Distinguish between which a network operation, and then inside the kernel after processing, according to the ID number, the corresponding data pass up.

If you really do not understand what this thing is, then look directly at the code behind it, and slowly understand the ... ... and so on.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 3.3 Completion Ports (CompletionPort)

For the completion of the concept of port, I do not know why its name is called 'complete port', my personal feeling should call it 'complete the queue' seems more appropriate some , In short, this 'port' and we usually say for the network communication 'port' is not a thing, we do not confuse.

First, it is called 'done' port, that is, the system will be in the network I / O operation 'completed' will not notify us, that is, we are in the Received the notice of the system, in fact, the network operation has been completed, that is, for example, when the system to inform us, not the data from the network, but from the network data has been received; or customers The end of the connection request has been completed by the system, and so on, we only need to deal with the back of the things just fine.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Will your friends be happy and what? Has been processed before we informed us, it would not be so cool? In fact, there is nothing cool, it is because we assigned to the system before the work, all asked, we will tell the system through the code 'You give me this to do that, waiting to finish and then notify me,' but these work Is done before or after the difference only.

Second, we need to know that the so-called port, in fact, and HANDLE, is also a kernel object, although Jeff Richter scare us and said: 'The completion of the port may be the most complex.' Kernel object ', but we do not have to control him, because it is how to achieve the specific internal and we have nothing to do, as long as we can learn to use it to the API to complete the framework of the port to build up on it. We only use it as a general understanding of the queue to accommodate the network communication operation just fine, it will complete the operation of the network notification, are placed in the queue inside, we only take from the queue inside the line, take a Less one ....

There are more specific details on the completion of the port kernel object. I will study in more detail with my friends in the following section on 'Completing the basic principles of the port'. Of course, , If you do not see this section in the article, that is, I was lazy did not write ... in the follow-up article I will make up. Here for so long to say so much, and that time we can see it mechanism is not so complicated, it may be only because the operating system other kernel objects in terms of comparison is too easy to achieve it ^ _ ^

four. Use the basic flow of the completed port

so many nonsense, we can not wait for it, we finally reached the time of the specific code..............

The use of port completion, it is difficult to say, but that is simple, in fact, simple - and said a nonsense =. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; =

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; In general, use the completed port only to follow the following steps:

(1) Call the CreateIoCompletionPort () function to create a complete port, and in general, we need and only need to create this one to complete the port, save its handle , We will often use it in the future ...

(2) According to the number of processors in the system, how many workers are created (for the sake of clarity, the following directly to the Worker) thread, these threads are specialized Used to communicate with the client, the current temporary no work;

(3) The following is to connect the connection to the Socket, there are two ways to achieve: First, and other programming models, but also need to start a separate thread (1) and (2) , Specifically for the client to accept the connection request; Second, the performance of higher and better asynchronous AcceptEx () request, because you should be very familiar with the use of the reuse, and online information will be a lot, so for the sake of more comprehensive, Is the performance of the better AcceptEx, as for the difference between the two code preparation, I will be more detailed next.

(4) whenever there is a client connected to the time, we still have to call CreateIoCompletionPort () function, here is not a new completion of the completion of the port, but the new!... Connected to the Socket (that is, in front of the so-called device handle), and the current completion of the port binding together.

At this point, we have already completed the deployment of the port to complete the work, ah, yes, finished, and the back of the code, we can fully enjoy the completion of the port with the tape.......... nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Give us a great advantage, sit and enjoy it, is not it simple?

(5) For example, after the client is connected, we can submit a network request on this socket, such as WSARecv (), and then the system will help us to accept the reception. Data operation, we can rest assured to do other things;

(6) At this point, we prepared in advance that several Worker threads can not be idle, we have set up in front of several Worker will be busy up, all need to be, and so on. Call the GetQueuedCompletionStatus () function separately to see if there is a network communication request (such as reading data, sending data, etc.) in the queue of the scan completion port, and, if so, retrieving the request from the queue that completed the port The implementation of the thread in the back of the processing code, after processing, we continue to submit the next network communication request on the OK, so the cycle.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; If you still do not understand, I then with a flow chart to express:

Of course, I assume that you have learned about the basic routines of network programming, so I have omitted a lot of basic details and have a better understanding of my friends in order to meet my friends. Of the code, in the flow chart I marked the name of some function, and painted very detailed.

Another need to pay attention to is because there are two ways for the client connected, one is the general blocking of the accept, the other is a better performance of AcceptEx, in order to be able to do so. Friends from other network programming in the way of transition, I painted here two ways to flow chart, to facilitate the comparison of friends learning, Figure a is the use of accept the way, of course, supporting the source code I do not provide by default, If necessary, I can also send up; Figure b is the use of AcceptEx, and with matching source.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; AcceptEx way flow diagram is as follows:

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; What is the biggest difference between the two graphs? Yes, the biggest point is the main thread of doing nothing, leisurely egg pain ... ...

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Why? Because we use the asynchronous communication mechanism, these trivial things do not need to repeat the main thread to do their own, only in the initialization time and Worker thread can be a good deal, with a word to describe is the main thread Never know how busy Worker threads, and Worker threads will never experience the main thread in the initial establishment of the communication framework when the operation of the number of heart ... ...

(a) is connected by _AcceptThread (), and the connected Socket and the completion of the port binding, the other more than _WorkerThread () It is responsible for monitoring the completion of the port on the situation, once the situation, and then take out to deal with, if the CPU has a multi-core, then you can run a number of threads to complete the port on the information, it is clear that efficiency is improved.

The most obvious difference in graph b is the biggest difference between AcceptEx and traditional accept, which is the cancellation of the blocking call, that is, AcceptEx is done through the completion of the port asynchronously, so the cancellation of the dedicated connection for the thread, with the completion of the port to carry out the asynchronous AcceptEx call; and then complete the port queue to complete the Worker function, according to the user to complete the operation Type, and then find out the delivery of the Accept request, to deal with the corresponding.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Readers will ask, where is the benefit of doing so? Why should the asynchronous delivery of the operation of AcceptEx connection?

First of all, I can tell you very clearly, if a short time the client's concurrent connection request is not particularly large, then accept and AcceptEx in terms of performance is nothing in terms of what is it? difference.

In terms of our current mainstream PC, if the client only to connect requests, and nothing to do so, our Server can only receive about 30,000 to 4 million And then the client's connection request can only receive WSAENOBUFS (10055), because the system is too late for the newly connected client to prepare resources.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; What resources need to be prepared? Of course, is prepared to Socket ... ... Although we create a Socket only SOCKET s = socket (...) such a line of code on the OK, but the system to establish a Socket is a considerable resource, because Winsock2 is a hierarchical system, Create a Socket need to be processed between multiple providers, and ultimately the formation of a socket available. In short, the system to create a Socket of the overhead is quite high, so accept the words, the system may be too late for more concurrent client site prepared Socket.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; And AcceptEx is stronger than Accept? There are three points:

(1) This benefit is the most critical, because AcceptEx is in the client before the connection, put the client Socket built up, that is to say, AcceptEx is the first to establish the Socket, and then issued the AcceptEx call, that is, in the client before the communication, whether or not there is a client connected, Socket is built in advance; and do not need to accept is in the client After joining, and then live to spend time to build Socket. If you do not know how to achieve, see the implementation of the back part.

(2) compared to accept only way to establish a link into the entrance, for a large number of concurrent client, the entrance is a bit crowded; and AcceptEx (2) You can simultaneously deliver multiple requests on the port, so that when the client is connected, it is very elegant and leisurely while drinking tea to deal with the request.

AcceptEx also has a very thoughtful advantage, that is, when the delivery of AcceptEx, we can also pass in the way at the same time, the client received the client sent to the client. The first set of data, this is done at the same time, that is, we received the notice of completion of AcceptEx, we have the first set of data is completed; but it also means that if the client is only But do not send the data, we will not receive the notice of the completion of this AcceptEx ... ... we in the back of the realization of the part, you can also see in detail.

Finally, you have a heart to prepare, compared to accept, asynchronous to the use of AcceptEx to be much more trouble ... ... and ... ...

Fives. Complete the implementation of the port

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Also said a section of nonsense, and finally to the hands of the time to achieve ... ...

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Here I am going to complete the port implementation steps and will be involved in the function, So that we can easily read, where the removal of the contents of the error handling, of course, the content in the sample code will be there.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 1] Create a complete port

First of all, we first complete the port to build again.

We normally need that we only need to build this one to complete the port, the code is very simple:

HANDLE m_hIOCompletionPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 0); \u0026 nbsp;

ya, see CreateIoCompletionPort () parameters do not strange, the parameters is an INVALID, a NULL, two 0 ..., that white is a -1, three 0 ... ... and nothing and nothing, but the Windows system is a good meal inside the busy, the completion of port-related resources and data structures have been defined (in the latter part of the principle we will see that the completion of port-related Most of the data structure is used to coordinate a variety of network I / O queue), and then the system will give us a meaningful return HANDLE, as long as the return value is not NULL, it shows the completion of the port success, it is so simple, Is not it?

sometimes I really appreciate the Windows API package, put a lot of things is actually very complicated things so simple ... ... ... ... ... ... ... ... nbsp \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

As for the specific meaning of the various parameters inside, I will put the steps in the back to say, anyway, as long as we know here to create our only complete this port, it is only need so few Parameters.

but for the last argument 0, I would like to say a few words here, this 0 is not an ordinary 0, it represents the NumberOfConcurrentThreads, that is, to allow The number of threads that the application executes at the same time. Of course, we here in order to avoid context switching, the ideal state is that each processor is only running a thread, so we set to 0, that is how many processors, allowing the number of threads running at the same time.

For example, if a machine has only two CPUs (or two cores), if the system is running more threads than the number of CPUs on the machine, it is not. What the meaning of things, because the CPU would have to switch between the implementation of multiple threads, which will waste valuable CPU cycles, but reduce the efficiency, we must bear in mind this principle.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 2] Build the corresponding Worker thread according to the number of CPU cores in the system

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 我们前面已经提到,这个Worker线程很重要,是用来具体处理网络请求、具体和客户端通信的线程,而且对于线程数量的设置很有意思,要等于系统中CPU的数量,那么我们就要首先获取系统中CPU的数量,这个是基本功,我就不多说了,代码如下:

SYSTEM_INFO si;\u0026nbsp;

GetSystemInfo(\u0026si);\u0026nbsp;

int m_nProcessors=si.dwNumberOfProcessors;\u0026nbsp;

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 这样我们根据系统中CPU的核心数量来建立对应的线程就好了,下图是在我的 i7 2600k CPU上初始化的情况,因为我的CPU是8核,一共启动了16个Worker线程,如下图所示

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 啊,等等!各位没发现什么问题么?为什么我8核的CPU却启动了16个线程?这个不是和我们第二步中说的原则自相矛盾了么?

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 哈哈,有个小秘密忘了告诉各位了,江湖上都流传着这么一个公式,就是:

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 我们最好是建立CPU核心数量*2那么多的线程,这样更可以充分利用CPU资源,因为完成端口的调度是非常智能的,比如我们的Worker线程有的时候可能会有Sleep()或者WaitForSingleObject()之类的情况,这样同一个CPU核心上的另一个线程就可以代替这个Sleep的线程执行了;因为完成端口的目标是要使得CPU满负荷的工作。

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 这里也有人说是建立 CPU"核心数量 * 2 +2"个线程,我想这个应该没有什么太大的区别,我就是按照我自己的习惯来了。

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 然后按照这个数量,来启动这么多个Worker线程就好可以了,接下来我们开始下一个步骤。

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 什么?Worker线程不会建?

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; …囧…

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; Worker线程和普通线程是一样一样一样的啊~~~,代码大致上如下:

// 根据CPU数量,建立*2的线程

\u0026nbsp; m_nThreads=2 * m_nProcessors;\u0026nbsp;

HANDLE* m_phWorkerThreads=new HANDLE[m_nThreads];\u0026nbsp;

for (int i=0; i \u0026lt; m_nThreads; i++)\u0026nbsp;

\u0026 nbsp;

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; m_phWorkerThreads[i]=::CreateThread(0, 0, _WorkerThread, …);\u0026nbsp;

\u0026 nbsp;

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 其中,_WorkerThread是Worker线程的线程函数,线程函数的具体内容我们后面再讲。

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 【第三步】创建一个用于监听的Socket,绑定到完成端口上,然后开始在指定的端口上监听连接请求

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 最重要的完成端口建立完毕了,我们就可以利用这个完成端口来进行网络通信了。

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; 首先,我们需要初始化Socket,这里和通常情况下使用Socket初始化的步骤都是一样的,大约就是如下的这么几个过程(详情参照我代码中的LoadSocketLib()和InitializeListenSocket(),这里只是挑出关键部分):

// 初始化Socket库

WSADATA wsaData;\u0026nbsp;

WSAStartup(MAKEWORD(2,2), \u0026wsaData);\u0026nbsp;

//初始化Socket

struct sockaddr_in ServerAddress;\u0026nbsp;

// 这里需要特别注意,如果要使用重叠I/O的话,这里必须要使用WSASocket来初始化Socket

// 注意里面有个WSA_FLAG_OVERLAPPED参数

SOCKET m_sockListen=WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);\u0026nbsp;

// 填充地址结构信息

ZeroMemory((char *)\u0026ServerAddress, sizeof(ServerAddress));\u0026nbsp;

ServerAddress.sin_family=AF_INET;\u0026nbsp;

// 这里可以选择绑定任何一个可用的地址,或者是自己指定的一个IP地址

//ServerAddress.sin_addr.s_addr=htonl(INADDR_ANY);\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;

ServerAddress.sin_addr.s_addr=inet_addr("你的IP");\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;

ServerAddress.sin_port=htons(11111);\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;

// 绑定端口

if (SOCKET_ERROR==bind(m_sockListen, (struct sockaddr *) \u0026ServerAddress, sizeof(ServerAddress)))\u0026nbsp;\u0026nbsp;

// 开始监听

listen(m_sockListen,SOMAXCONN))\u0026nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; need to pay attention to two places:

(1) Want to use overlapping I / O, then initialize the Socket must use WSASocket and bring WSA_FLAG_OVERLAPPED parameters can only (only in the server side need to do so, and then use the WSA / The client is not needed);

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (2) Notice the constants used behind the listen function SOMAXCONN? This is defined in Microsoft in WinSock2.h, and also comes with a note, Maximum queue length specifiable by listen., So that there is no need to white ^ _ ^

there is a very important action: since we have to use the port to help us monitor the work, then we must take this monitor Socket and complete the port binding only Can it:

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; how to bind?同样很简单,用\u0026nbsp;CreateIoCompletionPort()函数。

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; We did not think this function is familiar? Yes, this and the front of the creation of the port is actually used with the same API! But here the API is not used to build the completion of the port, but for the Socket and the previously created to complete the port binding, we have to spotted, do not be confused, because their parameters are obviously not the same The parameters in front of that is a -1, three 0, too good to remember ...

In fact, I feel that Microsoft should separate these two functions, get hold of CreateNewCompletionPort () how good is it?

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Here in a detailed explanation of CreateIoCompletionPort () several parameters:

HANDLE WINAPI CreateIoCompletionPort(\u0026nbsp;

\u0026nbsp;\u0026nbsp;\u0026nbsp; __in\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; HANDLE\u0026nbsp; FileHandle,\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; // 这里当然是连入的这个套接字句柄了

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; __in_opt\u0026nbsp; HANDLE\u0026nbsp; ExistingCompletionPort, // 这个就是前面创建的那个完成端口

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; __in\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; ULONG_PTR CompletionKey,\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; // 这个参数就是类似于线程参数一样,在

// 绑定的时候把自己定义的结构体指针传递

// 这样到了Worker线程中,也可以使用这个

// 结构体的数据了,相当于参数的传递

\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; __in\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; DWORD NumberOfConcurrentThreads // 这里同样置0

);\u0026nbsp;

Now these parameters are nothing to talk about it, use at a glance.............. And for that of the CompletionKey, we will be mentioned later.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

After initializing Socket, you can post the AcceptEx request on this Socket. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 4] Submit the AcceptEx request on this monitor Socket

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; The handling here is more complicated.

This is a special exception, and this is Microsoft specifically in the Windows operating system which provides the extended function, which means that this is not provided within the Winsock2 standard, Microsoft is to facilitate our us, and so on............ The use of overlapping I / O mechanism, the addition of some of the functions, so before you still need to do some preparatory work.

The implementation of Microsoft is provided through mswsock.dll, so we can use AcceptEx by statically linking mswsock.lib. But this is a non-recommended way, we should use WSAIoctl with SIO_GET_EXTENSION_FUNCTION_POINTER parameter to get the function pointer, and then call AcceptEx.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Why is this?因为我们在未取得函数指针的情况下就调用AcceptEx的开销是很大的,因为AcceptEx 实际上是存在于Winsock2结构体系之外的(因为是微软另外提供的),所以如果我们直接调用AcceptEx的话,首先我们的代码就只能在微软的平台上用了,没有办法在其他平台上调用到该平台提供的AcceptEx version (if any), and even worse, each time we call AcceptEx, Service Provider has to pass the WSAIoctl () once the function pointer, the efficiency is too low, so not as we ourselves Code to go directly to get a good look.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; The code to get the AcceptEx function pointer is as follows:

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // AcceptEx Function Pointer \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; GUID GuidAcceptEx = WSAID_ACCEPTEX; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // GUID, this is required to recognize the AcceptEx function

DWORD dwBytes = 0; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

WSAIoctl (\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; m_pListenContext- \u0026 gt; m_Socket, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; SIO_GET_EXTENSION_FUNCTION_POINTER, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 GuidAcceptEx, \u0026 nbsp; \u0026 nbsp;

sizeof (GuidAcceptEx), \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 m_lpfnAcceptEx, \u0026 nbsp; \u0026 nbsp;

sizeof (m_lpfnAcceptEx), \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 dwBytes, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; NULL, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; NULL); \u0026 nbsp;

There is nothing to say about the specific implementation, because they are fixed routines, that GUID is Microsoft to the definition of good, take it directly on the line, WSAIoctl () is the Through this to find the address of AcceptEx, the other need to pay attention to is, through the WSAIoctl getEx function pointer, only need to casually passed to WSAIoctl () a valid SOCKET can, the type of Socket will not affect the access to the AcceptEx function pointer.

and then we can call the AcceptEx function through the pointer m_lpfnAcceptEx in it. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; AcceptEx function is defined as follows:

BOOL AcceptEx (\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; SOCKET sListenSocket, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; SOCKET sAcceptSocket, \u0026 nbsp; \u0026 nbsp;

PVOID lpOutputBuffer, \u0026 nbsp; \u0026 nbsp;

DWORD dwReceiveDataLength, \u0026 nbsp; \u0026 nbsp;

DWORD dwLocalAddressLength, \u0026 nbsp; \u0026 nbsp;

DWORD dwRemoteAddressLength, \u0026 nbsp; \u0026 nbsp;

LPDWORD lpdwBytesReceived, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; LPOVERLAPPED lpOverlapped \u0026 nbsp; \u0026 nbsp;

); \u0026 nbsp;

At first glance a lot of parameters, but the actual use is also very simple:

Parameter 1 - sListenSocket, this is the only used to monitor the Socket, and nothing to say;

Parameter 2 - sAcceptSocket, used to accept the connection of the socket, this is what we need to build in advance, and so have a client connected directly to the Socket that it is used, is the key to the high performance of AcceptEx.

Parameter 3 - lpOutputBuffer, receive buffer, which is AcceptEx more distinctive place, since AcceptEx is not an ordinary accpet function, then the buffer is not an ordinary buffer, the buffer contains three messages: First, customers The first group of data sent, the second is the address of the server, the third is the client address, are the essence of ah ... but read it will be very troublesome, but behind a better solution.

Parameter 4 - dwReceiveDataLength, in front of the parameter lpOutputBuffer used to store the data space size. If this parameter = 0, then Accept will not wait for the data to come, and directly return, if this parameter is not 0, then have to wait to receive the data will return ... ... so usually when Accept Received data, it Need to set the parameters: sizeof (lpOutputBuffer) - 2 * (sizeof sockaddr_in +16), which means that the total length minus the length of the two address space is, and it seems complicated, in fact, want to understand also nothing ...

Parameter 5 - dwLocalAddressLength, the size of the space where the address information is stored;

Parameter 6 - dwRemoteAddressLength, the size of the space where the remote address information is stored;

Parameter 7 - lpdwBytesReceived, out parameters, useless for us, no control;

Parameters 8 - lpOverlapped, the overlapped I / O overlap structure to be used.

This parameter is nothing, it seems complicated, but we can still pass one by one, and then in the corresponding IO operation is completed, these parameters will naturally be the Windows kernel will be.... nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Help us fill up.

but it is very asynchronous, we are asynchronous operation, we are in the thread to start the place of this operation, so we see these variables once again, it is on, Is already inside the Worker thread, because Windows will directly pass the results of the operation passed to the Worker thread, so that we put in the start of so many IO requests, which came back from the Worker thread these results, in the end is the corresponding Which IO request? The The The The

you must think of it, yes, the Windows kernel also helped us think: with a sign to bind each IO operation, so to the time inside the Worker thread , Received a complete operation of the network after the notice, and then through this logo to find out which group of data in the end corresponds to which Io operation.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; The logo here is the following structure:

typedef struct _PER_IO_CONTEXT \u0026 nbsp;

\u0026 nbsp; OVERLAPPED \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // Every overlap I / \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; The connection is the same

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Area, used to pass the parameters of the overlap operation, WSABUF later will speak

char \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; m_szBuffer [MAX_BUFFER_LEN]; // corresponds to buffer in WSABUF

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // Flag This overlap I / O operation is what to do, Such as Accept / Recv

PER_IO_CONTEXT, * PPER_IO_CONTEXT; \u0026 nbsp;

(of course, OVERLAPPED that ... ...)........................................................

But AcceptEx is not an ordinary accept, buffer is not an ordinary buffer, then this structure of course, can not be an ordinary structure of the ... ... but ... ...

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; That is to say that each overlapping I / O should correspond to such a set of parameters, as to how the definition of this structure does not matter, and this structure is not necessarily to be defined, but not it ... ... really is not, we can put it Understood as a thread parameter, it is like when you use the thread, the thread parameters are not necessary, but not pass really is not ... ...

In addition to this, we also think that since each I / O operation has a corresponding PER_IO_CONTEXT structure, and in each of the Socket, we will deliver more I / O requests, for example, we can be in the monitor Socket on the delivery of multiple AcceptEx request, so the same, we also need a 'single handle data' to manage all the I / O request on the handle, where the ' Handle 'of course refers to the Socket, and I in the code is so defined:

\u0026 nbsp;

typedef struct _PER_SOCKET_CONTEXT \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // Every client connected Socket

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; m_ClientAddr; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // The address of this client

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // array, all client IO operation parameters,

// that is for each client Socket

// You can send multiple IO requests at the same time

PER_SOCKET_CONTEXT, * PPER_SOCKET_CONTEXT; \u0026 nbsp;

This is also a better understanding, that is, we need to handle in a Socket, the management of the Socket in the delivery of each IO request _PER_IO_CONTEXT............

Of course, the same, you can also according to their own ideas to easily define, as long as they can play to manage each IO request on the need to pass the network parameters of the network parameters......... The purpose is enough, the key is to track the status of these parameters, when necessary to release these resources, do not cause memory leaks, because the server always need to run for a long time, so if there is memory leak that is very terrible , Must put an end to a shred of memory leak.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

This is the preparation of all of us, the specific realization of each of them can cooperate with my flow chart to look at the sample code, I believe we should understand it faster.....................

The work of the port initialization is more complicated than the other models, so that for the main thread, it always feels that he has paid a lot, always feel that Worker thread is to enjoy it, but Worker own bitter only their own understand, Worker thread work is not less than the main thread, but also more complex, and the specific communication work is all Worker thread to complete, Worker thread but also Think the main thread is next to watch the fun, only know that the command order only, but after all, who is still who can not do without who, and that the boss and the staff of the company's subtle relationship is the same ...

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 5] Let's take a look at what Worker threads do

The work of the Worker thread is related to the specific communication issues, mainly to complete the following work, let us step by step look at the work.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (1) Use GetQueuedCompletionStatus () to monitor the completion port

First of all, this work to do the work we can guess, nothing more than a few Worker thread brother together with a few teams to monitor the completion of the port queue whether there is a queue to complete the work. Complete the network operation just fine, the code as follows:

\u0026 nbsp;

void * lpContext = NULL; \u0026 nbsp;

OVERLAPPED \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; * pOverlapped = NULL; \u0026 nbsp;

DWORD \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; dwBytesTransfered = 0; \u0026 nbsp;

BOOL bReturn \u0026 nbsp; = \u0026 nbsp; GetQueuedCompletionStatus (\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; pIOCPModel- \u0026 gt; m_hIOCompletionPort, \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 dwBytesTransfered, \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (LPDWORD) \u0026 lpContext, \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 pOverlapped, \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; INFINITE); \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Are you aware of the GetQueuedCompletionStatus () function? This is the first part of the Worker thread is the most important thing, and the role of this function is that I mentioned earlier, will let the Worker thread into the CPU does not take the sleep state, until the completion of the need to deal with the port Network operation or beyond the waiting time limit.

Once the completed I / O requests have been completed on the port, the waiting thread is immediately woken up and then continues with the subsequent code. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; As for this magical function, the prototype is like this:

BOOL WINAPI GetQueuedCompletionStatus (\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // This is the only completion port we have created

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __out \u0026 nbsp; LPDWORD \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; lpNumberOfBytes, \u0026 nbsp; \u0026 nbsp; // This is the number of bytes returned after the operation is completed

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __out \u0026 nbsp; PULONG_PTR \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; lpCompletionKey, \u0026 nbsp; \u0026 nbsp; // This is the custom structure parameter that we bind when we set up the port

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __out \u0026 nbsp; LPOVERLAPPED \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; * lpOverlapped, \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // This is the overlapping structure we built with Socket

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; dwMilliseconds \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // wait for the port to time out if the thread does not Need to do other things, then INFINITE on the line

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp;); \u0026 nbsp;

Therefore, if the function suddenly returns, it means that there is a need to deal with the network operation --- of course, in the absence of errors in the case..........

and then switch (), depending on the type of operation required to deal with, then we have to deal with the corresponding.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; But how do you know what type of operation This requires the use of the loContext parameters passed from the outside, that is, we encapsulate the parameter structure, which will contain the structure of the beginning of this operation we set the operation of the type of operation, and then we follow this operation again The corresponding processing.

but there are problems, this parameter is where it came from it?........... What are the contents of the time?

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; This question is ok!

First, we need to know two key points: first and foremost,

(1) This parameter is when you bind Socket to a complete port, use the CreateIoCompletionPort () function, the incoming CompletionKey parameter, if forgotten............ , Then turn on the document 'third step' to see the relevant content; we are here is the definition of the PER_SOCKET_CONTEXT, that is, 'single handle data', because we are bound to a Socket, where naturally also You need to pass in the Socket-related context, how do you pass the past, what will be received here, that is to say that the lpCompletionKey is our PER_SOCKET_CONTEXT, directly to the inside of the data can be used.

(2) There is also a very magical place, inside the lpOverlapped parameter, which comes with our PER_IO_CONTEXT................ Where did this parameter come from? We go to see in front of the implementation of AcceptEx request, is not passed an overlapping parameters into? Here is it, and we can use a very magical macro, and it is stored in the other variables, all read out, for example:

PER_IO_CONTEXT * pIoContext = CONTAINING_RECORD (lpOverlapped, PER_IO_CONTEXT, m_Overlapped); \u0026 nbsp;

The meaning of this macro is to pass in the lpOverlapped variable to find the data associated with the m_Overlapped member of PER_IO_CONTEXT in the structure. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; you think about it, really amazing!

However, to achieve this magical effect, we should ensure that we define the Overlapped variable as the first one in the structure when the structure is defined by the structure PER_IO_CONTEXT. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; member.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

Since we can get the PER_IO_CONTEXT structure, then we can naturally according to the m_OpType parameter, that this received the completion of the notice, is about which Socket (s), and so on. On which the I / O operation, so that the corresponding deal with the appropriate.

In my example code, when the AcceptEx request is completed, I am the _DoAccept () function that is executed, and when the WSARecv request is completed, the execution of the request is \u0026 lt; RTI ID = 0.0 \u0026 gt; \u0026 quot; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; _DoRecv () function, I will explain the following two functions of the implementation process.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 6] When receiving the Accept notification _DoAccept ()

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Accept the efficiency of the accept () is not willing to use the reasons for AcceptEx it

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (1) assign a Socket for this newly connected connection;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (2) Submit the first asynchronous send / receive request on this Socket;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (3) continue to monitor.

In fact, some are very simple things but because of the 'single handle data' and 'single IO data' to join, things become more chaotic............. Because it is so, let us together with a ray of ah, it is best to look with the code together, or too abstract ... ...

(1) First, the _Worker thread will receive an lpCompletionKey in GetQueuedCompletionStatus (), which is PER_SOCKET_CONTEXT, which holds the I / O-related Socket and Overlapped (s) associated with this I / O (1), \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; There are the first set of data sent by the client, and so on, right? But here have to pay attention to the SOCKET the context of the data is on the monitor Socket, rather than the new connection of the client Socket, do not mix ... ...

(2) So, AcceptEx is not to our new connection of the Socket has long been built a Socket? So here we need to use this new Socket to re-create a new client for the PER_SOCKET_CONTEXT, and the following series of new PER_IO_CONTEXT, do not go to the introduction of this Listen Socket PER_SOCKET_CONTEXT, do not use the Overlapped Information, because this is the AcceptEx I / O operation, nor is it that you put the Recv I / O operation ... ... or you next time to continue to monitor the tragedy of the ... ...

(3) wait until the new Socket is ready, we quickly or with the incoming Listen Socket on the PER_SOCKET_CONTEXT and PER_IO_CONTEXT to continue to post the next AcceptEx, cycle up (in) , Stay here too dangerous, sooner or later have to be changed ... ...

(4) and our new Socket context data and I / O operation data are ready, we have to do two things: one thing is to put this one. The new Socket and our only one to complete the port binding, this would not have to elaborate, and the front binding monitor Socket is the same; then is in this Socket on the first I / O operation request, in my example The code is delivered with WSARecv (). Because the follow-up WSARecv, it is not delivered here, and here only responsible for the first request.

However, as to how the WSARecv request is to be delivered, we go to the next section. In this section, we have a very important thing, and I have to give We mention that the client is connected to the time, how do we get the client into the address information.

Now we need to introduce another very high-end function, GetAcceptExSockAddrs (), which, like AcceptEx (), is an extension function provided by Microsoft, so it is also necessary to do so. Through the following way to import can use ... ...

WSAIoctl (\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; m_pListenContext- \u0026 gt; m_Socket, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; SIO_GET_EXTENSION_FUNCTION_POINTER, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 GuidGetAcceptExSockAddrs, \u0026 nbsp;

sizeof (GuidGetAcceptExSockAddrs), \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 m_lpfnGetAcceptExSockAddrs, \u0026 nbsp; \u0026 nbsp;

sizeof (m_lpfnGetAcceptExSockAddrs), \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 dwBytes, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; NULL, \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; NULL); \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Like ExportExExport, it is also necessary to use its GUID to get the corresponding function pointer m_lpfnGetAcceptExSockAddrs.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; say so much, why is this function used? It is worthy of the name 'Friends of AcceptEx', why do you say so? Because I mentioned in front of AcceptEx has a very magical function, that is, with a magical buffer, the buffer powerful, including the client sent the first set of data, the local address information, the client's address information, three Unity, ah, you say magic is not magic?

This function can also be seen from its literal meaning, that is used to decode the buffer, yes, it does not provide any other function, that is, Specifically used to resolve the contents of the AcceptEx buffer. For example, the following code:

PER_IO_CONTEXT * pIoContext = I / O Context for this communication \u0026 nbsp;

SOCKADDR_IN * ClientAddr = NULL; \u0026 nbsp;

SOCKADDR_IN * LocalAddr = NULL; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

int remoteLen = sizeof (SOCKADDR_IN), localLen = sizeof (SOCKADDR_IN); \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

m_lpfnGetAcceptExSockAddrs (pIoContext- \u0026 gt; m_wsaBuf.buf, pIoContext- \u0026 gt; m_wsaBuf.len - ((sizeof (SOCKADDR_IN) +16) * 2), \u0026 size size (SOCKADDR_IN) +16, sizeof (SOCKADDR_IN) +16, (LPSOCKADDR * ) \u0026 LocalAddr, \u0026 localLen, (LPSOCKADDR *) \u0026 ClientAddr, \u0026 remoteLen); \u0026 nbsp;

After decoding, we can get a lot of interesting address information from the following structure pointers: \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

inet_ntoa (ClientAddr- \u0026 gt; sin_addr) is the client IP address

ntohs (ClientAddr- \u0026 gt; sin_port) is the port that the client is connected to

inet_ntoa (LocalAddr -> sin_addr) is the local IP address

ntohs (LocalAddr -> sin_port) is the local communication port

pIoContext- \u0026 gt; m_wsaBuf.buf is the buffer that stores the first set of data sent by the client

Since the use of 'Friends of AcceptEx', everything is clean ....

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 7] When receiving a Recv notification, _DoRecv ()

In explaining how to deal with Recv requests, we'll talk about how to deliver WSARecv requests first.

and WSARecv general code is as follows, in fact, his party, in the code, we can clearly see that we use a lot of new PerIoContext parameters, here again to emphasize that , Attention must be their own new ah, it must not be passed in the Worker thread that PerIoContext, because that is listening to the Socket, do not give people broken ... ...:

int nBytesRecv = WSARecv (pIoContext- \u0026 gt; m_Socket, pIoContext - \u0026 gt; p_wbuf, 1, \u0026 dwBytes, 0, pIoContext- \u0026 gt; p_ol, NULL)

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Here, I'll tell you about the prototype WSARev function again

int WSARecv (\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // of course is the socket that delivers this operation

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

// Here you need an array of WSABUF constructs

DWORD dwBufferCount, \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // The number of WSABUF structures in the array is set to 1

LPDWORD lpNumberOfBytesRecvd, \u0026 nbsp; // If the receive operation completes immediately, it will return the number of bytes received by the function call

LPDWORD lpFlags, \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; //

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; LPWSAOVERLAPPED lpOverlapped, \u0026 nbsp; // This Socket corresponds to the overlapping structure

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; // This parameter is only used to complete the routine mode,

/ / Complete the port we can set to NULL

); \u0026 nbsp;

In fact, if you are familiar with or read my previous overlapping I / O articles, should be more familiar with, just pay attention to two of the parameters :

LPWSABUF \u0026 nbsp; lpBuffers;

today is the need for our own new WSABUF structure to pass in;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; if you have to ask WSABUF structure is what stuff? I give you more than two words, that is defined in ws2def.h, defined as follows:

typedef struct _WSABUF \u0026 nbsp;

ULONG len;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __field_bcount (len) CHAR FAR * buf;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; WSABUF, FAR * LPWSABUF; \u0026 nbsp;

and kindly Microsoft also comes with a note, it is not easy .......

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; see? If you can not understand some of the strange symbols inside, do not control him, only to see a ULONG and a CHAR * on it, it is not a buffer length, one is the buffer pointer? As for what that FAR ... .. let him go to hell, now is the 32-bit and 64-bit era ... ...

In fact, the data has been received by our host, we directly through the WSABUF pointer to go The system buffer to get the data just fine, rather than those who did not use overlapping I / O model, received a notice of arrival when the data have to go to their own recv, too low-end ... ... This is why the overlap I / O is one of the reasons better than other I / O performance.

LPWSAOVERLAPPED \u0026 nbsp; lpOverlapped

This parameter is what we call the overlapping structure, and that is the definition, and then there is a Socket connection comes in, generate and initialize it, and then in the delivery of the first one. When the request is completed, it can be passed as a parameter,

OVERLAPPED * m_pol = new OVERLAPPED; \u0026 nbsp;

eroMemory (m_pol, sizeof (OVERLAPPED)); \u0026 nbsp;

After the first overlap request is completed, our OVERLAPPED structure will be allocated valid system parameters, and we need to be on every Socket. Each I / O operation type must have a unique Overlapped structure to identify.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; In fact, is to do two things:

(1) Show the data received in this buffer in WSARecv; and (1) show the data received in this buffer in WSARecv;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; (2) issue the next WSARecv ();

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Over ...

At this point, we finally took a deep breath, and completed most of the work of the port we have completed, and very grateful to you patience to see me so boring text has been to see Here is really an easy thing! The

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [Step 8] How to close the port

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

we do not have to be happy too early, although we have let our port to complete the smooth operation of the up, but in the exit when the release of resources we also want to know, Otherwise it would not fall short of ...

In the previous section, we have learned that once the Worker thread has entered the GetQueuedCompletionStatus () phase, it will go to sleep, and the INFINITE wait for the completion of the port, if Complete the port has not been completed I / O request, then these threads will not be awakened, which also means that the thread can not quit properly.

familiar with or unfamiliar with multi-threaded programming friends, should know that if the thread sleep, simple and crude put the thread off, then it will be a very Terrible things, because many threads in the body a lot of resources are too late to release, whether these resources will eventually be the operating system recovery, we as a C + + programmer, should not allow such things to appear.

so we have to have a very elegant way to let the thread exit itself. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; () Function relative to the function of this function is to allow us to manually add a complete port I / O operation, so that the state of sleep waiting for the thread will have a wake up, if for each of our Worker threads are called once PostQueuedCompletionStatus (), then all the threads will be awakened.

The prototype of the PostQueuedCompletionStatus () function is defined as follows: \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

BOOL WINAPI PostQueuedCompletionStatus (\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __in \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; HANDLE CompletionPort, \u0026 nbsp; ;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __in \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; DWORD dwNumberOfBytesTransferred, \u0026 nbsp; ;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __in \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; ULONG_PTR dwCompletionKey, \u0026 nbsp; ;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; __in_opt \u0026 nbsp; LPOVERLAPPED lpOverlapped \u0026 nbsp;

); \u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; we canSee that the parameters of this function are almost exactly the same as GetQueuedCompletionStatus (), and we need to pass in the port we have created. Then the three parameters are the number of bytes transferred, the structure parameters, and the overlapping structure.

Note that there is also a very magical thing here, under normal circumstances, GetQueuedCompletionStatus () to get back the parameters should be the system to help us fill, or in the binding to complete the port But we can now use PostQueuedCompletionStatus () directly to the back of the three parameters passed to GetQueuedCompletionStatus (), it is very convenient.

For example, in order to be able to achieve the effect of notification thread exit, we can define some of our own conventions, such as setting a special value for the following three parameters, and then the Worker thread receives the completion notification. After that, by judging whether these three parameters in the special value to determine whether it should be out of the thread.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; For example, when we call, you can do this:

for (int i = 0; i \u0026 lt; m_nThreads; i ++) \u0026 nbsp;

\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; PostQueuedCompletionStatus (m_hIOCompletionPort, 0, (DWORD) NULL, NULL); \u0026 nbsp;

\u0026 nbsp;

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; [ Receive this completion of the notice, and then judge whether this parameter is set to NULL, because under normal circumstances, this parameter will always have a non-NULL pointer incoming, if the Worker found that this parameter is set to The NULL, then the Worker thread will know that this is the application and then sent to the Worker thread exit command, so Worker thread in the internal can be their own 'elegant' out of the ... ...

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; learn?

However, there is a very obvious question here, smart friends must have thought, and only think of the people of this problem, be considered really understand this method.............

we just sent m_nThreads times, how can we ensure that every Worker thread just received one, and then all the threads are just to withdraw from it? Yes, we have no way to guarantee, so it is likely that a Worker thread to deal with a complete request, the occurrence of certain things, the results again to the next cycle to complete the completion of the request, it will cause some Worker thread no way We received an exit notification from us.

Therefore, when we exit, be sure to ensure that the Worker thread only calls GetQueuedCompletionStatus (), which requires us to find a way, and please refer to my work in the Worker thread. In the implementation of the code, I am with an exit Event, in the exit SetEvent look to ensure that the Worker thread will only call a round of GetQueuedCompletionStatus (), it should be more secure.

In addition, in the Vista / Win7 system, we also have a simpler way, we can close CloseHandle off the handle of the completion of the port, so that all in GetQueuedCompletionStatus () The thread will wake up, and return FALSE, then call GetLastError () to get the error code, it will return ERROR_INVALID_HANDLE, so that each Worker thread can be in this way easy to know that they should exit. Of course, if we can not guarantee that our application only in Vista / Win7, it is still honest PostQueuedCompletionStatus () it.

Finally, in the final stage of the release of resources, remember, because the completion of the port is also a Handle, so have to use CloseHandle this handle closed, of course, remember to use closesocket [/ url] Close a series of socket, there are other indicators of what, this is as a qualified C + + programmers basic skills, not much to say here, if there is still not clear friends, please refer to my The StopListen () and DeInitialize () functions in the sample code.

six. Complete the use of port considerations

at the end of the article, do not know that friends are basically learned to complete the use of the port, or was completed port and so many articles of my mouth tortured not work........ nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; The ...

Finally, add some not mentioned in the previous, the practical application of some of the Notes it!.............

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 1. Socket's communication buffer is set to fit?

In the x86 system, the memory page is locked in 4KB units, that is, even when you deliver WSARecv () only 1KB in size Buffer, the system still have to give you 4KB of memory. In order to avoid this waste, it is best to set the buffer for sending and receiving data directly to a multiple of 4KB.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 2. \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp;

This is not to be able to know, call GetQueuedCompletionStatus () to obtain I / O completed port request, it is certainly a first-come-first-out way to carry out.

However, we probably can not think of is to wake up those who call the GetQueuedCompletionStatus () thread is the way out before the first way to carry out...............

For example, there are four threads waiting, if there is a completed I / O items, then the last call to GetQueuedCompletionStatus () thread will be awakened.... nbsp \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Usually this order is not important, but in the order of the packet when the request, such as the transmission of large pieces of data, it is necessary to pay attention to this order.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; - The reason why Microsoft does this is certainly justified so that if only one I / O operation is repeated instead of multiple operations, the kernel only needs Wake up the same thread on it, without the need to wake up a number of threads, saving resources, and can be other long sleep thread out of memory, that resource utilization.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 3. \u0026 nbsp; if you want to transfer files ...

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Because the practice of sending documents, in accordance with the normal way of thinking, will be the first to open a file, and then continue to call ReadFile () to read a piece, and then call WSASend () to send.

But we know that, when ReadFile (), is the need for the operating system through the disk driver, to the actual physical hard disk to read the file, which will make the operation The system from the user state to the kernel state to call the driver, and then read the results back to the user state; the same reason, WSARecv () will also involve the switch from the user state to the kernel state of the problem --- so So that we have to frequently in the user state to the kernel state between the conversion, inefficient ... ...

A very good solution is to use the extended function provided by Microsoft to provide the transfer function TransmitFile () because it only needs to pass a handle to the TransmitFile () file and need to transfer it. Of the number of bytes, the program will switch to the entire kernel state, whether it is to read the data or send sync files between pc and usb thumb drive, are directly in the kernel state of the implementation of the file until the completion of the transfer to the user state to send the notification to the main process. This is much more efficient.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; 4. Questions about overlapping structure data release

Since we are using asynchronous communication, we have to get used to that, that is, we deliver out the completion of the request, do not know when we can receive the operation to complete the notice , And in this time waiting for notice, we have to pay attention to ensure that when we submit the request of the variables used in the period have to be effective.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; For example, we send the WSARecv request when using the Overlapped variable, because the operation is completed, this structure which will save a lot of very important data, for the device driver , Instructs to keep the pointer to our Overlapped variable, and after the operation is complete, the driver writes the Buffer's pointer, the number of bytes that have been transmitted, the error code, and so on to the Overlapped pointer we passed to it go with. If we have been careful to release the Overlapped, or to other operations used to the words, who knows the driver will write these things to go? Would it not crash?

I think the problem is so much now, if you really want to be eight children to write a great pressure to visit the Server, then you will slowly Found that only with my example code is not enough, you have to be in a lot of details to improve, for example, with a better data structure to manage the context of data, and the need for a very good exception handling mechanism, etc. In short, Very much looking forward to everyone's criticism and correction.

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; Thank you for seeing here! The The

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; ------ Finished in DLUT

\u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; \u0026 nbsp; ------ 2011-9-31

  1. 02Dec
    by HermelindaFef0836

    Is Very Cheap Four Jingdong Selling 32GB USB Recommended

  2. 02Dec
    by ShellieBonney9317966

    Who Can Not Fool Me! Teach You To Choose A Mobile Storage Device

  3. No Image 02Dec
    by JanellNeilsen73

    How To Install The Win7 Original Pe Mirror System Tutorial

  4. 02Dec
    by PreciousR5929976

    IPhone16G Savior Sandisk IXpand U Disk Evaluation

  5. No Image 02Dec
    by CatherineMmn90627

    Easy Wireless Transmission Flash Di 64G Mobile Phone USB Only 145 Yuan

  6. No Image 27Oct
    by GeorgiaBrookman

    Tar Package Backup Directory When The Volume Package Compression (incremental Backup, And Automatically Split Into Multiple Files)

  7. 24Oct
    by AntoniettaCerda

    Hand To Teach You Fun SOCKET Model: Complete Port (Completion Port) Detailed

  8. 24Oct
    by Gaston68A1638982076

    Lenovo Mobile Phone A850 Open The USB Debugging Connection Not On The Computer How To Do

  9. 24Oct
    by JeffreyPinnock54154

    1 Control 4 Keyboard Synchronizer (no Drive)

  10. 24Oct
    by Ruby695625436640

    Xilinx, Arm, Cadence And TSMC Jointly Announced The World's First 7-nanometer Process CCIX Test Chip

  11. No Image 24Oct
    by KeriHawes35792127416

    Usblib Used

  12. No Image 09Oct
    by LonnaTkn51221587

    FBackup Automatic Backup Software Download V5 4 799 Chinese Version

  13. 09Oct
    by MaryjoTimperley009

    Computer Hard Disk Bad Inside All The Information Lost How To Do

  14. 09Oct
    by JoyceFitzRoy55492834

    Wang Haipeng

  15. 09Oct
    by LatashiaLuker55

    Elasticsearch And Mongodb Distributed Cluster Environment Data Synchronization

  16. No Image 09Oct
    by JDTStephaine0060830

    Copyright Printing

  17. 04Oct
    by TanishaPapst8475

    Nike Colts 12 Andrew Luck Royal Blue Team Color Mens Stitched NFL Limited Jersey Discount 20% Off

  18. 30Sep
    by CedricYarborough246

    China Nfl Jerseys From Viamao

  19. 27Sep
    by GlennaHaris3602814

    68 Cyril Richardson Jersey W0J

  20. 26Sep
    by KarineGresham77293

    What Is The Function Of Glucosamine In A Dog Diet Regime

Board Pagination Prev 1 2 Next
/ 2
GUIDE

START-UP with 공사박사

  • 사이트 제목 바꾸기

    대금 보호 서비스

    대금 보호 서비스를 이용하세요.
    혹시 수금이 안될까? 공사박사는 서울보증보험과 함께 대금 보호 서비스를 제공합니다.(자세히보기)

  • 메뉴 구조 구성하기

    장인들과의 만남

    10년 이상 장인들이 책임 시공합니다.
    공사 박사는 현장 경험 10년 이상인 장인분들 함께 만들어 가는 곳입니다.

  • 레이아웃 디자인 바꾸기

    평가 시스템 운영

    수요자 공급자 양방향 평가를 합니다.
    양방향 평가를 위해 수요자 공급자 모두의 가치를 끌어올립니다.

  • 초기화면 바꾸기

    무료 등록

    광고비가 없습니다.
    무료로 등록만 해 두시면 수요자와 공급자를 공사박사만의 특화된 시스템으로 매칭해 드립니다.

  • 기능과 디자인 추가하기

    일일 작업 리포트 제공

    진행 사항을 한눈에 확인 하실 수 있습니다.
    공사 박사가 제공하는 일일 작업 리포트로 공사 진행 사항부터 예상 완료 시점까지 체크할 수 있습니다.

  • 레이아웃 꾸미기

    자동 이력 관리

    자동 이력 관리 시스템으로 믿을 수가 있습니다. 공급자는 자신의 커리어를 관리할 수 있으며, 수요자는 공급자 선택에 신뢰가 생깁니다.

위로