Unity【Socket TCP】- 서버와 클라이언트 간 통신의 간단한 예

팬의 요구에 부응하여 서버와 클라이언트 간의 통신 예를 만들었습니다.요구 사항은 비교적 간단합니다.우리는 Socket TCP 프로토콜을 사용하여 고정 길이 메시지 방식을 구축하고 직접 사용합니다.

1. 서버 구축:

Visual Studio, File/New/Project를 열고 콘솔 애플리케이션을 만듭니다.

새 서버 클래스와 클라이언트 클래스를 만듭니다.

코드 쇼는 아래와 같습니다.

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);
        }
	}
}

데이터 구문 분석을 위해 여기에서 LitJson.dll 도구를 사용합니다. 도구가 없으면 저에게 연락하여 사본을 보내고 보기/솔루션 탐색기를 열 수 있습니다.

솔루션/추가/프로젝트 참조를 마우스 오른쪽 버튼으로 클릭합니다.

찾아보기를 클릭하고 LitJson 도구를 찾은 다음 확인을 클릭하여 다음을 참조하십시오.

LitJson을 사용하여 데이터를 구문 분석할 수 있지만 아직 데이터 구조를 정의하지 않았습니다.전송하려는 데이터에는 그림과 문자가 포함되므로 다음 데이터 구조를 여기에 정의합니다.

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

LitJson 네임스페이스를 도입한 후 데이터를 구문 분석합니다.

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

이제 서버를 실행합니다.

둘째, Unity 클라이언트 구성:

Client 클래스를 만들고 MonoBehaviour에서 상속하고 서버와 일치하는 데이터 구조를 정의합니다.

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;
}

빈 개체를 만들고 이에 대한 클라이언트 스크립트를 마운트합니다.

Unity 프로그램을 실행하고 서버 콘솔 창으로 돌아가면 서버에 성공적으로 연결되었음을 확인할 수 있습니다.

이미지를 찾고, 이미지와 문자 데이터를 서버 테스트로 보내고, Assets 디렉토리에 넣고, 코드를 통해 이 이미지의 데이터를 읽습니다.

 

예제 코드, 클라이언트 스크립트와 동일한 개체에 걸기:

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);
        }
    }
}

 프로그램을 실행하고 데이터 보내기 버튼을 클릭한 다음 서버 콘솔로 돌아가서 데이터를 받았는지 확인합니다.

위는 클라이언트에서 서버로 데이터를 보내는 예제이고, 다음으로 서버에서 클라이언트로 데이터를 보내려고 합니다.

서버는 그림과 같이 솔루션에 그림을 배치하고 코드를 통해 그림 데이터를 읽습니다.

클라이언트가 액세스할 때 클라이언트에 데이터를 보내므로 당분간 클라이언트 생성자에 작성합니다.

/// <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);
}

클라이언트에서는 서버가 보낸 데이터를 대기열에 저장했으므로 데이터는 대기열에서 차례로 제거됩니다.

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);
    }
}

 

 

  공개 계정 "Contemporary Wild Programmer"에 오신 것을 환영합니다.

추천

출처blog.csdn.net/qq_42139931/article/details/123750597