Author: sodimethyl Source: http://blog.csdn.net/sodme statement: This article can be reproduced, reproduced, spread without the consent of the author, but any reference to this article please keep the author, source and this statement information. Thank you!
The common network servers are basically 7x24. For online games, at least require the server to work continuously for more than a week and ensure that no catastrophic event such as server crash occurs. In fact, it is almost unrealistic to require a server to operate at full capacity without any exceptions. The server itself can cause exceptions (but it should be as few as possible). However, the server itself should be designed to be robust and robust, and the "minor illness and minor disaster recovery" cannot beat it down, this requires the server to take a lot of effort in exception handling.
Server Exception Handling involves a wide range of content. This article only discusses exceptions in network packets, hoping to help those who are engaged in related work.
Network Packet exceptions can be divided into two categories: one is the exception in the packet format, and the other is the exception in the packet content (that is, the packet data. In terms of packet format exception handling, the network packet receiving module at the bottom can handle the exception. For exceptions in the contents of the packed data, it is only necessary to determine and test the logic of the game. Exception Handling in game logic varies with each game. Therefore, the subsequent content of this article will focus on exception handling in the network packet receiving module.
To facilitate the following discussions, we should first clarify the two concepts (these two concepts are for the purpose of narration, which I have taken on my own and there is no standard): 1. Logic package: data packets are submitted at the application layer. A complete logical package can indicate an exact logical meaning. For example, the logon package can contain the username field and password field. Although it seems to be a piece of buffer data, each interval in the buffer zone represents a certain logical significance. 2. Physical package: indicates the data packets received from the bottom layer of the network using Recv (recvfrom) or wsarecv (wsarecvfrom). Can a received data packet represent a complete logical meaning, it depends on whether it is a packet sent through the UDP-type "Datagram Protocol" or a packet sent through the TCP-type "stream protocol.
We know that TCP is a stream protocol. The difference between "stream protocol" and "Datagram Protocol" is that a network package in "Datagram Protocol" is a complete logical package, that is, after the application layer uses sendto to send a logical package, the receiver receives the logical package that was just sent using sendto through recvfrom. This package will not be sent separately, it will not be sent together with other packages. However, for TCP, TCP willAlgorithm, Or separate a logical package, or divide a logical package into multiple sends, or combine multiple logical packages to send them out. Because of TCP's logic packet processing, we need to write the corresponding package and unpackage during TCP-based applications.Code.
Therefore, upper-layer TCP-based applications generally need to define their own packet formats. In the definition of TCP packets, except for the logical meaning of specific data content, the first step is to determine the method to indicate the start and end of the current packet. Generally, there are two methods to indicate the start and end of a TCP logical package: 1. It is represented by a special start and end sign, for example, ff00 indicates the start, and 00FF indicates the end. 2. The length of the package is expressed directly. For example, you can use the first byte to indicate the total length of the package. If you think this is the case, the package size can be small or two bytes.
The code to be given below is a 2nd-type data packet defined. The packet length is expressed in the first two bytes of each packet. I will give relevant explanations and instructions in combination with the code.
Description of variables used in functions:
Client_buffer_size: the length of the buffer, defined as: const int client_buffer_size = 4096. M_clientdatabuf: The data sorting buffer. The data received each time is copied to the end of the buffer, and the following sorting function sorts the buffer. It is defined as char m_clientdatabuf [2 * client_buffer_size]. M_databufbytecount: Number of unsorted bytes in the data sorting buffer. Getpacketlen (const char *): the function that extracts the length of the current logical package based on the first address of the introduced buffer according to the application layer protocol. Getgamepacket (const char *, INT): a function that generates game logic data packets based on the input buffer. Addtoexelist (pbasegamepacket): This function adds the specified game logic data packet to the queue of the game logic data packet to be processed and waits for the logic processing thread to process it. Data_pos: refers to the starting position of the real data packet content in addition to the packet length, package type, and other Mark Fields.
Bool splitfun (const char * pdata, const Int & Len) {pbasegamepacket pgamepacket = NULL; _ int64 startpos = 0, prepos = 0, I = 0; int packetlen = 0;
// Copy the received data to the end of the sorting buffer. startpos = m_databufbytecount; memcpy (m_clientdatabuf + startpos, pdata, Len); m_databufbytecount + = Len;
// When the number of bytes in the sorting buffer is less than data_pos, exit if the length is not obtained. // Note: When exiting, m_databufbytecount is not set to 0 if (m_databufbytecount <data_pos + 1) return false;
// According to the normal logic, the following conditions are not possible. For the sake of security, add If (m_databufbytecount> 2 * client_buffer_size) {// set m_databufbytecount to 0, this means that the existing data in the buffer zone is discarded. m_databufbytecount = 0;
// You can consider opening an interface for processing data packets in error format and handing over the processing logic to the upper layer. // onpacketerror () return false ;}
// Restore the starting pointer startpos = 0;
// This statement packetlen = getpacketlen (piocpclient-> m_clientdatabuf) can be executed only when the number of bytes in m_clientdatabuf is greater than the minimum package length );
// When the package length of the logic layer is invalid, the package if (packetlen <data_pos + 1) | (packetlen> 2 * client_buffer_size) is directly discarded )) {m_databufbytecount = 0;
// Onpacketerror () return false ;}
// Keep the pointer _ int64 oldlen = m_databufbytecount at the end of the buffer;
While (packetlen <= m_databufbytecount) & (m_databufbytecount> 0) {// call the package logic to obtain the data packet pgamepacket = getgamepacket (m_clientdatabuf + startpos, packetlen );
If (pgamepacket! = NULL) {// Add the data packet to the execution queue addtoexelist (pgamepacket );}
Pgamepacket = NULL; // adjust the number of remaining bytes in the buffer and the starting position of the new logical package m_databufbytecount-= packetlen; startpos + = packetlen;
// The number of bytes in the residual buffer is smaller than the size of a normal package. Only copy the package forward and then exit if (m_databufbytecount <data_pos + 1) {for (I = startpos; I <startpos + m_databufbytecount; ++ I) m_clientdatabuf [I-startpos] = m_clientdatabuf [I];
Return true ;}
Packetlen = getpacketlen (m_clientdatabuf + startpos );
// If (packetlen <data_pos + 1) | (packetlen> 2 * client_buffer_size) of the package whose length is invalid in the logic layer, discard the package and the package after the buffer zone )) {m_databufbytecount = 0;
// Onpacketerror () return false ;}
If (startpos + packetlen> = oldlen) {for (I = startpos; I <startpos + m_databufbytecount; ++ I) m_clientdatabuf [I-startpos] = m_clientdatabuf [I];
Return true;} // obtain all the complete packages
Return true ;}
The above is the processing function of the Data receiving module. The following is a brief description:
1. The buffer (m_clientdatabuf) used for package sorting should be larger than the length (client_buffer_size) of the receiving buffer (pdata) specified in Recv, generally, the former is twice the latter (2 * client_buffer_size) or larger.
2. To avoid additional overhead caused by moving the remaining data forward, m_clientdatabuf is recommended to use a circular buffer.
3. To avoid unassembled packages, we agree that the maximum length of a single logical package cannot exceed twice the client_buffer_size for each sent logical package. Because our sorting buffer is only 2 * client_buffer_size so long, we will not be able to sort out the longer data. This requires that such an exception handling mechanism be added to the Protocol design and the final sending function processing.
4. For packets that are too short or too long, we usually set m_databufbytecount to 0, that is, discard the processing of the current packet. If m_databufbytecount is not set to 0, but the client sends a packet with an incorrect format, the subsequent package will also generate a format error, if m_databufbytecount is set to 0, it is better to avoid subsequent packages from being affected by the format error of this package. A better way is to open an onpacketerror interface for handling packet format exceptions here, and the upper-layer logic determines how to handle such exceptions. For example, the upper-layer logic can count the exceptions in the packet format. If the number of errors exceeds a certain value, the client can be disconnected.
5. do not perform the preceding operations after the Recv or wsarecv function. When a Recv receives a piece of data, it generates a struct or object (it mainly contains data and Len, the former is the data buffer, and the latter is the data length ), put such a struct or object into a queue and use the splitfun function by the following threads. In this way, the network data receiving speed can be maximized without wasting time here due to data sorting.
in the code, I have made a more detailed comment, which can be used as a reference for the package function. The code is extracted and modified from my application, it is only used for demonstration, so it is not debugged and needs to be improved by yourself. If you have any questions, you can leave a message on my blog.