服务器和客户端发送和接收的 UDP SocketAsyncEventArgs 不匹配数据

UDP SocketAsyncEventArgs mismatching data sent and received by server and client

我正在尝试学习游戏网络编程,所以我使用 Unity 开始了一个非常简单的异步 udp 套接字系统,但是当我添加 JSON 序列化时,事情变得有点奇怪。

我可以连接到服务器并使用 SendPacket(string msg) 方法发送数据包,它会在服务器端正常接收。只要 msg 变量的大小相同或更大,它就可以正常工作。因此,如果我发送一个字符串“Hello”,然后发送一个“Hello World”就可以正常工作,并且能够将其打印在其他尺寸上。但是,如果我现在要发送另一个带有字符串“Hi”的数据包,则什么也不会发生,并且该套接字上将不再有连接,也不会发送新数据包。

我已经检查过,我正在通过网络接收数据包,它不是空的,它有信息,但是当它到达代码时它似乎停止了:

Packet p = e.Buffer.FromJsonBinary<Packet>();

我服务器上的 OnConnect 函数 side.After 那个函数似乎我的服务器停止监听不接受新连接并且不会收到其他数据包,我怀疑它以某种方式停止了我的异步进程。

对于 Json 序列化,我使用 JsonUnity 的实用程序。

知道发生了什么事吗?

服务器Class

public class Server
{
    private static string _protocolID = "hash";
    private static ushort _port = 11000;
    private Socket _socket;

    private SocketAsyncEventArgs _event;
    private List<Socket> _connections = new List<Socket>();
    
    public void Start()
    {
        Listen();
    }

    private bool Listen()
    {
        // Create UDP Socket
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        // Set the socket into non-blocking mode 
        _socket.Blocking = false;

        try
        {
            _socket.Bind(new IPEndPoint(IPAddress.Any, _port));
            _event = new SocketAsyncEventArgs();
            _event.Completed += OnConnect;

            byte[] buffer = new byte[1024];
            _event.SetBuffer(buffer, 0, 1024);

            
            //_socket.ReceiveAsync(_event);
            StartListening(_event);
            

        }
        catch (Exception e)
        {
            Debug.LogException(e);
            return false;
        }

        return true;
    }

    private void StartListening(SocketAsyncEventArgs e)
    {
        //e.AcceptSocket = null;
        _socket.ReceiveAsync(e);
        
    }
    private void OnConnect(object sender, SocketAsyncEventArgs e)
    {
        if (e.BytesTransferred > 0)
        {
            if (e.SocketError != SocketError.Success)
                Debug.Log("ERROR"); // TODO: Close Socket

            Packet p = e.Buffer.FromJsonBinary<Packet>();
            if (p._protocolID != _protocolID)
                Debug.Log("Protocol Error");

            Debug.Log("Connect:" + p._msg);

            if (!_socket.ReceiveAsync(e))
            {
                // Call completed synchonously
                StartListening(e);
            }
        }
        else
        {
            Debug.Log("No data");
        }
    }
}

客户Class

public class Client
{
    private static string _protocolID = "hash";
    private ushort _port = 11000;
    private string _ip = "127.0.0.1";
    private Socket _socket;
    private SocketAsyncEventArgs _event;

    public bool Connect()
    {

        // Create UDP Socket
        _socket = new Socket(SocketType.Dgram, ProtocolType.Udp);

        // Set the socket into non-blocking mode 
        _socket.Blocking = false;
        IPEndPoint ip = new IPEndPoint(IPAddress.Parse(_ip), _port);

        _event = new SocketAsyncEventArgs();
        //_event.Completed += Callback;
       

        try 
        {
            _socket.Connect(ip);
            Debug.Log($"Connection was to {_ip} was sucessfull");
        } 
        catch(Exception e)
        {
            Debug.LogException(e);
            Debug.Log("Couldn't connect Socket");
            return false;
        }

        return true;
    }

    public void SendPacket<T>(T t)
    {
        try
        {
            if (_socket == null)
                Debug.Log("Null socket");

            // Encode the data string into a byte array.  
            byte[] buffer = t.ToJsonBinary();
            _event.SetBuffer(buffer, 0, buffer.Length);

            // Send the data through the socket.  
            //_socket.SendAsync(_event);

            bool willRaiseEvent = _socket.SendAsync(_event);
            //if (!willRaiseEvent)
            //{
            //    SendPacket<T>(t);
            //}
        }
        catch (SocketException e)
        {a
            Debug.LogException(e);
            Debug.Log("Couldn't Send Packet");
        }
    }

    public void SendPacket(string msg)
    {
        Packet packet = new Packet(_protocolID, msg);
        SendPacket<Packet>(packet);
    }
}

Json连载

    public static byte[] ToJsonBinary(this object obj)
    {
        return Encoding.ASCII.GetBytes(JsonUtility.ToJson(obj));
    }

    public static T FromJsonBinary<T>(this byte[] data)
    {
        return JsonUtility.FromJson<T>(Encoding.ASCII.GetString(data));
    }

数据包

[Serializable]
public struct Packet
{
    public string _protocolID;
    public string _msg;

    public Packet(string protocolID, string msg)
    {
        _protocolID = protocolID;
        _msg = msg;
    }
}

编辑:我发现它因 Json 实用程序

而崩溃
  System.ArgumentException: JSON parse error: The document root must not follow by other values.

Json string before being sent (top), Json string reiceived at the server (bottom)

服务器上的缓冲区似乎没有被清除,因此其中仍有信息。

好的,看来问题出在 SocketAsyncEventArgs 的工作方式上。由于我递归调用并传递相同的事件,因此它永远不会清除它的缓冲区。

 if (!_socket.ReceiveAsync(e))
       {
            OnConnect(null, e);
       }

我找到了两个解决方案,一个是传递一个新的 SocketAsyncEventArgs,它将有一个清晰的缓冲区。但我相信更好的主意是创建一个池来处理和重用一组预定义的 SocketAsyncEventArgs,而不是不断创建新的。

我找到的另一个解决方案是简单地设置一个新缓冲区作为清除最后一个缓冲区的方法。

byte[] buffer = t.ToJsonBinary();
_event.SetBuffer(buffer, 0, buffer.Length);