In the previous chapter, we talked about how to use tcplistener to listen for connections and how to use tcpclient to establish a connection and send messages (step by step to teach you how to use. Net for socket communication ). Download the source code of this article (Attachment)
In this chapter, we will explain in depth how to define the network communication protocol and how to use it for communication.
First, we need to reconstruct the network communication component mentioned in the previous chapter.
We know that both the server side and the client side need to listen on whether the other side sends messages. If we only run in the main thread, we can't do other work, such as UI. We put the listen method into a base class.
Create a socketbase class. Let both the server and client inherit from this class. Put the connections attribute in the server class into this class (because the client may also have multiple connections), because the client also needs to listen for messages sent from the server, therefore, we also put the _ listenningthread and listenning functions into the class. Change the private type variable to protected so that the inherited class can be used. Change the listening function to virtual so that the listening function can be rewritten in the subclass.
The network protocol is defined as follows:
A message body consists of a message header and a message body. The message body is a string type.
The message header contains three parts. The first four bytes are the total length of the message, 5th bytes represent the command, and 6th and 7 bytes represent the Protocol version. According to this protocol, we define the message class. The source code is as follows:
1 using system;
2
3 namespace socketlibrary
4 {
5 public class message
6 {
7 public Enum commandheader: byte {
8 sendmessage = 1
9}
10 public connection sendtoorreceivedfrom;
11 Public int messagelength;
12 public commandheader command;
13 public byte mainversion;
14 public byte secondversion;
15
16 Public String messagebody;
17
18 public message ()
19 {
20 sendtoorreceivedfrom = NULL;
21}
22 public message (commandheader command, byte mainversion, byte secondversion, string messagebody): this (){
23 This. Command = command;
24 This. mainversion = mainversion;
25 this. secondversion = secondversion;
26 This. messagebody = messagebody;
27}
28}
We add a connection variable in the message to store the message to be sent or from which the connection receives the message.
Because we need to write a byte array to the network stream. We add a tobytes Method to the Message class. Returns a byte array.
1 Public byte [] tobytes (){
2 This. messagelength = 7 + socketfactory. defaultencoding. getbytecount (this. messagebody); // calculate the total length of the message. The length of the message header is 7 plus the length of the message body.
3 byte [] buffer = new byte [This. messagelength];
4 // write the four bytes in length to the array first.
5 bitconverter. getbytes (this. messagelength). copyto (buffer, 0 );
6 // write the commandheader into the Array
7 Buffer [4] = (byte) This. Command;
8 // write the primary version number to the array
9 buffer [5] = (byte) This. mainversion;
10 // write the version number to the array
11 buffer [6] = (byte) This. secondversion;
12
13 // The message header has been written, and the message body is now written.
14 socketfactory. defaultencoding. getbytes (this. messagebody, 0, this. messagelength-7, buffer, 7 );
15 return buffer;
16}
The byte array we read from the network stream also needs to be converted to the Message class. We use a static method parse. The parameter of this method is connection. Returns a message class.
1 public static message parse (connection ){
2 message = new message ();
3 // read the first four bytes, that is, the message length.
4 byte [] buffer = new byte [4];
5 If (connection. networkstream. dataavailable ){
6 int COUNT = connection. networkstream. Read (buffer, 0, 4 );
7 if (COUNT = 4 ){
8 Message. messagelength = bitconverter. toint32 (buffer, 0 );
9}
10 else
11 throw new exception ("Incorrect network stream length ");
12}
13 else
14 throw new exception ("Currently the network is unreadable ");
15 // read other bytes of the message
16 buffer = new byte [message. messagelength-4];
17 if (connection. networkstream. dataavailable ){
18 int COUNT = connection. networkstream. Read (buffer, 0, buffer. Length );
19 if (COUNT = message. messagelength-4 ){
20 message. Command = (commandheader) buffer [0];
21 message. mainversion = buffer [1];
22 message. secondversion = buffer [2];
23
24 // read the message body
25 message. messagebody = socketfactory. defaultencoding. getstring (buffer, 3, buffer. Length-3 );
26 message. sendtoorreceivedfrom = connection;
27
28 return message;
29}
30 else
31 throw new exception ("Incorrect network stream length ");
32}
33 else
34 throw new exception ("Currently the network is unreadable ");
35}
36
37}
Let's write another class messagecollection inherited from collectionbase. Used to store message queues.
1 using system;
2
3 namespace socketlibrary
4 {
5 public class messagecollection: system. Collections. collectionbase
6 {
7 public messagecollection ()
8 {
9
10}
11 Public void add (Message Value ){
12 list. Add (value );
13}
14 public message this [int Index] {
15 get {
16 return list [Index] as message;
17}
18 set {
19 list [Index] = value;
20}
21}
22 public messagecollection this [connection] {
23 get {
24 messagecollection collection = new messagecollection ();
25 foreach (message in list ){
26 if (message. sendtoorreceivedfrom = connection)
27 collection. Add (Message );
28}
29 return collection;
30}
31}
32}
33}
Okay. After the protocol is developed, we will modify the listenning function of socketbase. When the network stream is readable, we read a message in message format. Sockonclose and messagereceived events are defined in socketbase. It is triggered in socketbase's listenning according to the situation.
Add a messageconnection attribute to socketbase. Indicates the message queue to be sent.
We need to start another thread. This interface is used to extract and send the message to be sent from the message queue.
The source code is as follows. We did not close the network connection when the source code is stopped. Disable it on your own.
In the final optimization, the source code mentioned above has been modified. Please download the source code from the top of this article (click to view the attachment)