Unity【Socket TCP】- 服务端与客户端通讯的简单示例

举报
CoderZ1010 发表于 2022/09/25 04:20:43 2022/09/25
2.7k+ 0 0
【摘要】 应粉丝需求做一个服务端与客户端通讯的示例,需求比较简单,我们使用Socket TCP协议去构建,直接使用固定长度信息法。 一、服务端搭建: 打开Visual Studio,文件/新建/项目,创建一个控制台应用: 新建Server类与Client类: 代码如下: using System.Net;using Syst...

应粉丝需求做一个服务端与客户端通讯的示例,需求比较简单,我们使用Socket TCP协议去构建,直接使用固定长度信息法。

一、服务端搭建:

打开Visual Studio,文件/新建/项目,创建一个控制台应用:

新建Server类与Client类:

代码如下:


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

创建一个空物体为其挂载Client脚本:

运行Unity程序,回到服务端控制台窗口,可以看到我们已经成功与服务端连接:

我们找一张图片,将图片和字符数据发送给服务端测试,将它放到Assets目录中,我们通过代码读取这张图片的数据:

示例代码,将其与Client脚本挂在同一物体上:


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

 运行程序点击发送数据按钮,回到服务端控制台查看可以看见我们已经接收到数据:

上面是客户端发送数据到服务端的示例,下面我们尝试从服务端发送数据到客户端:

服务端将图片放于解决方案中如图所示位置,我们通过代码读取图片数据:

我们在客户端接入的时候将数据发送给客户端,因此就暂且将其写在Client构造函数里:


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

 

文章来源: coderz.blog.csdn.net,作者:CoderZ1010,版权归原作者所有,如需转载,请联系作者。

原文链接:coderz.blog.csdn.net/article/details/123750597

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

作者其他文章

评论(0

抱歉,系统识别当前为高风险访问,暂不支持该操作

    全部回复

    上滑加载中

    设置昵称

    在此一键设置昵称,即可参与社区互动!

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

    *长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。