C# UDP数据包丢失,尽管所有数据包都到达(WireShark)

4
正如标题所述,我在C#中使用UDP遇到了问题。 我正在尝试构建一个适用于游戏DayZ的rcon协议的库。
我的问题是,我没有收到应该接收到的每个数据包。 发送命令后,服务器会回复一个分割的答案。 数据包头包含总数据包数和当前数据包的索引。 如果我应该获得17个数据包,则我的应用程序只会收到8-15个数据包。
经过使用WireShark测试,我现在知道所有数据包都到达了我的计算机。 它们只是没有被我的应用程序识别或类似的东西。
我的实际问题是:是否有可能防止在我的网络卡和应用程序之间丢失数据包? 或者为什么会发生这种情况?
以下是我的当前代码。 它相当混乱,因为它没有按预期工作而被拆开:
    private Socket _udpClient;
    private Thread _receiverThread;
    private Thread _workerThread;
    private Queue<byte[]> _packetQueue;
    private PacketBuffer[] MessageBuffer;
    private byte SenderSequence = 0;
    private IPEndPoint connection;

    public RCon(IPAddress ip, int port)
    {
        connection = new IPEndPoint(ip, port);
        _udpClient = new Socket(connection.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
        _udpClient.Connect(connection);
        MessageBuffer = new PacketBuffer[256];
        _packetQueue = new Queue<byte[]>();

        _receiverThread = new Thread(new ThreadStart(ReceiveCallback));
        _receiverThread.IsBackground = true;
        _receiverThread.Priority = ThreadPriority.AboveNormal;
        _receiverThread.Start();
        _workerThread = new Thread(new ThreadStart(WorkerCallback));
        _workerThread.IsBackground = true;
        _workerThread.Start();
    }

    public void Login(string password)
    {
        LoginPacket packet = new LoginPacket(password);

        _udpClient.Send(packet.Bytes);
    }

    public void SendCommand(string command)
    {
        CommandPacket packet = new CommandPacket(SenderSequence, command);
        SenderSequence++;

        _udpClient.Send(packet.Bytes);
    }

    private void ReceiveCallback()
    {

        while (true)
        {
                byte[] buffer = new byte[1036];
                if (_udpClient.Receive(buffer) > 0)
                    _packetQueue.Enqueue(buffer);
        }
    }

    private void WorkerCallback()
    {
        while (true)
        {
            if (_packetQueue.Count > 0)
            {
                byte[] buffer = _packetQueue.Dequeue();

                if (buffer != null)
                {
                    try
                    {
                        Packet receivedPacket = Packet.ParseIncoming(buffer);

                        OnPacketReceived(new PacketReceivedEventArgs(receivedPacket));

                        switch (receivedPacket.Type)
                        {
                            case PacketType.Message:
                                OnMessageReceived(new MessageReceivedEventArgs(receivedPacket.Content));
                                MessageCallbackPacket packet = new MessageCallbackPacket(receivedPacket.SequenceNumber);
                                _udpClient.Send(packet.Bytes);
                                break;
                            case PacketType.CommandCallback:
                                if (MessageBuffer[receivedPacket.SequenceNumber] == null)
                                    MessageBuffer[receivedPacket.SequenceNumber] = new PacketBuffer(receivedPacket);
                                else
                                    MessageBuffer[receivedPacket.SequenceNumber].AddPacket(receivedPacket);

                                if (MessageBuffer[receivedPacket.SequenceNumber].IsComplete)
                                    OnCommandCallback(new CommandCallbackEventArgs(MessageBuffer[receivedPacket.SequenceNumber].GetContent()));
                                break;
                        }
                    }
                    catch (ArgumentException) { }
                    catch (OverflowException) { }
                    catch (FormatException) { }
                }
            }
        }
    }

1
我总是对那些本来很聪明的人不知道“loose”和“lose”不是同一个词感到惊讶。 - Jimmy D
1
感谢您帮助我提高英语水平。 - Swarley
_udpClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, a big value like 0x40000); - L.B
1个回答

4

通常情况下,这是因为您没有足够快地消耗数据报,导致内核套接字缓冲区已满,网络栈开始丢弃新到达的数据包。一些要点:

  • 增加套接字的接收缓冲区大小,
  • 不要在每次迭代中获取锁 - 尽可能多地读取数据,然后将数据放入队列中,
  • 考虑非阻塞方法而不是线程。

增加接收缓冲区对我有用。非阻塞是指BeginReceive和EndReceive,对吗?我以前用过它,但为了测试目的切换到了线程。谢谢你的帮助! - Swarley
不,我的意思是非阻塞套接字和放弃线程。.NET异步回调在线程池中运行。 - Nikolai Fetissov
我们从哪里可以确认接收缓冲区是实际的问题?我正在使用默认缓冲区大小为8 KB的WS 2016,并将缓冲区大小增加到2 MB,但在我的情况下没有任何改变。 - Girish
@Girish,请检查Socket.Available属性,并确保它远小于缓冲区大小。 - gog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接