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.