【问题标题】:C# Tcp is losing packets?C# Tcp 正在丢失数据包?
【发布时间】:2014-02-24 21:53:45
【问题描述】:

我编写了一个 C# 聊天软件,它使用了一个新的(至少对我而言)系统,我称之为请求系统。我不知道以前是否已经创建过,但现在我认为它是我的创作:P

无论如何,这个系统是这样工作的:

  1. soc收到信号
  2. 检查信号
  3. 如果刚刚收到的数据是数字2,客户端软件就知道服务器要发送聊天消息了。如果数字是3,则客户端知道服务器即将发送成员列表,依此类推。

问题是这样的:当我在 VS2012 中逐步执行时,它工作正常,聊天工作正常。当我在调试模式下使用它或只是在我的桌面上运行它时,似乎缺少数据,这不应该是因为代码运行良好......

客户端收发消息代码示例:

    public void RecieveSystem()
    {
        while (true)
        {
            byte[] req = new byte[1];
            soc.Receive(req);
            int requestID = int.Parse(Encoding.UTF8.GetString(req));
            if (requestID == 3)
            {
                byte[] textSize = new byte[5];
                soc.Receive(textSize);
                byte[] text = new byte[int.Parse(Encoding.UTF8.GetString(textSize))];
                soc.Receive(text);
                Dispatcher.Invoke(() => { ChatBox.Text += Encoding.UTF8.GetString(text) + "\r\n"; });
            }
        }
    }

    public void OutSystem(string inputText)
    {
        byte[] req = Encoding.UTF8.GetBytes("3");
        soc.Send(req);
        byte[] textSize = Encoding.UTF8.GetBytes(Encoding.UTF8.GetByteCount(inputText).ToString());
        soc.Send(textSize);
        byte[] text = Encoding.UTF8.GetBytes(inputText);
        soc.Send(text);
        Thread.CurrentThread.Abort();
    }

在服务器上:

    public void UpdateChat(string text)
    {
        byte[] req = Encoding.UTF8.GetBytes("3");
        foreach (User user in onlineUsers)
            user.UserSocket.Send(req);
        byte[] textSize = Encoding.UTF8.GetBytes(Encoding.UTF8.GetByteCount(text).ToString());
        foreach (User user in onlineUsers)
            user.UserSocket.Send(textSize);
        byte[] data = Encoding.UTF8.GetBytes(text);
        foreach (User user in onlineUsers)
            user.UserSocket.Send(data);
    }
           public void RequestSystem(Socket soc)
        {
~~~
                    }
                    else if (request == 3)
                    {
                        byte[] dataSize = new byte[5];
                        soc.Receive(dataSize);
                        byte[] data = new byte[int.Parse(Encoding.UTF8.GetString(dataSize))];
                        soc.Receive(data);
                        UpdateChat(Encoding.UTF8.GetString(data));
                    }
                }
                catch
                {
                    if (!soc.Connected)
                    {
                        Dispatcher.Invoke(() => { OnlineMembers.Items.Remove(decodedName + " - " + soc.RemoteEndPoint); Status.Text += soc.RemoteEndPoint + " Has disconnected"; });
                        onlineUsers.Remove(user);
                        Thread.CurrentThread.Abort();
                    }
                }
            }
        }

可能是什么问题?

【问题讨论】:

  • 服务器代码示例中的“~~~”部分是什么?这是一个不重要的代码还是你错过了什么?
  • 你可能对我的文章here感兴趣,尤其是题为“意识流-他说了很多,但没有真正意义!”的部分!
  • TCP 不在 C# 中,TCP 不会丢包。
  • @aleksey.berezan 这是一个不重要的代码 :) EJP 我知道,我从来没有说过它在 C# 中......而且我知道它不会丢失数据包,这就是为什么我感到困惑并认为出了点问题。我的代码现在可以工作,但我还有另一个问题。如果你能在那里帮助我,我会很高兴:) stackoverflow.com/questions/22022162/…

标签: c# sockets tcp chat


【解决方案1】:

您假设每次Send 调用都会收到一个数据包。那不是面向流的——那是面向数据包的。您正在发送我怀疑已合并为一个数据包的多条数据,然后您将在一个 Receive 调用中将它们全部获取。 (即使涉及多个数据包,单个Receive 调用仍然可以接收所有数据。)

如果您使用的是 TCP/IP,您应该以更加面向流的方式进行思考。我还鼓励您更改协议的设计,这至少可以说很奇怪。在每条消息之前使用长度前缀很好,但是当两台计算机之间的二进制连接非常好时,为什么还要将其编码为文本呢?

我建议您查看BinaryReaderBinaryWriter:使用TcpClientTcpListener 而不是Socket(或至少使用NetworkStream),并使用读取器/写入器对使其更容易读取和写入数据片段(有效负载或原语,如消息长度)。 (BinaryWriter.Write(string) 甚至为您执行长度前缀,这让事情变得容易多了。)

【讨论】:

  • 伙计,你很快!你的答案,嗯,我可以改变我的协议\套接字类型或面向数据包的东西吗?流是如何工作的?
  • @user3348638:你可以使用 UDP 来代替,但这会打开另外一罐蠕虫病毒。有关更面向流的方法,请参阅我的最后一段。
  • @user3348638 UDP 是面向数据包的,但不要被它看似简单的机制所诱惑;不得不处理重复/无序/丢失的数据包并不好玩。并不是说 TCP 可以保证这一点,但对于大多数应用程序来说,它是非常罕见的。
  • @Mr.Smith:我认为 TCP 实际上 确实 保证了排序、没有重复和没有丢失的数据包。我认为这就是重点...(显然数据包可能go丢失,但它们会自动重新发送,您无需担心。如果连接完全断开,您应该会收到错误消息而不是继续处理不良数据。)
  • 等等,TCP 不是保证我发送的所有数据都会按正确的顺序接收吗?我现在很困惑。 TCP 是否适合聊天?和你谈到的流,如果我理解正确的话,它会在客户端和服务器之间创建一个流,你需要通过 System.IO 或 BinaryWriter 中的 Stream 类写入它(我真的不知道)。我不认为我想要一个流,但是别的东西,我不知道如何调用它 - 每次只需向客户端发送一个数据片段并关闭或其他东西。简单将是最好的。有没有类似的?
猜你喜欢
  • 1970-01-01
  • 2018-09-04
  • 1970-01-01
  • 2020-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
相关资源
最近更新 更多