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

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

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

image.gif

一、服务端搭建:

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

image.gif

image.gif

新建Server类与Client类:

image.gifimage.gif编辑代码如下:

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

image.gif

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

image.gif

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

image.gif

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

image.gif

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

image.gif

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

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

image.gif

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

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

image.gif

此时运行我们的服务端:

image.gif

image.gif

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

image.gif

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

image.gif

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

image.gif

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

image.gif

示例代码,将其与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);
        }
    }
}

image.gif

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

image.gif

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

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

image.gif

我们在客户端接入的时候将数据发送给客户端,因此就暂且将其写在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);
}

image.gif

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

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

image.gif

image.gif

目录
相关文章
|
缓存 监控 Java
Java Socket编程最佳实践:优化客户端-服务器通信性能
【6月更文挑战第21天】Java Socket编程优化涉及识别性能瓶颈,如网络延迟和CPU计算。使用非阻塞I/O(NIO)和多路复用技术提升并发处理能力,减少线程上下文切换。缓存利用可减少I/O操作,异步I/O(AIO)进一步提高效率。持续监控系统性能是关键。通过实践这些策略,开发者能构建高效稳定的通信系统。
566 1
|
7月前
|
网络协议 API
区分TCP/IP、HTTP、Socket三者的差异
HTTP关注于应用层的协议规范,而Socket关注于为应用程序提供编程中的网络功能,这些功能本身是建立在底层的TCP/IP协议之上;HTTP是更高层次的抽象,定义了如何包装数据,而TCP/IP定义了如何传送数据,Socket则是两者之间在程序中的桥梁,负责实现细节。在实际应用中,通常HTTP通信也是通过Socket来完成,因为HTTP仅是具体内容的封装形式,而Socket则是传送方式的实现形式。
634 16
|
7月前
|
网络协议 安全 API
WebSocket、Socket、TCP 和 HTTP 的差别与应用场景
WebSocket、Socket、TCP 和 HTTP 是网络通信中的四大“使者”,各具特色:HTTP 适合短时请求,TCP 稳定可靠,Socket 灵活定制,WebSocket 实现实时双向通信。本文用通俗语言解析它们的区别与应用场景,助你为项目选择最合适的通信方式。
2692 3
|
11月前
|
网络协议 开发者 Python
Socket如何实现客户端和服务器间的通信
通过上述示例,展示了如何使用Python的Socket模块实现基本的客户端和服务器间的通信。Socket提供了一种简单且强大的方式来建立和管理网络连接,适用于各种网络编程应用。理解和掌握Socket编程,可以帮助开发者构建高效、稳定的网络应用程序。
592 10
|
IDE Java 开发工具
从零开始学Java Socket编程:客户端与服务器通信实战
【6月更文挑战第21天】Java Socket编程教程带你从零开始构建简单的客户端-服务器通信。安装JDK后,在命令行分别运行`SimpleServer`和`SimpleClient`。服务器监听端口,接收并回显客户端消息;客户端连接服务器,发送“Hello, Server!”并显示服务器响应。这是网络通信基础,为更复杂的网络应用打下基础。开始你的Socket编程之旅吧!
307 3
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
513 0
|
安全 Java 网络安全
Java Socket编程教程:构建安全可靠的客户端-服务器通信
【6月更文挑战第21天】构建安全的Java Socket通信涉及SSL/TLS加密、异常处理和重连策略。示例中,`SecureServer`使用SSLServerSocketFactory创建加密连接,而`ReliableClient`展示异常捕获与自动重连。理解安全意识,如防数据截获和中间人攻击,是首要步骤。通过良好的编程实践,确保网络应用在复杂环境中稳定且安全。
365 0
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
820 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
JSON 数据格式 Python
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
本文介绍了如何使用Python的socket模块实现客户端到服务器端的文件传输,包括客户端发送文件信息和内容,服务器端接收并保存文件的完整过程。
768 1
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
536 1

热门文章

最新文章