Unity 网络连接协议总结

Tcp同步连接


Tcp同步连接介绍

Tcp协议作为稳定协议,在消息发送前必须完成客户端连接,且客户端连接在Tcp协议中只能是一对一的,即如果有ABC三个连接,那个A连接与B连接如果相互连接,则A与C之间则无法互相通信,只能由A接受到消息时创建出额外的D连接,然后由D与C相互通信

同步作为与异步区分的概念,同步即线程执行到发送或等待接受消息的指令时会进入阻塞状态,即暂停执行,直到接收到消息时,线程才会再次开始工作


Tcp客户端

tcp客户端介绍

客户端的概念是作为客机向主机连接的通信通道,客户端不需要绑定IP和端口,客户端在创建之后直接向服务器发送连接申请,成功连接后即可作为单向的消息的收发通道,客户端的代码中不需要存在自己的IP及端口号,只需要存在需要连接的主机和端口号即可

创建Tcp客户端

cs 复制代码
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

代码的功能为创建一个Tcp协议下的客户端

构造函数的第一个参数为采用IPv4的方式进行网络传输,保持不变即可,偶尔可用IPv6方式

构造函数的第二个参数为采用流的方式传递数据,保持不变即可

构造函数的第三个参数为使用tcp网络传输协议,保持不变即可

Tcp客户端连接服务器

cs 复制代码
socket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345));

代码的功能为连接服务器主机,这个方法只需要在客户端执行,服务器中不需要使用Connect连接功能,需要注意的是连接的Socket必须要是服务器中已经调用Bind和Listen的Socket

传入的参数为IPEndPoint,声明IpEndPoint则需要传入IpAddress和端口号,IpAddress代表Ip地址,IpAddress.Parse(string)方法为获取指定字符串所指向的IP地址,连接时必须指定特定IP,不能使用IpAddress,Any或其他,IpAddress和开放端口号组成IpEndPoint

Tcp客户端接收消息

cs 复制代码
byte [] message = new byte[1024];
socket.Receive(message);
string str = Encoding.UTF8.GetString(message);

连接服务器完成后,即可调用Receive方法开始接受,需要注意的是执行的Receive语句时,无论是主线程或是分支线程,都会开始阻塞(暂停执行),直到收到服务器消息为止,所以Receive语句需要写到线程当中,然后在主线程中开启分支线程,在分支线程中执行Receive语句

Receive方法中的参数为byte数组,使用前需要先声明并赋予任意初始值,在Receive方法执行完后byte数组中将包含从服务器中接受到的数据流,需要通过下面的Encoding.UTF8.GetString方法将byte数组传入并返回由数据流转换好的字符串

Tcp客户端发送消息

cs 复制代码
socket.Send(Encoding.UTF8.GetBytes("客户端发送内容"));

Send方法的参数类型为byte数组,功能是将参数中的byte数组数据流发送到服务器,Encoding.UTF8.GetBytes方法为将传入的String类型的参数专函为byte数组并返回,在这里直接将方法的返回值传入的Send方法的参数中


Tcp服务器

Tcp服务器介绍

tcp服务器并不需要特殊的系统或特殊的硬件

服务器相当于不主动发送连接请求而被动接受连接请求的主机端

与客户端不同的是客户端向服务器申请连接,而服务器则是被动等待客户端的申请

服务器必须要在声明时绑定固定的Ip地址和端口号,这样才能被客户端寻找到

服务器在接收到新的连接申请时,并不会由服务器与其进行连接,而是会生成一个本地客户端与访问服务器的客户端进行一对一连接,客户端发送消息时将发送到服务器生成的本地客户端上

创建Tcp服务器

cs 复制代码
Socket mainSocket;        
mainSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
mainSocket.Bind(new IPEndPoint(IPAddress.Any,12345));

tcp服务器的声明与客户端的声明相同,但是区别于客户端的方式是服务器必须调用Bind方法绑定固定的IP地址和端口,服务器在声明时IP地址可以传入IPAddress.Any,等同于传入"127.0.0.1"或者自己的Ipv4地址,12345代表服务器部署的端口

Tcp服务器开启监听连接请求

cs 复制代码
mainSocket.Listen(10);

服务器必须调用Listen代码之后,才能收到来自客户端的连接申请,Listen方法的参数为同时最多可连接到服务器的客户端数量,超出数量的客户端发送连接请求时将返回连接失败

Tcp监听连接请求并生成本地客户端

cs 复制代码
Socket socket = mainSocket.Accept();

服务器调用Accept方法后,无论是主线程还是分支线程,都将进入阻塞状态(暂停执行),直到收到连接请求后才会继续执行,所以需要将Accept方法写入到分支线程中,然后在主线程开启的分支线程中调用Accept方法.

Accept方法调用后,服务器将为接下来第一个连接进来的客户端生成一个一对一专属的通信客户端作为方法的返回值返回,客户端发送消息时将会由通信客户端进行通信,与服务器将无关

所以通过Accept生成的客户端需要利用List或Dictionary等方式储存起来,供发送信息时使用

Accept方法接受到连接申请并创建本地客户端后,需要新开线程,在线程中用Receive方法队本地客户端进行消息监听处理


客户端示例代码

代码仅供学习演示,未进行异常处理,仅供展示方法功能

cs 复制代码
using UnityEngine;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class SocketScript : MonoBehaviour
{
    Socket mainSocket;
    /// <summary>
    /// 声明客户端并连接服务器
    /// </summary>
    void Start()
    {
        mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        mainSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345));
        Thread thread = new Thread(Listen);
        thread.Start();
    }

    byte [] message = new byte[1024];
    /// <summary>
    /// 监听服务器消息并处理
    /// </summary>
    void Listen()
    {
        while (true)
        {
            mainSocket.Receive(message);
            Debug.Log(Encoding.UTF8.GetString(message));
        }
    }

    /// <summary>
    /// 客户端向服务器发送消息
    /// </summary>
    /// <param name="str"></param>
    void Send(string str)
    {
        mainSocket.Send(Encoding.UTF8.GetBytes(str));
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Send("客户端发送测试数据");
        }
    }
}

服务器示例代码

代码仅供学习演示,未进行异常处理,仅供展示方法功能

cs 复制代码
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;

public class SeverSocketScript : MonoBehaviour
{
    Dictionary<string,Socket> connectSockets = new Dictionary<string,Socket>();
    Socket mainSocket;
    /// <summary>
    /// 声明服务器并开启接受监听线程
    /// </summary>
    void Start()
    {
        mainSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
        mainSocket.Bind(new IPEndPoint(IPAddress.Any,12345));
        mainSocket.Listen(10);
        Thread thread = new Thread(CreatConnectSocket);
        thread.Start();
    }

    /// <summary>
    /// 接受监听后保存生成的通信客户端,并开启线程监听通信客户端消息
    /// </summary>
    void CreatConnectSocket()
    {
        while (true)
        {
            Socket socket = mainSocket.Accept();
            if (!connectSockets.ContainsKey(socket.RemoteEndPoint.ToString()))
            {
                connectSockets.Add(socket.RemoteEndPoint.ToString(), socket);
                Thread thrad = new Thread(() => { ListenNewConnectSocket(socket); });
                thrad.Start();
            }
        }
    }

    byte[] bytes = new byte[1024];
    /// <summary>
    /// 接受通信客户端消息并对消息进行处理
    /// </summary>
    /// <param name="socket"></param>
    void ListenNewConnectSocket(Socket socket)
    {
        while (true)
        {
            socket.Receive(bytes);
            Debug.Log("接受消息" + Encoding.UTF8.GetString(bytes));
            socket.Send(Encoding.UTF8.GetBytes("服务器收到测试数据"));
            Debug.Log("发送消息回执");
        }
    }
    
    /// <summary>
    /// 由服务器向全部客户端进行数据广播
    /// </summary>
    void Send()
    {
        foreach (var socket in connectSockets)
        {
            socket.Value.Send(Encoding.UTF8.GetBytes("服务器发送测试数据"));
            Debug.Log("服务器向" + socket.Key + "发送测试数据");
        }
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Send();
        }
    }
}
相关推荐
Elastic 中国社区官方博客37 分钟前
让我们把这个 expense 工具从 n8n 迁移到 Elastic One Workflow
大数据·运维·elasticsearch·搜索引擎·ai·信息可视化·全文检索
汤愈韬1 小时前
TK_网络基础和常见攻击(笔记)
网络·笔记
程序员佳佳1 小时前
2025年大模型终极横评:GPT-5.2、Banana Pro与DeepSeek V3.2实战硬核比拼(附统一接入方案)
服务器·数据库·人工智能·python·gpt·api
( •̀∀•́ )9201 小时前
GitHub Actions SSH 部署密钥
运维·ssh·github
louqle1 小时前
docker基本知识及常用命令汇总
运维·docker·容器
学烹饪的小胡桃2 小时前
【运维学习】实时性能监控工具 WGCLOUD v3.6.2 更新介绍
linux·运维·服务器·学习·工单系统
北邮刘老师2 小时前
【智能体互联协议解析】需要“智能体名字系统”(ANS)吗?
网络·人工智能·大模型·智能体·智能体互联网
叫致寒吧2 小时前
Docker
运维·docker·容器
白露与泡影3 小时前
使用systemd,把服务装进 Linux 心脏里~
linux·运维·python
照海19Gin3 小时前
【企业网架构实验解析】三层组网与 AC+AP 无线部署的协议逻辑(eNSP 仿真实践)
网络