Unity【Socket TCP】- A simple example of communication between server and client

In response to the needs of fans, an example of communication between the server and the client is made. The requirements are relatively simple. We use the Socket TCP protocol to build and directly use the fixed-length message method.

1. Server construction:

Open Visual Studio, File/New/Project, and create a console application:

Create a new Server class and Client class:

code show as below:

using System.Net;
using System.Net.Sockets;

namespace CoderZ
{
    public class Server
    {
        //端口
        private const int port = 8008;
        //客户端列表
        private List<Client> clients = new List<Client>();

        private static void Main(string[] args)
        {
            Console.WriteLine("服务端启动...");
            Server server = new Server();
            server.Init();
        }

        //服务端初始化
        private void Init()
        {
            TcpListener listener = new TcpListener(IPAddress.Any, port);
            listener.Start();
            try
            {
                while (true)
                {
                    Console.WriteLine("等待客户端接入...");
                    TcpClient client = listener.AcceptTcpClient();
                    Client clientInstance = new Client(client, this);
                    clients.Add(clientInstance);
                    Console.WriteLine($"{client.Client.RemoteEndPoint}接入.");
                }
            }
            catch(Exception error)
            {
                throw new Exception(error.ToString());
            }
        }

        /// <summary>
        /// 广播:向所有客户端发送数据
        /// </summary>
        /// <param name="data"></param>
        public void Broadcast(string data)
        {
            for (int i = 0; i < clients.Count; i++)
            {
                clients[i].Send(data);
            }
        }
        /// <summary>
        /// 移除客户端
        /// </summary>
        /// <param name="client"></param>
        public void Remove(Client client)
        {
            if (clients.Contains(client)) 
            { 
                clients.Remove(client);
            }
        }
    }
}
using System.Text;
using System.Net.Sockets;

namespace CoderZ
{
	public class Client
	{
		private Server server;
		private TcpClient tcpClient;
		private NetworkStream stream;

		/// <summary>
		/// 构造函数
		/// </summary>
		/// <param name="tcpClient"></param>
		/// <param name="server"></param>
		public Client(TcpClient tcpClient, Server server)
        {
			this.server = server;
			this.tcpClient = tcpClient;
			//启动线程 读取数据
			Thread thread = new Thread(TcpClientThread);
			thread.Start();
        }

		private void TcpClientThread()
        {
			stream = tcpClient.GetStream();
			//使用固定长度
			byte[] buffer = new byte[1024];

            try
            {
                while (true)
                {
					int length = stream.Read(buffer, 0, buffer.Length);
					if (length != 0)
                    {
						string data = Encoding.UTF8.GetString(buffer, 0, length);
						//解包
						Unpack(data);
                    }
                }
            }
			catch(Exception error)
            {
                Console.WriteLine(error.ToString());
            }
            finally
            {
				server.Remove(this);
            }
        }
		//拆包:解析数据
		private void Unpack(string data)
        {
            
        }
		/// <summary>
		/// 发送数据
		/// </summary>
		/// <param name="data"></param>
		public void Send(string data)
        {
			byte[] buffer = Encoding.UTF8.GetBytes(data);
			stream.Write(buffer, 0, buffer.Length);
        }
	}
}

For data parsing, we use the LitJson.dll tool here. If you don't have the tool, you can contact me to send a copy and open the View/Solution Explorer:

Right click on Solution/Add/Project References:

Click Browse, find the LitJson tool, and click OK to reference:

With LitJson, we can parse the data, but we haven't defined any data structure yet. The data we want to transmit includes pictures and characters, so the following data structure is defined here:

[Serializable]
public class SimpleData
{
	/// <summary>
	/// 图片数据
	/// </summary>
	public string pic;
	/// <summary>
	/// 字符内容
	/// </summary>
	public string content;
}

After introducing the LitJson namespace, parse the data:

//拆包:解析数据
private void Unpack(string data)
{
	SimpleData simpleData = JsonMapper.ToObject<SimpleData>(data);
	Console.WriteLine(simpleData.pic);
	Console.WriteLine(simpleData.content);
}

Now run our server:

Second, the Unity client construction:

Create a Client class, inherit from MonoBehaviour, and define a data structure consistent with the server:

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

public class Client : MonoBehaviour
{
    private string ipAddress;
    private int port;
    private bool isConnected;
    private Thread connectThread;
    private Thread readDataThread;
    private TcpClient tcpClient;
    private NetworkStream stream;
    //将数据存于队列 依次取出
    private Queue<string> queue = new Queue<string>();

    private void Start()
    {
        connectThread = new Thread(ConnectThead);
        connectThread.Start();
    }
    //连接线程
    private void ConnectThead()
    {
        tcpClient = new TcpClient();
        tcpClient.BeginConnect(ipAddress, port, ConnectThreadCallBack, tcpClient);
        float waitTime = 0f;
        while (!isConnected)
        {
            Thread.Sleep(500);
            waitTime += Time.deltaTime;
            if (waitTime > 3f)
            {
                waitTime = 0f;
                throw new Exception("连接超时");
            }
        }
    }

    private void ConnectThreadCallBack(IAsyncResult result)
    {
        tcpClient = result.AsyncState as TcpClient;
        if (tcpClient.Connected)
        {
            isConnected = true;
            tcpClient.EndConnect(result);
            stream = tcpClient.GetStream();
            readDataThread = new Thread(ReadDataThread);
            readDataThread.Start();
        }
    }
    //读取数据线程
    private void ReadDataThread()
    {
        try
        {
            while (isConnected)
            {
                byte[] buffer = new byte[1024];
                int length = stream.Read(buffer, 0, buffer.Length);
                string data = Encoding.UTF8.GetString(buffer, 0, length);
                queue.Enqueue(data);
            }
        }
        catch(Exception error)
        {
            throw new Exception(error.ToString());
        }
    }
    //程序退出时关闭线程
    private void OnApplicationQuit()
    {
        stream?.Close();
        connectThread?.Abort();
        readDataThread?.Abort();
    }

    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="content"></param>
    public void SendData(string content)
    {
        byte[] buffer = Encoding.UTF8.GetBytes(content);
        stream.Write(buffer, 0, buffer.Length);
    }
}

[Serializable]
public class SimpleData
{
    /// <summary>
    /// 图片数据
    /// </summary>
    public string pic;
    /// <summary>
    /// 字符内容
    /// </summary>
    public string content;
}

Create an empty object and mount the Client script for it:

Run the Unity program, go back to the server console window, and you can see that we have successfully connected to the server:

We find an image, send the image and character data to the server test, put it in the Assets directory, and read the data of this image through code:

 

Example code, hang it on the same object as the Client script:

using System;
using System.IO;
using UnityEngine;
using LitJson;

public class Foo : MonoBehaviour
{
    private void OnGUI()
    {
        if (GUILayout.Button("发送数据", GUILayout.Width(200f), GUILayout.Height(50f)))
        {
            var bytes = File.ReadAllBytes(Application.dataPath + "/pic.jpg");
            SimpleData simpleData = new SimpleData()
            {
                pic = Convert.ToString(bytes),
                content = "这是一张汽车图片"
            };
            //使用LitJson序列化
            string data = JsonMapper.ToJson(simpleData);
            GetComponent<Client>().SendData(data);
        }
    }
}

 Run the program, click the send data button, and go back to the server console to see that we have received the data:

The above is an example of sending data from the client to the server. Next, we try to send data from the server to the client:

The server places the picture in the solution as shown in the figure, and we read the picture data through the code:

We send data to the client when the client accesses, so we will write it in the Client constructor for the time being:

/// <summary>
/// 构造函数
/// </summary>
/// <param name="tcpClient"></param>
/// <param name="server"></param>
public Client(TcpClient tcpClient, Server server)
{
	this.server = server;
	this.tcpClient = tcpClient;
	//启动线程 读取数据
	Thread thread = new Thread(TcpClientThread);
	thread.Start();

	byte[] bytes = File.ReadAllBytes("pic.jpg");
	SimpleData simpleData = new SimpleData()
	{
		pic = Convert.ToBase64String(bytes),
		content = "这是一张图片"
	};
	string data = JsonMapper.ToJson(simpleData);
	Send(data);
}

In the client, we have stored the data sent by the server in the queue, so the data is taken out from the queue in turn:

private void Update()
{
    if (queue.Count > 0)
    {
        string data = queue.Dequeue();
        //使用LitJson反序列化
        SimpleData simpleData = JsonMapper.ToObject<SimpleData>(data);
        byte[] bytes = Convert.FromBase64String(simpleData.pic);
        //将图片存到Assets目录
        File.WriteAllBytes(Application.dataPath + "/test.jpg", bytes);
        //打印字符内容
        Debug.Log(simpleData.content);
    }
}

 

 

  Welcome to the public account "Contemporary Wild Programmer"

Guess you like

Origin blog.csdn.net/qq_42139931/article/details/123750597