Winsock provides two functions specially optimized for file and memory data transmission. The transmitfile () API function can be used in both Windows NT 4.0 and Windows 2000, while transmitpackets () will be implemented in future Windows versions.
Transmitfile () is used to transmit the file content through Winsock. Generally, you can call createfile () to open a file and call readfile () and wsasend () repeatedly until the data is sent. However, this method is inefficient because every call to readfile () and wsasend () involves a conversion from user mode to kernel mode. If you replace it with transmitfile (), you only need to give it a handle to the opened file and the number of bytes to be sent. The involved mode conversion operation will only call createfile () it occurs once when the file is opened, and then again when transmitfile. In this way, the efficiency is much higher.
Transmitpackets () is more advanced than transmitfile (). It allows users to send multiple specified files and memory buffers only once. The function prototype is as follows:
Bool transmitpackets (
Socket hsocket,
Lptransmit_packet_element lppacketarray,
DWORD nelementcount,
DWORD nsendsize,
Lpoverlapped,
DWORD dwflags
);
Here, lppacketarray is a structure array. Each element can be a file handle or a memory buffer. The structure is defined as follows:
Typedef struct _ transmit_packets_element {
DWORD dwelflags;
DWORD clength;
Union {
Struct {
Large_integer nfileoffset;
Handle hfile;
};
Pvoid pbuffer;
};
} Transmit_file_buffers;
Each field is self-descriptive ).
Dwelflags field: Specifies whether the current element is a file handle or a memory buffer (specified by the constant tf_element_file and tf_element_memory respectively );
Clength field: Specify the number of bytes that will be sent from the data source (if it is a file, the value of this field is 0 indicates that the entire file is sent );
The unsung consortium in the structure: Memory buffer (and possible offset) that contains the file handle ).
Another advantage of using these two APIS is that you can reuse the socket handle by specifying the tf_reuse_socket and tf_disconnect flag. Every time the API completes data transmission, it will disconnect at the transport layer level, so that this socket can be re-provided to acceptex. Using this optimized programming method will reduce the pressure on the thread dedicated to the operation to create a socket (as mentioned above ).
Both APIs share a common weakness: in Windows NT Workstation or Windows 2000 Professional Edition, a function can only process two call requests at a time, full support is only available for Windows NT, Windows 2000 Server, Windows 2000 Advanced Server, or Windows 2000 data center.
Put them together
In the previous sections, we discussed how to develop high-performance, large-response applications.ProgramRequired functions, methods, and possible resource bottlenecks. What do these mean to you? In fact, it depends on how you construct your server and client. When you can better control the server and client design, the more you can avoid bottlenecks.
Let's look at a demonstration environment. We need to design a server to respond to client connections, send requests, receive data, and disconnect. Then, the server will need to create a listening socket, associate it with a completed port, and create a working thread for each CPU. Create another thread dedicated to issuing acceptex (). We know that the client will send data immediately after a connection request is sent, so it will be easier if we are ready to receive the buffer. Of course, do not forget to poll the socket used in the acceptex () call from time to time (using the so_connect_time option parameter) to ensure that there is no malicious timeout connection.
There is an important issue in this design. We should consider the number of acceptex () waiting times. This is because every time an acceptex () is issued, we need to provide a receiving buffer for it at the same time, so there will be a lot of locked pages in the memory (as mentioned above, each overlap operation consumes a small part of the non-Paging memory pool and locks all the involved buffers ). There is no definite answer to this question. The best way is to make this value adjustable. Through repeated performance tests, you can get the best value in a typical application environment.
Well, after you make a clear estimate, the following is the problem of sending data. The focus is on how many concurrent connections you want the server to process at the same time. Generally, the server should limit the number of concurrent connections and the number of sending and calling requests waiting for processing. The more concurrent connections, the more non-Paging memory pools are consumed. The more sending and calling requests waiting for processing, the more pages the memory is locked (Be careful not to exceed the limit ). This also requires repeated tests to know the answer.
In the preceding environment, you do not need to disable the buffer of a single socket, because only one operation to receive data is performed in acceptex, it is not too difficult to provide the receiving buffer for each incoming connection. However, if the interaction between the client and the server changes, the client needs to send more data after sending the data once. In this case, disabling the receiving buffer is not good, unless you want to ensure that each connection sends an overlapping Receiving call to receive more data.
Conclusion
developing a Winsock server with a large response scale is not terrible. In fact, it is to set up a listening socket, accept connection requests, and perform overlapping sending and receiving calls. By setting a reasonable number of overlapping calls for waiting to prevent unused non-Paging memory pools, this is the main challenge. Based on the principles discussed earlier, you can develop server applications with large response sizes.