Unity 与C#服务器 Socket的UDP通信

版权声明:辛辛苦苦写的,转载请注明出处噢 https://blog.csdn.net/wangjiangrong/article/details/81034069

前言

这几天简单的研究了下Unity和服务器有关Socket的UDP通信,因为帧同步当中,UDP相比TCP是更适合的那种,因为UDP在网络环境较差时,波动更小,实时性也更高,可以更好的解决卡顿的问题(具体的小伙伴们可自行查阅资料)。当然使用UDP造成的开发量与开发难度会更大。在这里,我就先记录下自己这几天实现的udp简单的客户端与服务器的发送与接收功能的demo代码。


注意

1.本文是接着之前Socket通信的demo继续的,所以有些用的的东西可能在上一篇文章当中,大家需要的可以看一下:https://blog.csdn.net/wangjiangrong/article/details/80844392

2.相对tcp的强链接,udp是弱连接,客户端用udp给服务器发消息的时候,不需要和tcp那样先建立连接(connect)再发送,而是直接向服务器地址发送消息即可。类似我们发短信,知道对方手机号即可直接发送,而不用关心对方是否开机等信息。

3.目前研究下来,有两种写法,一种是用Socket对象,另一种是用UdpClient对象。两种写法我都试了,其中UdpClient可以成功的实现客户端和服务器的接收和发送,但是Socket只实现了客户端像服务器发送消息且服务器成功接收,但是客户端接收服务器消息则遇见了问题还没解决,有大佬懂的请指教一下。(研究下来是,Socket调用ReceiveFrom方法接收前,需要调用Bind方法,但是调用了Bind方法之后,SendTo方法那就报错了,因为SendTo方法不能调用前Bind。)

Socket.ReceiveFrom 方法:https://msdn.microsoft.com/zh-cn/library/7t7fzd26(v=vs.110).aspx

Socket.SendTo 方法:https://msdn.microsoft.com/zh-cn/library/1h691da7(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2

4.现在的代码只实现了一对一的通信,若要一对多,小伙伴们可以先研究研究多加些逻辑判断,后续补充有时间。

代码

下面就直接上代码了,大致思路就是,客户端和服务器都建立好Socket,并且服务器建立一个定时器用来给客户端定时发消息,,然后客户端像服务器发送消息,当服务器接收到消息的时候就开始向客户端定时发送消息。

因为没有连接,所以服务器也不知道客户端什么时候停止向客户端发送消息,向客户端发送消息的定时器需要何时停止,所以我目前的思路是,客户端要停止发送消息的时候可以给服务器发送一个指定消息,当服务器接收到指定消息的时候就停止再向客户端的消息发送,关掉定时器,这段功能暂时没有加。

首先是UdpClient写法的:

UdpClient

服务器代码

先上用一个类来存放常量

namespace Tool {
    class SocketDefine {
        public const int port = 8078;//端口号
        public const string ip = "127.0.0.1";
    }
}

然后建立一个单例类来管理UdpClient

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

namespace Tool {
    class UdpManager : SingleClass<UdpManager> {

        UdpClient m_udpClient;
        IPEndPoint m_clientIpEndPoint;//存放客户端地址
        Thread m_connectThread;//接收客户端消息的线程
        byte[] m_result = new byte[1024];//存放接收到的消息

        bool m_isStartSend;//接收到客户端消息的时候才可以发送

        //初始化
        public void Start() {
            m_isStartSend = false;
            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(SocketDefine.ip), SocketDefine.port);
            m_udpClient = new UdpClient(ipEnd);

            //定义客户端
            m_clientIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
            Console.WriteLine("等待连接数据");
            //开启一个线程连接
            m_connectThread = new Thread(new ThreadStart(Receive));
            m_connectThread.Start();

            System.Timers.Timer t = new System.Timers.Timer(3000);//实例化Timer类,设置间隔时间为10000毫秒
            t.Elapsed += new System.Timers.ElapsedEventHandler(SendToClient);//到达时间的时候执行事件
            t.AutoReset = true;//设置是执行一次(false)还是一直执行(true)
            t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件
        }

        public void SendToClient(object source, System.Timers.ElapsedEventArgs e) {
            if(m_isStartSend) {
                Send("server msg 111");
            }
        }

        public void Send(string data) {
            try {
                Console.WriteLine("send to  " + m_clientIpEndPoint + "  " + data);
                NetBufferWriter writer = new NetBufferWriter();
                writer.WriteString(data);
                m_udpClient.Send(writer.Finish(), writer.finishLength, m_clientIpEndPoint);
            } catch(Exception ex) {
                Console.WriteLine("send error   " + ex.Message);
            }
        }

        //服务器接收
        void Receive() {
            while(true) {
                try {
                    m_result = new byte[1024];
                    m_result = m_udpClient.Receive(ref m_clientIpEndPoint);
                    NetBufferReader reader = new NetBufferReader(m_result);
                    string data = reader.ReadString();
                    Console.WriteLine(m_clientIpEndPoint + "  数据内容:{0}", data);
                    m_isStartSend = true;
                } catch(Exception ex) {
                    Console.WriteLine("receive error   " + ex.Message);
                    Close();
                }
            }
        }

        //连接关闭
        void Close() {
            m_isStartSend = false;
            //关闭线程
            if(m_connectThread != null) {
                m_connectThread.Interrupt();
                m_connectThread.Abort();
            }

            if(m_udpClient != null) {
                m_udpClient.Close();
                m_udpClient.Dispose();
            }
            Console.WriteLine("断开连接");
        }
    }
}

最后我们在程序主入口调用即可

using System;
using Tool;

namespace MyApp {

    class Program {

        static void Main(string[] args) {
            UdpManager.instance.Start();
            Console.ReadLine();
        }
    }
}
客户端代码

因为所有的通信都涉及连接,发送,接收,断开连接(当然udp不存在连接,所以连接和断开连接只是做一些初始化和清理操作),所以定义一个接口ISocket如下

namespace Tool {

    public interface ISocket {

        //是否已连接的标识
        bool IsConnected();

        //连接指定IP和端口的服务器
        void Connect(string ip, int port);

        //发送数据给服务器
        void SendMessage(string data);

        //断开连接
        void Disconnect();

        //接收服务器端Socket的消息
        void RecieveMessage();
    }
}

然后一样是我们的客户端UdpClient管理单例类,这里需要注意一点的是UdpClient初始化的时候UdpClient(0)端口要设置为0,不然后面接收的时候会报错提供了一个无效的参数

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

namespace Tool {

    public class UdpManager : SingleClass<UdpManager>, ISocket {

        public delegate void OnGetReceive(string message);//接收到消息的委托
        public OnGetReceive onGetReceive;

        byte[] m_result = new byte[1024];

        Thread m_connectThread;
        UdpClient m_udpClient;
        IPEndPoint m_serverIPEndPoint;
        IPEndPoint m_recieveIPEndPoint;

        bool m_isConnected;

        //是否已连接的标识  
        public bool IsConnected() {
            return m_isConnected;
        }

        //udp是无连接的,所以方法里只做了一些初始化操作
        public void Connect(string ip, int port) {
            m_serverIPEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            m_udpClient = new UdpClient(0);//!!!!!主要一定要设置port为0,不然无法接收到服务器的消息
            m_isConnected = true;

            m_recieveIPEndPoint = new IPEndPoint(IPAddress.Any, 0);

            //开启一个线程连接
            m_connectThread = new Thread(new ThreadStart(RecieveMessage));
            m_connectThread.Start();
        }

        public void SendMessage(string data) {
            if(IsConnected()) {
                NetBufferWriter writer = new NetBufferWriter();
                Debug.Log("SendMessage  " + data);
                writer.WriteString(data);
                m_udpClient.Send(writer.Finish(), writer.finishLength, m_serverIPEndPoint);
            }
        }

        public void Disconnect() {
            if(IsConnected()) {
                m_isConnected = false;
            }
            if(m_connectThread != null) {
                m_connectThread.Interrupt();
                m_connectThread.Abort();
            }

            if(m_udpClient != null) {
                m_udpClient.Close();
            }
        }

        public void RecieveMessage() {
            while(IsConnected()) {
                try {
                    m_result = new byte[1024];
                    //m_udpClient的port不是0的时候,会报错,无效参数
                    m_result = m_udpClient.Receive(ref m_recieveIPEndPoint);
                    NetBufferReader reader = new NetBufferReader(m_result);
                    string msg = reader.ReadString();
                    Debug.Log("RecieveMessage   " + msg);
                } catch(Exception e) {
                    Debug.Log("recieve error    " + e.Message);
                    Disconnect();
                }
            }
        }
    }
}

最后我们调用UdpManager.instance.Connect("127.0.0.1", 8078);和UdpManager.instance.SendMessage(content);即可。

效果图如下:



Socket

代码也贴一下,又需要的朋友可以看看,基本类似,使用方法也一样。

服务器代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace Tool {

    class UdpSocketManager : SingleClass<UdpSocketManager> {

        Socket m_serverSocket;
        EndPoint m_clientEndPoint;
        byte[] m_result = new byte[1024];
        int m_resultLength;
        Thread m_connectThread;
        bool m_isStartSend;

        public void Start() {
            m_isStartSend = false;
            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(SocketDefine.ip), SocketDefine.port);
            m_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            m_serverSocket.Bind(ipEnd);

            //定义客户端
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            m_clientEndPoint = (EndPoint)sender;
            Console.WriteLine("等待连接数据");

            //开启一个线程连接
            m_connectThread = new Thread(new ThreadStart(Receive));
            m_connectThread.Start();

            System.Timers.Timer t = new System.Timers.Timer(1000);//实例化Timer类,设置间隔时间为10000毫秒
            t.Elapsed += new System.Timers.ElapsedEventHandler(SendToClient);//到达时间的时候执行事件
            t.AutoReset = true;//设置是执行一次(false)还是一直执行(true)
            t.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件
        }

        public void SendToClient(object source, System.Timers.ElapsedEventArgs e) {
            if(m_isStartSend) {
                Send("server msg 111");
            }
        }

        public void Send(string data) {
            try {
                Console.WriteLine("send to  " + m_clientEndPoint + "  " + data);
                NetBufferWriter writer = new NetBufferWriter();
                writer.WriteString(data);
                m_serverSocket.SendTo(writer.Finish(), SocketFlags.None, m_clientEndPoint);
            } catch(Exception ex) {
                Console.WriteLine("send error   " + ex.Message);
            }
        }

        //服务器接收
        void Receive() {
            while(true) {
                try {
                    m_result = new byte[1024];
                    m_resultLength = m_serverSocket.ReceiveFrom(m_result, ref m_clientEndPoint);
                    NetBufferReader reader = new NetBufferReader(m_result);
                    string data = reader.ReadString();
                    Console.WriteLine(m_clientEndPoint + " 数据内容:{0}", data);
                    m_isStartSend = true;
                } catch(Exception ex) {
                    Console.WriteLine("receive error   " + ex.Message);
                }
            }
        }

        //连接关闭
        public void Close() {
            m_isStartSend = false;
            //关闭线程
            if(m_connectThread != null) {
                m_connectThread.Interrupt();
                m_connectThread.Abort();
            }
            //最后关闭socket
            if(m_serverSocket != null) {
                m_serverSocket.Close();
            }
            Console.WriteLine("断开连接");
        }
    }
}

客户端代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using UnityEngine;

namespace Tool {

    public class UdpSocketManager : SingleClass<UdpSocketManager>, ISocket {

        Socket m_clientSocket;
        EndPoint serverEnd;
        IPEndPoint m_ipEndPoint;

        Thread m_connectThread;
        byte[] m_result = new byte[1024];
        int m_resultLength = 0;
        bool m_isConnected;

        public UdpSocketManager() {
            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        }

        public bool IsConnected() {
            return m_isConnected;
        }

        IPEndPoint endPoint;

        public void Connect(string ip, int port) {
            m_ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            m_isConnected = true;

            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            serverEnd = (EndPoint)sender;

            //RecieveMessage需要bind  https://msdn.microsoft.com/zh-cn/library/7t7fzd26(v=vs.110).aspx

            //string ip4 = "";
            //IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());   //Dns.GetHostName()获取本机名Dns.GetHostAddresses()根据本机名获取ip地址组
            //foreach(IPAddress ipp in ips) {
            //    if(ipp.AddressFamily == AddressFamily.InterNetwork) {
            //        ip4 = ipp.ToString();
            //    }
            //}
            //IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(ip4), 11000);
            //m_clientSocket = new Socket(endPoint.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
            //m_clientSocket.Bind(endPoint);

            //m_connectThread = new Thread(new ThreadStart(RecieveMessage));
            //m_connectThread.Start();
        }

        public void Disconnect() {
            if(IsConnected()) {
                m_isConnected = false;
            }
            if(m_connectThread != null) {
                m_connectThread.Interrupt();
                m_connectThread.Abort();
            }

            if(m_clientSocket != null) {
                m_clientSocket.Close();
            }
        }

        //udp不需要先于对方建立连接关系,直接向对方ip发送消息即可
        public void SendMessage(string data) {
            if(IsConnected()) {
                Debug.Log("SendMessage  " + data);
                NetBufferWriter writer = new NetBufferWriter();
                writer.WriteString(data);
                m_clientSocket.SendTo(writer.Finish(), SocketFlags.None, m_ipEndPoint);
            }
        }

        public void RecieveMessage() {
            while(IsConnected()) {
                try {
                    m_result = new byte[1024];
                    m_resultLength = m_clientSocket.ReceiveFrom(m_result, ref serverEnd);

                    NetBufferReader reader = new NetBufferReader(m_result);
                    string ss = reader.ReadString();
                    Debug.Log("RecieveMessage   " + ss);
                } catch(Exception ex) {
                    Debug.Log("RecieveMessage error " + ex.Message);
                    Disconnect();
                    break;
                }
            }
        }
    }
}


猜你喜欢

转载自blog.csdn.net/wangjiangrong/article/details/81034069