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

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

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

一、服务端搭建:

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

新建Server类与Client类:

代码如下:


  
  1. using System.Net;
  2. using System.Net.Sockets;
  3. namespace CoderZ
  4. {
  5. public class Server
  6. {
  7. //端口
  8. private const int port = 8008;
  9. //客户端列表
  10. private List<Client> clients = new List<Client>();
  11. private static void Main(string[] args)
  12. {
  13. Console.WriteLine("服务端启动...");
  14. Server server = new Server();
  15. server.Init();
  16. }
  17. //服务端初始化
  18. private void Init()
  19. {
  20. TcpListener listener = new TcpListener(IPAddress.Any, port);
  21. listener.Start();
  22. try
  23. {
  24. while (true)
  25. {
  26. Console.WriteLine("等待客户端接入...");
  27. TcpClient client = listener.AcceptTcpClient();
  28. Client clientInstance = new Client(client, this);
  29. clients.Add(clientInstance);
  30. Console.WriteLine($"{client.Client.RemoteEndPoint}接入.");
  31. }
  32. }
  33. catch(Exception error)
  34. {
  35. throw new Exception(error.ToString());
  36. }
  37. }
  38. /// <summary>
  39. /// 广播:向所有客户端发送数据
  40. /// </summary>
  41. /// <param name="data"></param>
  42. public void Broadcast(string data)
  43. {
  44. for (int i = 0; i < clients.Count; i++)
  45. {
  46. clients[i].Send(data);
  47. }
  48. }
  49. /// <summary>
  50. /// 移除客户端
  51. /// </summary>
  52. /// <param name="client"></param>
  53. public void Remove(Client client)
  54. {
  55. if (clients.Contains(client))
  56. {
  57. clients.Remove(client);
  58. }
  59. }
  60. }
  61. }

  
  1. using System.Text;
  2. using System.Net.Sockets;
  3. namespace CoderZ
  4. {
  5. public class Client
  6. {
  7. private Server server;
  8. private TcpClient tcpClient;
  9. private NetworkStream stream;
  10. /// <summary>
  11. /// 构造函数
  12. /// </summary>
  13. /// <param name="tcpClient"></param>
  14. /// <param name="server"></param>
  15. public Client(TcpClient tcpClient, Server server)
  16. {
  17. this.server = server;
  18. this.tcpClient = tcpClient;
  19. //启动线程 读取数据
  20. Thread thread = new Thread(TcpClientThread);
  21. thread.Start();
  22. }
  23. private void TcpClientThread()
  24. {
  25. stream = tcpClient.GetStream();
  26. //使用固定长度
  27. byte[] buffer = new byte[1024];
  28. try
  29. {
  30. while (true)
  31. {
  32. int length = stream.Read(buffer, 0, buffer.Length);
  33. if (length != 0)
  34. {
  35. string data = Encoding.UTF8.GetString(buffer, 0, length);
  36. //解包
  37. Unpack(data);
  38. }
  39. }
  40. }
  41. catch(Exception error)
  42. {
  43. Console.WriteLine(error.ToString());
  44. }
  45. finally
  46. {
  47. server.Remove(this);
  48. }
  49. }
  50. //拆包:解析数据
  51. private void Unpack(string data)
  52. {
  53. }
  54. /// <summary>
  55. /// 发送数据
  56. /// </summary>
  57. /// <param name="data"></param>
  58. public void Send(string data)
  59. {
  60. byte[] buffer = Encoding.UTF8.GetBytes(data);
  61. stream.Write(buffer, 0, buffer.Length);
  62. }
  63. }
  64. }

数据的解析我们这里使用LitJson.dll工具,没有该工具的可以联系我发一份,打开视图/解决方案资源管理器:

右键解决方案/添加/项目引用:

点击浏览,找到LitJson工具,点击确定进行引用:

有了LitJson后我们便可以进行数据的解析,但是我们还没有定义任何数据结构,我们想要传输的数据包括图片和字符,因此这里定义如下数据结构:


  
  1. [Serializable]
  2. public class SimpleData
  3. {
  4. /// <summary>
  5. /// 图片数据
  6. /// </summary>
  7. public string pic;
  8. /// <summary>
  9. /// 字符内容
  10. /// </summary>
  11. public string content;
  12. }

引入LitJson命名空间后,解析数据:


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

此时运行我们的服务端:

二、Unity客户端搭建:

创建Client类,继承自MonoBehaviour,同时定义与服务端一致的数据结构:


  
  1. using System;
  2. using System.Text;
  3. using UnityEngine;
  4. using System.Threading;
  5. using System.Net.Sockets;
  6. using System.Collections.Generic;
  7. public class Client : MonoBehaviour
  8. {
  9. private string ipAddress;
  10. private int port;
  11. private bool isConnected;
  12. private Thread connectThread;
  13. private Thread readDataThread;
  14. private TcpClient tcpClient;
  15. private NetworkStream stream;
  16. //将数据存于队列 依次取出
  17. private Queue<string> queue = new Queue<string>();
  18. private void Start()
  19. {
  20. connectThread = new Thread(ConnectThead);
  21. connectThread.Start();
  22. }
  23. //连接线程
  24. private void ConnectThead()
  25. {
  26. tcpClient = new TcpClient();
  27. tcpClient.BeginConnect(ipAddress, port, ConnectThreadCallBack, tcpClient);
  28. float waitTime = 0f;
  29. while (!isConnected)
  30. {
  31. Thread.Sleep(500);
  32. waitTime += Time.deltaTime;
  33. if (waitTime > 3f)
  34. {
  35. waitTime = 0f;
  36. throw new Exception("连接超时");
  37. }
  38. }
  39. }
  40. private void ConnectThreadCallBack(IAsyncResult result)
  41. {
  42. tcpClient = result.AsyncState as TcpClient;
  43. if (tcpClient.Connected)
  44. {
  45. isConnected = true;
  46. tcpClient.EndConnect(result);
  47. stream = tcpClient.GetStream();
  48. readDataThread = new Thread(ReadDataThread);
  49. readDataThread.Start();
  50. }
  51. }
  52. //读取数据线程
  53. private void ReadDataThread()
  54. {
  55. try
  56. {
  57. while (isConnected)
  58. {
  59. byte[] buffer = new byte[1024];
  60. int length = stream.Read(buffer, 0, buffer.Length);
  61. string data = Encoding.UTF8.GetString(buffer, 0, length);
  62. queue.Enqueue(data);
  63. }
  64. }
  65. catch(Exception error)
  66. {
  67. throw new Exception(error.ToString());
  68. }
  69. }
  70. //程序退出时关闭线程
  71. private void OnApplicationQuit()
  72. {
  73. stream?.Close();
  74. connectThread?.Abort();
  75. readDataThread?.Abort();
  76. }
  77. /// <summary>
  78. /// 发送数据
  79. /// </summary>
  80. /// <param name="content"></param>
  81. public void SendData(string content)
  82. {
  83. byte[] buffer = Encoding.UTF8.GetBytes(content);
  84. stream.Write(buffer, 0, buffer.Length);
  85. }
  86. }
  87. [Serializable]
  88. public class SimpleData
  89. {
  90. /// <summary>
  91. /// 图片数据
  92. /// </summary>
  93. public string pic;
  94. /// <summary>
  95. /// 字符内容
  96. /// </summary>
  97. public string content;
  98. }

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

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

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

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


  
  1. using System;
  2. using System.IO;
  3. using UnityEngine;
  4. using LitJson;
  5. public class Foo : MonoBehaviour
  6. {
  7. private void OnGUI()
  8. {
  9. if (GUILayout.Button("发送数据", GUILayout.Width(200f), GUILayout.Height(50f)))
  10. {
  11. var bytes = File.ReadAllBytes(Application.dataPath + "/pic.jpg");
  12. SimpleData simpleData = new SimpleData()
  13. {
  14. pic = Convert.ToString(bytes),
  15. content = "这是一张汽车图片"
  16. };
  17. //使用LitJson序列化
  18. string data = JsonMapper.ToJson(simpleData);
  19. GetComponent<Client>().SendData(data);
  20. }
  21. }
  22. }

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

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

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

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


  
  1. /// <summary>
  2. /// 构造函数
  3. /// </summary>
  4. /// <param name="tcpClient"></param>
  5. /// <param name="server"></param>
  6. public Client(TcpClient tcpClient, Server server)
  7. {
  8. this.server = server;
  9. this.tcpClient = tcpClient;
  10. //启动线程 读取数据
  11. Thread thread = new Thread(TcpClientThread);
  12. thread.Start();
  13. byte[] bytes = File.ReadAllBytes("pic.jpg");
  14. SimpleData simpleData = new SimpleData()
  15. {
  16. pic = Convert.ToBase64String(bytes),
  17. content = "这是一张图片"
  18. };
  19. string data = JsonMapper.ToJson(simpleData);
  20. Send(data);
  21. }

客户端中我们已经将服务端发送的数据存于队列中,因此从队列中将数据依次取出:


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

 

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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