socket network communication module based on Unity

Establish connection and event distribution

1, Client
1. Define a message body. When the server communicates with the client, it transmits such information.

using System.Collections;
using System.Text;

public class SocketMessage{
	//Large modules, such as login registration, role module, shopping mall purchase module, etc
	public int ModuleType{get;set;}
	//Further classification and refinement, such as the separation of login and registration in login registration
	public int MessageType{get;set;}
	//The core content of SocketMessaged, including various contents
	public string Message{get;set;}
	//Total bytes of information
	public int Length{get;set;}

	public SocketMessage(int moduleType,int messageType,string message){
		ModuleType = moduleType;
		MessageType = messageType;
		Message = message;
		//Bytes of Length, bytes of ModuleType, bytes of MessageType, bytes of storage string Length automatically added by the system, bytes of Message
		Length = 4 + 4 + 4 + 1 + Encoding.UTF8.GetBytes(message).Length;
		}
	}
}

2. Because binary information is transmitted, there should be a class dedicated to reading and writing binary information

using System.Collections;
using System.IO;
using System;
using System.Text;

//Reading and writing of SocketMessage
public class ByteArray{
	//In order to save the transmission traffic, binary is transmitted
    //Read and write operations are performed on a stream. MemoryStream is used here
	private MemoryStream memoryStream;
	private BinaryReader binaryReader;
	private BinaryWriter binaryWriter;

	private int readIndex = 0;
	private int writeIndex = 0;

	public ByteArray(){
		memoryStream = new MemoryStream();
		binaryReader = new BinaryReader(memoryStream);
		binaryWriter = new BinaryWriter()memoryStream;
	}
	 
	public void Destroy(){
		binaryReader.Close();
		binaryWriter.Close();
		memoryStream.Close();
    	memoryStream.Dispose();
	}
	
	public int GetReadIndex(){
		return readIndex;
	}
	
	public int GetLength(){
		return (int)memoryStream.Length;
	}
	
	public int GetPosition(){
		//position starts from 0
		return (int)memoryStream.Positon;
	}
	   public byte[] GetByteArray()
    {
        return memoryStream.ToArray();
    }
 
    public void Seek(int offset, SeekOrigin seekOrigin)
    {
        //Offset: offset parameter relative to the position specified by SeekOrigin
        memoryStream.Seek(offset, seekOrigin);
    }
 
 
    #region read
   	 public bool ReadBoolean(){
        Seek(readIndex, SeekOrigin.Begin);
        bool a = binaryReader.ReadBoolean();
        readIndex += 1;
        return a;
    	}
 
    public short ReadInt16(){
        Seek(readIndex, SeekOrigin.Begin);
        short a = binaryReader.ReadInt16();
        readIndex += 2;
        return a;
   		}
 
    public int ReadInt32(){
        Seek(readIndex, SeekOrigin.Begin);
        int a = binaryReader.ReadInt32();
        readIndex += 4;
        return a;
	    }
 
    public float ReadSingle(){
        Seek(readIndex, SeekOrigin.Begin);
        float a = binaryReader.ReadSingle();
        readIndex += 4;
        return a;
	    }
 
    public double ReadDouble(){
        Seek(readIndex, SeekOrigin.Begin);
        double a = binaryReader.ReadDouble();
        readIndex += 8;
        return a;
	    }
 
    public string ReadString(){
        Seek(readIndex, SeekOrigin.Begin);
        string a = binaryReader.ReadString();
        //Because when a binaryWriter writes a string, a byte will be added before the string to store the length of the string
        readIndex += Encoding.UTF8.GetBytes(a).Length + 1;
        return a;
    	}
    #endregion
 
    #region write
   	 public void Write(bool value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        writeIndex += 1;
	    }
 
    public void Write(short value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        writeIndex += 2;
    	}
 
    public void Write(int value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        writeIndex += 4;
    	}
 
    public void Write(float value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        writeIndex += 4;
    	}
 
    public void Write(double value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        writeIndex += 8;
    	}
 
    public void Write(string value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        //Because when a binaryWriter writes a string, a byte will be added before the string to store the length of the string
        writeIndex += Encoding.UTF8.GetBytes(value).Length + 1;
    	}
 
    public void Write(byte[] value){
        Seek(writeIndex, SeekOrigin.Begin);
        binaryWriter.Write(value);
        writeIndex += value.Length;
   	 	}
    #endregion
	}
}

3. Define the socket client to connect to the server and send and receive information

using System.Collections;
using System.Net.Sockets;
using System.Net;
using System;
using System.Text;
using System.Threading;
using UnityEngine;

public class SocketClient {
	private Socket socket;  							//Current socket
	private ByteArray byteArray = new ByteArray();  	// Byte array cache
	private Thread handleMessage;  						//Threads processing messages
	
	public SocketClient(){
		handleMessage = new Thread(HandleMessage);
		handleMessage.Start();
	}
	
	public SocketClient(Socket socket){
		this.socket = socket;
		handleMessage = new Thread(HandleMessage);
		handleMessage.Start();
	}
	
	public Socket GetSocket(){
		return socket;
	}

	public void Destroy(){
		handleMessage.Abort();
		socket.Colse();
		byteArray.Destroy();
	}

	/// <summary>
    ///Asynchronous connection server
    /// </summary>
	public void AsynConnect(){
		IPEndpiont serverIp = new IPEndPoint(IPAress.Parse("127.0.0.1"),80);
		socket = new Socket(AdressFamily.InterNetWork,SocketType.Stream,ProtocolType.Tcp);
		socket.BeginConnect(serverIp,asynResult =>
		{
			socket.EndConnect(asynResult);
			Debug.Log("connect success!");

			AsynReceive();
			AsynSend(new SocketMessage(19,89,"Hello, server"));
			AsynSend(new SocketMessage(19,89,"Hello, server"));
			AsynSend(new SocketMessage(19,89,"Hello, server"));
		},null);
	}
	/// <summary>
    ///Receive information asynchronously
    /// </summary>
    public void AsynReceive(){
    	byte[] data = new byte[1024];
    	socket.BeginReceive(data,0,data.Length,SocketFlags.None,
    	asynResult =>
    		{int length = socket.EndReceive(asyncResult);
    		byte[] temp = new byte[length];
    		Debug.log("The number of bytes received is" + length);
    		Array.Copy(data , 0 , temp ,0 ,legth);
    		byteArray.Write(temp);
			AsynReceive();},
			null);
    }
	 /// <summary>
    ///Send information asynchronously
    /// </summary>
    public void AsynSend(SocketMessage sm){
   		ByteArray ba = new ByteArray();
   		ba.Write(sm.Length);
   		ba.Write(sm.ModuleType);
   		ba.Write(sm.MessageType);
   		ba.Write(sm.MessageType);
   		ba.Write(sm.Message);
		
		byte[] data = ba.GetByteArray();
		ba.Destroy();
		socket.BeginSend(data, 0, data.Length,SocketFlags.None, asyncResult =>{
			int length = socket.EndSend()asyncResult;
			}, null);
    }
     /// <summary>
    ///Parsing information
    /// </summary>
    public void HandleMessage(){
    	int tempLength = 0;						//Length used to hold messages
    	bool hasGetMessageLength = false;		//Whether the message length is obtained

		while(true){
			if(!hasGetMessaeLength){
				if(byteArray.GetLength() - byteArray.GetReadIndex() > 4)  		//Message length is int, accounting for 4 bytes
				{
					tempLength = byteArray.ReadInt32();							//Length of read message
					hasGetMessageLength = true;
				}
			}
			else{
				//Judge whether the message is complete according to the length. GetReadIndex() can get the read bytes
				//Note that after the above ReadInt32 is read, the read index will be added with 4. At this time, you need to subtract the extra
				if((tempLength + byteArray.GetReadIndex() - 4) <= byteArray.GetLength()){
					SocketMessage sm = new SocketMessage(byteArray.ReadInt32(),byteArray.ReadInt32(),byteArray.ReadString());
					SocketSingletion.Instance.Send(sm);
					hasGetMessageLength =false;
				}
			}
		}
	}
}

4 define a singleton base class

using UnityEngine;
using System.Collection;

private class MonoSingletion<T>:Monobehaviour{
	private static T instance;
	public static T Instance{
		get{return instance;}
	}
	void Awake(){
		instance = GetComponent<T>();
	}
}

5. Define a class, manage the life cycle of socke client, and provide event interface for other classes to use

using UnityEngine;
using System.Collections;

public class SocketSingletion : MonoSingletion<SocketSingletion>{
	public SocketClient socketClient;
	public deglegate void SendDelegate(SocketMessage sm);
	public event SendDegegate SendEvent =null;
	
	//Initialize use this for initialization
	void Start(){
		socketClient = new SocketClient();
		socketClient.AsynConnect();
	}
	//Update is called once per frame
	void Update(){
	}

	public void Send(SocketMessage sm){
		sendEvent(sm);
	}

	void OnDestroy(){
		print("Destory socketClient")
		socketClient.Destroy();
	}
}

2, Server side
1.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
 
public class SocketServer {
 
    private Socket socket;//Current socket
    public Dictionary<string, SocketClient> dictionary = new Dictionary<string, SocketClient>();//string is the ip address
 
    public void Listen()
    {
        IPEndPoint serverIp = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Bind(serverIp);
        socket.Listen(100);
        Console.WriteLine("server ready.");
        AsynAccept(socket);
    }
 
    /// <summary>
    ///Asynchronous connection client
    /// </summary>
    public void AsynAccept(Socket serverSocket)
    {
        serverSocket.BeginAccept(asyncResult =>
        {
            Socket client = serverSocket.EndAccept(asyncResult);
            SocketClient socketClient = new SocketClient(client);
            
            string s = socketClient.GetSocket().RemoteEndPoint.ToString();
            Console.WriteLine("Connected clients are: " + s);
            dictionary.Add(s, socketClient);
 
            socketClient.AsynRecive();
            socketClient.AsynSend(new SocketMessage(20, 15, "Hello, client"));
            socketClient.AsynSend(new SocketMessage(20, 15, "Hello, client"));
            socketClient.AsynSend(new SocketMessage(20, 15, "Hello, client"));
            AsynAccept(serverSocket);
        }, null);
    }
 
    /// <summary>
    ///Parsing information
    /// </summary>
    public static void HandleMessage(SocketClient sc, SocketMessage sm)
    {
        Console.WriteLine(sc.GetSocket().RemoteEndPoint.ToString() + "   " + 
            sm.Length + "   " + sm.ModuleType + "   " + sm.MessageType + "   " + sm.Message);
    }
}

2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
            SocketServer socketServer = new SocketServer();
            socketServer.Listen();
            Console.ReadKey();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace ConsoleApplication6
{
    class Program
    {
        static void Main(string[] args)
        {
            SocketServer socketServer = new SocketServer();
            socketServer.Listen();
            Console.ReadKey();
        }
    }
}

Three. Test

1. Create a new test class in unity

using UnityEngine;
using System.Collections;
 
public class ReceiveSocketMessage : MonoBehaviour {
 
	// Use this for initialization
	void Start () 
    {
        SocketSingletion.Instance.sendEvent += PrintInfo;
	}
	
	// Update is called once per frame
	void Update () 
    {
	
	}
 
    public void PrintInfo(SocketMessage sm)
    {
        print("   " + sm.Length + "   " + 
            sm.ModuleType + "   " + sm.MessageType + "   " + sm.Message);
    }
}

2. Operation procedure
Analysis: the server sends three messages to the client, and I use two GameObjects to subscribe to the received events, so I print six messages. At the same time, only two messages are received in the client, one is 62 bytes and the other is 31 bytes, which indicates that there is a packet sticking problem. What I use here is to add four bytes (int type) before each message body to record the length of the message, so that the subcontracting can be realized. In the same way, the server only accepts one message, which is also the embodiment of packet sticking.

Published 4 original articles, won praise 5, visited 1185
Private letter follow

Tags: socket encoding Unity

Posted on Mon, 10 Feb 2020 00:44:57 -0500 by anna_cm