C# 的UDP使用记录

背景

由于要写一个unrealmmorpg demo, 就用python写了服务器,回头一想,既然unreal的都已经写了,那就干脆把unity的也写了~然后在写unity的网络库udp的时候,遇到了以下几个问题,于是就记录了以下!

简单的基本代码(问题重现版)

这里只放出C#的代码(前端使用)

public override bool Connect(string url, int port, double timeout=5000){
    var MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    EndPoint RemoteAddress = new IPEndPoint(IPAddress.Parse(url), port);
    ThreadStart childref = new ThreadStart(this.ReceiveRun);
    receiveThread = new Thread(childref);
    receiveThread.Start();
    NetPackage package = new NetPackage();
    package.Head.Protocol = 1001;
    var bytes = package.Encode();
    MainSocket.SendTo(bytes, RemoteAddress);
}
private void ReceiveRun()
{
    while (IsLoop)
    {
        byte[] buffer = new byte[1024];
        if (Receive(ref buffer))
        {
            Debug.LogError("receive msg");
        }
    }
}
public override bool Receive(ref byte[] buffer)
{
    base.Receive(ref buffer);
    serverEnd = new IPEndPoint(IPAddress.Any, 0);
    int len = MainSocket.ReceiveFrom(buffer, ref serverEnd);
    return len > 0;
}

问题以及解决思路

上面的代码在运行的时候,直接抛出了以下的异常

SocketException: 提供了一个无效的参数。

System.Net.Sockets.Socket.ReceiveFrom (System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint& remoteEP) (at <743b6648c9264b5cabe5d91045f6e132>:0)
System.Net.Sockets.Socket.ReceiveFrom (System.Byte[] buffer, System.Net.EndPoint& remoteEP) (at <743b6648c9264b5cabe5d91045f6e132>:0)
WDFramework.UdpNetDriver.Receive (System.Byte[]& buffer) (at Assets/Framework/Runtime/Network/UdpNetDriver.cs:34)
WDFramework.NetDriver.ReceiveRun () (at Assets/Framework/Runtime/Network/NetDriver.cs:59)
System.Threading.ThreadHelper.ThreadStart_Context (System.Object state) (at <7b935204f5ff4bcab44b3b0ebca330bf>:0)

这里居然说ReceiveFrom有无效参数!!!总共就两个参数,所以我又把Receive函数改成下面这样!

public override bool Receive(ref byte[] buffer)
{
    base.Receive(ref buffer);
    byte[] recvBuffer =  new byte[1024];
    serverEnd = new IPEndPoint(IPAddress.Any, 0);
    int len = MainSocket.ReceiveFrom(recvBuffer, ref serverEnd);
    return len > 0;
}

结果运行的时候还是一样的报错!

然后我就跑去google以下,有人说要bind以下,然后我把Connect函数改了~

public override bool Connect(string url, int port, double timeout=5000){
    var MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    EndPoint RemoteAddress = new IPEndPoint(IPAddress.Parse(url), port);
    ThreadStart childref = new ThreadStart(this.ReceiveRun);
    receiveThread = new Thread(childref);
    receiveThread.Start();

    string ip4 = "";
    IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
    foreach (var ipp in ips)
    {
        if (ipp.AddressFamily == AddressFamily.InterNetwork)
            ip4 = ipp.ToString();
    }
    var localAddress = new IPEndPoint(IPAddress.Parse(ip4), 6001);
    MainSocket.Bind(localAddress);

    NetPackage package = new NetPackage();
    package.Head.Protocol = 1001;
    var bytes = package.Encode();
    MainSocket.SendTo(bytes, RemoteAddress);
}

结果更惨,有多了一个错误!说是SendTo的地址无效!

SocketException: 在其上下文中,该请求的地址无效。

System.Net.Sockets.Socket.SendTo (System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.Net.Sockets.SocketFlags socketFlags, System.Net.EndPoint remoteEP) (at <743b6648c9264b5cabe5d91045f6e132>:0)
System.Net.Sockets.Socket.SendTo (System.Byte[] buffer, System.Net.EndPoint remoteEP) (at <743b6648c9264b5cabe5d91045f6e132>:0)
WDFramework.UdpNetDriver.Connect (System.String url, System.Int32 port, System.Double timeout) (at Assets/Framework/Runtime/Network/UdpNetDriver.cs:38)
UILoginView.OnEnable () (at Assets/Scripts/Login/UILoginView.cs:16)
UnityEngine.GameObject:SetActive(GameObject, Boolean)
WDFramework.ViewProcessor:BindOrUnbindView() (at Assets/Framework/Runtime/UISystem/ViewProcessor.cs:170)
WDFramework.ViewProcessor:OnUpdate() (at Assets/Framework/Runtime/UISystem/ViewProcessor.cs:107)
WDFramework.UISystem:Update() (at Assets/Framework/Runtime/UISystem/UISystem.cs:162)

我就想是不是参数不完整导致,就试一下把SendTo的参数补全。

MainSocket.SendTo(bytes, bytes.Length, SocketFlags.None, RemoteAddress);

结果完全没有变化!

到此就只能再继续google了~

然后发现有人说要sendto后再监听receivefrom才不会有问题!然后我调整一些线程在sendto后再Start

结果:完全没有问题了!

再逐步吧之前的BindSendto补全删掉,都没有问题!

结论

udp的时候需要先SendTo后再使用线程receivefrom就没有问题!