Small C/S development framework - CSFrameWork

Posted by ponsho on Thu, 03 Feb 2022 13:00:03 +0100

Small C/S development framework - csframework (I)

First of all, it should be explained that the framework is based on a multi-level system, which has three layers - communication layer, session layer and outermost layer (server and client). Among them, the communication layer is the lowest layer, the next layer is the session layer, and finally the outermost layer (server and client). I use a picture to roughly express the relationship between them.

The following are all the class names of the framework:
.

Three elements of duplex communication

The so-called duplex is the "two-way communication working" mode.
In the C/S network programming mode, when the server communicates with a client in two directions, it must have the following three elements:

  • The object of Socket class, that is, the communication port encapsulation class. It can create the following communication channel and close the network connection at the end of communication;
  • Object of the DataInputStream class. Receiving the input communication channel from the "opposite end" information;
  • Object of DataOutputStream class. The output communication channel that sends messages to the "opposite end".

The bottom layer -- the implementation of communication layer

The communication layer has the following functions:

  1. Establish communication channel;
  2. Providing methods for sending and receiving information and closing connections;
  3. Check and find the abnormal disconnection of the opposite end, and notify the upper layer for handling.

As the lowest layer, the communication layer is the hub for establishing contact between the client and the server. It is a common set of code and can provide methods for the server session layer and the client session layer at the same time.

Let's first look at the thread code fragment of Communication class:

This thread runs continuously and listens for messages sent by the opposite end through line 40 (message = this.dis.readUTF()). Judge when to stop the running of the thread through the goal of Boolean type, and the exception capture block will judge the value of goal when the opposite end drops abnormally. If it is true, it indicates that the opposite end drops abnormally. At this time, an abstract class method peerAbnormalDrop() will be executed and handed over to the upper layer (ServerConversation and ClientConversation) to deal with the abnormal drop.

Method of sending information to opposite end:

The following is the complete code of the Communication class:

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public  abstract class Communication implements Runnable{
	protected Socket socket;
	protected String ip;
	protected DataInputStream dis;
	protected DataOutputStream dos;
	protected volatile boolean goon;
	
	Communication(Socket socket) throws IOException {
		this.socket = socket;
		this.ip = this.socket.getInetAddress().getHostAddress();
		this.dis = new DataInputStream(this.socket.getInputStream());
		this.dos = new DataOutputStream(this.socket.getOutputStream());
		
		this.goon = true;
		new Thread(this).start();
	}
	
	void send(NetMessage message) {
		try {
			this.dos.writeUTF(NetMessage.gson.toJson(message));
		} catch (IOException e) {
			//Sending a message to the opposite end failed, which means that the opposite end has closed communication!
			//Close network connection
			close();
		}
	}
	
	public abstract void dealNetMessage(NetMessage netMessage);
	public abstract void peerAbnormalDrop();

	@Override
	public void run() {
		String message = "";
		while (this.goon) {
			try {
				message = this.dis.readUTF();
				NetMessage netMessage = 
						NetMessage.gson.fromJson(message, NetMessage.class);
				dealNetMessage(netMessage);
			} catch (IOException e) {
				if (this.goon == true) {
					//goon is true, which means that the opposite end is abnormally disconnected!
					//Call exception drop handling.
					this.goon = false;
					peerAbnormalDrop();
				} 
				//If goon is false, it means normal offline,
				//You don't have to do anything.
			}
		}
		close();
	}
	
	protected void close() {
		this.goon = false;
		try {
			if (this.dis != null) {
				this.dis.close();
			}	
		} catch (IOException e) {
		} finally {
			this.dis = null;
		}
		
		try {
			if (this.dos != null) {
				this.dos.close();
			}	
		} catch (IOException e) {
		} finally {
			this.dos = null;
		}
		try {
			if (this.socket != null && !this.socket.isClosed()) {
				this.socket.close();
			}	
		} catch (IOException e) {
		} finally {
			this.socket = null;
		}
	}
	
}

Standardization of network information

The client and the client should not only send text, pictures, audio and other information to each other, but also carry out "command" operation between the client and the server. For example, when the client goes offline, it should send a message to the server to tell it that the client wants to go offline, and then do a series of operations about going offline. But this message should be distinguished from other chat messages, that is, strictly distinguish "conversation content" from "network command"!

For the convenience of "network command", we use Enum (enumeration):

public enum ENetCommand {
	OUT_OF_ROOM,
	ID,
	OFFLINE,
	
	TO_ONE,
	TO_OTHER,
	TO_SP_OTHER,
	
	FORCEDOWN,
	

The above commands are user-defined and represent different commands, such as TO_ONE (private chat), OFFLINE (OFFLINE), etc.

Now let's talk about the NetMessage class, which generates the information to be sent by the server and client. During sending and receiving, the string formed by this type of object is transmitted on the network. It should be converted to this type of object before sending and after receiving. (since this class is the internal core class of the tool, it is strictly prohibited to contact outside the package, so the modifier is "package modifier", that is, there is no decoration.

The following is the complete code of the NetMessage class:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class NetMessage {
	public static final Gson gson = new GsonBuilder().create();
	private ENetCommand command;
	private String action;
	private String message;
	
	public NetMessage() {
	}

	String getAction() {
		return action;
	}

	NetMessage setAction(String action) {
		this.action = action;
		return this;
	}

	ENetCommand getCommand() {
		return command;
	}

	NetMessage setCommand(ENetCommand command) {
		this.command = command;
		return this;
	}

	String getMessage() {
		return message;
	}

	NetMessage setMessage(String message) {
		this.message = message;
		return this;
	}
	
	
}

We will see that this class defines three members: Command (ENetcommand type, representing command), action (String type, which is not explained here and will be useful in the future), and message(String type, representing text message).

Notice this code in the NetMessage class: public static final gson = new gsonbuilder() Create(), there is a class of gson. The function of this class is to realize the conversion between objects and json strings. Gson jar.


The code above explains the role of Gson:

Network command statement TO_ONE and text message "I love you, girl!" A string converted into a key value pair is equivalent to packaging and classifying the sent information for transmission. However, when it comes to transport, we will start to mention the session layer!

The session layer is derived from the communication layer communication class, which is divided into server session layer ServerConversation class and client session layer ClientConversation class. The role of the session layer is to deal with various connections between the client and the server, and it is the bridge between them. Since the outermost client and server have not been written yet, we first build an empty shell of the session layer. After the server and client are written in the future, we can complete a series of functions of the session layer.

Topics: network server