Recently, I am responsible for the development of an im project. The server and client are connected over TCP protocol. The server adopts C # development and the client adopts Delphi development. During server development, I encountered various abnormal network disconnections. I have some experience in handling these exceptions. I 'd like to write it out and share it with you.
What are the main causes of network disconnection? To sum up, there are two main types:
1. ClientProgramException.
In this case, we can handle it well, because the client program will cause a socket exception of connectionreset on the server side (that is, the 10054 exception in Winsock2) when it exits abnormally ). You only need to handle this exception on the server.
2. network link exceptions.
Such as network cable disconnection, switch power loss, and client machine power loss. When these conditions occur, the server will not have any exceptions. In this case, the aboveCodeYou cannot handle this situation. This is the case in msdn. I will post the original article of msdn here:
If you need to determine the current status of the connection, please do not block, zero-byte send call. If the call is successful or the waewouldblock error code (10035) is triggered, the socket is still in the connection status; otherwise, the socket is no longer in the connection status.
However, in practical applications, I found that this method described by msdn is often ineffective and cannot detect that the network has been disconnected abnormally. What should we do?
We know that TCP has a connection detection mechanism, that is, if no data is transmitted within the specified time (generally 2 hours), a keep-alive datagram will be sent to the peer end, the serial number used is the serial number of the last byte of the last sent packet. If the peer receives the data, it returns a tcp ack to confirm that the byte has been received, the connection is not disconnected. If the response is not received by the other party for a period of time, the system will try again. After several retries, it will send a reset to the peer end and disconnect the connection.
In Windows, the first test is performed every two seconds after the last data is sent. If no response is received for five times, the connection will be disconnected. But two hours is obviously too long for our project. We must shorten this time. So what should we do? I want to use the iocontrol () function of the socket class. Let's take a look at what this function can do:
Use iocontrolcode enumeration to specify the control code and set the low-level operation mode for the socket.
Namespace: system. net. Sockets
Assembly: system (in system. dll)
Syntax
C #
Public int iocontrol (
Iocontrolcode,
Byte [] optioninvalue,
Byte [] optionoutvalue
)
Parameters
Iocontrolcode
An iocontrolcode value that specifies the control code for the operation to be executed.
Optioninvalue
Byte array, which contains the input data required by the operation.
Optionoutvalue
Byte array, which contains the output data returned by the operation.
Return Value
The number of bytes in the optionoutvalue parameter.
For example:
Socket. iocontrol (iocontrolcode. keepalivevalues, inoptionvalues, Null );
We need to clarify the definition of inoptionvalues, which is a struct in C ++. Let's take a look at this struct:
Struct Tcp_keepalive
... {
U_long Onoff; // whether to enable keep-alive
U_long KeepAliveTime; // how long will the first probe start (unit: milliseconds)
U_long keepaliveinterval; // detection interval (unit: milliseconds)
} ;
In C #, we directly use a byte array to pass it to the function:
Uint Dummy = 0 ;
Byte [] Inoptionvalues = New Byte [Marshal. sizeof (dummy) * 3 ];
Bitconverter. getbytes (( Uint ) 1 ). Copyto (inoptionvalues, 0 ); // Whether to enable keep-alive
Bitconverter. getbytes (( Uint ) 5000 ). Copyto (inoptionvalues, inclual. sizeof (dummy); // how long does it take to start the first probe?
Bitconverter. getbytes (( Uint ) 5000 ). Copyto (inoptionvalues, Marshal. sizeof (dummy) * 2 ); // Test Interval
Specific implementation code:
Public Static Void Acceptthread ()
... {
Thread. currentthread. isbackground = True ;
While ( True )
... {
Uint Dummy = 0 ;
Byte [] Inoptionvalues = New Byte [Marshal. sizeof (dummy) * 3 ];
Bitconverter. getbytes (( Uint ) 1 ). Copyto (inoptionvalues, 0 );
Bitconverter. getbytes (( Uint ) 5000 ). Copyto (inoptionvalues, Marshal. sizeof (dummy ));
Bitconverter. getbytes (( Uint ) 5000 ). Copyto (inoptionvalues, Marshal. sizeof (dummy) * 2 );
Try
... {
Accept (inoptionvalues );
}
Catch ... {}
}
}
Private Static Void Accept ( Byte [] Inoptionvalues)
... {
Socket socket = Public. s_sockethandler.accept ();
Socket. iocontrol (iocontrolcode. keepalivevalues, inoptionvalues, Null );
Userinfo info = New Userinfo ();
Info. Socket = Socket;
Int ID = Getuserid ();
Info. Index = ID;
Public. s_userlist.add (ID, Info );
Socket. beginreceive (info. buffer, 0 , Info. Buffer. length, socketflags. None, New Asynccallback (effececallback), Info );
}
Well, this will be successful.