untiy 中的UDP和TCP通信 获取客户端的IP

本文使用udpclient和tcpclient,而非使用socket实例

命名空间

using System.Net;
using System.Net.Sockets;

TCP
TCP的核心对象为 TcpListener和TcpClient,前者用于服务器监听连接请求,并创建TcpClient对象,当需要通信时,直接使用创建的TcpClient调用Send,Receive方法即可

监听连接: TCP需要进行连接,因此需要一个连接监听器,服务器监听客户端连接如下

    /// <summary>
    /// TCP连接请求监听者
    /// </summary>
    private TcpListener tcpListener;
   
    private void Awake()
    {       
        //开启连接监听的线程,必须另开一个线程启动监听,如果在主线程里直接调用,主线程会阻塞
        tcpListenerT = new Thread(ListenConnect);
        tcpListenerT.IsBackground = true;
        tcpListenerT.Start();
    }

   //监听连接请求的方法
    private void ListenConnect()
    {
        //声明监听者的IP为任意可用`在这里插入代码片`
        tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 10086));
        tcpListener.Start(100);//监听请求 最多连接100个
        while (true)
        {
            //这一步会阻塞线程,不能在主线程中调用
            TcpClient client = tcpListener.AcceptTcpClient();  
            //获取客户端的IP          
            string ip = (client.Client.RemoteEndPoint as IPEndPoint).Address.ToString();                      
        }
    }

客户端连接服务器:

 private TcpClient tcpClient = new TcpClient();
 private void Start()
 {
     //参数为服务器的地址,和服务器的TcpListener绑定的端口
     tcpClient.Connect("192.168.1.36", 10086);
 }

发送消息:

    public void SendMsg(TcpClient client,string msg)
    {
         byte[] buff = Encoding.UTF8.GetBytes(msg);
         client.Send(buff);
    }

接收消息:

    private TcpClient tcpClient = new TcpClient();

    private void Start()
    {
        //连接到服务器
        tcpClient.Connect("192.168.1.36", 10086);
        //接收消息的方法必须在子线程中执行
        tcpReciveT = new Thread(Recciveimage);
        tcpReciveT.IsBackground = true;
        tcpReciveT.Start();
    }

    /// <summary>
    /// 接收字节数组
    /// </summary>
    private void Recciveimage()
    {
        //声明接收缓冲区的长度
        byte[] receiveBuff = new byte[1024];
        int reviceLength = 0;
        while (true)
        {
            try
            {
                //返回值为接收到的字节的数量 这一步会阻塞线程,决不能在主线程中执行
                reviceLength = tcpClient.Client.Receive(receiveBuff);
                //必须指定转换的字节数组的长度,因为缓冲区剩余的空位会用0填充,这些0我们并不需要
                msg = Encoding.UTF8.GetString(receiveBuff,0,reviceLength);
                isReveive = true;
                print("收到的消息 "+msg);
            }
        }
    }

UDP
UDP相对简单,UDP并不需要连接,核心对象为UdpClinent和IPEndPoint

UdpClient即UDP的对象,用于收发消息

IPEndPoint可以理解为Udp通信的端点,解决三个问题 我是谁?我要发到哪里去?我从哪里接收?
声明UdpClient时,需要指定UdpClient的IPEndPoint,即,我是谁
通过UdpClient发送消息时,需要指定目的地的IPEndPoint,即,我要发到哪里去
通过UdpClient接收消息时,需要指定监听的IPEndPoint,即,我从哪里收

IPEndPoint常用的构造有以下两种,都需要我们指定IP和端口,在构造函数中使用IPAddress类指定IP

//IP为当前可用IP,或任意IP  端口为0表示任何端口, 这种声明通常在接收消息中使用
new IPEndPoint(IPAddress.Any, 0)
//指定一个IP,255.255.255.255为群发  端口为40000  指定确定的IP端口通常用于声明UdpClient和发送消息
new IPEndPoint(IPAddress.Parse("192.168.1.36"), 40000)

发送

    //公共的消息发送中心
    private UdpClient udpClient;
    private  void Awake()
    {        
        //为UdpClient绑定本机的IP和端口  我是谁
        udpClient = new UdpClient(new IPEndPoint(IPAddress.Any, 40000));
    }
    
    /// <summary>
    /// 消息发送  IPEndPoint声明 IPEndPoint = new IPEndPoint(IPAddress.Parse("192.168.1.36"),10000)
    /// </summary>
    public void CommonendMsgSender(string msg, IPEndPoint commonIPEndPoint)
    {
        byte[] commonBuff = Encoding.UTF8.GetBytes(msg);
        udpClient.Send(commonBuff, commonBuff.Length, commonIPEndPoint);
    }

监听

    private UdpClient CommonUdpClient;
    // 公共的消息接收端口
    private IPEndPoint commonReceiveIPEndPoint;
    // 公共的消息线程
    Thread commonReceiveThread;
    // 公共消息的缓存
    private byte[] commonMsgBuff;

    private void Start()
    {
        //公共消息接收端口
        commonReceiveIPEndPoint = new IPEndPoint(IPAddress.Any, 0);
        //启动监听线程,和TCP一样,监听会阻塞线程,不能在主线程中监听消息
        commonReceiveThread = new Thread(ListenCommonMsg);
        commonReceiveThread.IsBackground = true;
        commonReceiveThread.Start();
    }
    // 监听公共消息
    private void ListenCommonMsg()
    {
        while (true)
        {
            //这里会阻塞线程
            commonMsgBuff = CommonUdpClient.Receive(ref commonReceiveIPEndPoint);
            string temp = Encoding.UTF8.GetString(commonMsgBuff);
            print("公共消息为" + temp);
        }
    }
    

注意事项
1 监听操作必须在子线程中执行,不要在主线程中执行,否则主线程会被阻塞
2 重复监听消息应该使用死循环
3 子线程可以操作主线程的简单属性,如果 int bool str,但不能直接操作Uinty对象如Text等,如果需要在接收消息后改变场景内容 ,在一个主线程的脚本中的Update里,使用一个bool制作一把锁,将子线程收到的消息,赋值给主线程脚本的string变量,并将锁置为true,使用这个脚本来控制Uinty对象

   //子线程接到消息后,置为true
    public bool isReceive = false;
    /// <summary>
    /// 子线程接到消息后,将消息赋值到这个变量
    /// </summary>
    public string msg;

void Update()
    {
        if (isReceive)
        {
            //处理msg
            if(msg == "1")
            {
                //逻辑
            }
        }
    }
上一篇:有了TCP ,为什么还需要UDP?


下一篇:TCP/UDP协议