【问题标题】:c# networking strangeness时间:2019-05-01 标签:c#networking
【发布时间】:2012-02-16 11:10:46
【问题描述】:

我正在开发一个小型网络服务器,并且正在使用最新版本的 MonoDevelop。

我有一个方法,PlayerReadLoop,它传递了一个普通的 Player 对象,也传递了一个 tcpClient 对象。该方法尚未完成,并且不依赖于它所传递的 Player 对象,因此无需提供任何有关 player 的信息。 TcpClient 肯定已正确初始化,因为我已经通过该 tcpClient 成功发送消息。

方法如下:

private void playerReadLoop(Player p, TcpClient c)
{
    byte[] message = new byte[4096];
    int bytesRead;
    NetworkStream clientStream = c.GetStream();
    while (true)
    {
        bytesRead = 0;
        try
        {
            bytesRead = clientStream.Read(message, 0, 4096);
        }
        catch
        {
            Console.WriteLine("ERROR");
            break;
        }
        if (bytesRead == 0)
        {
            Console.WriteLine("Disconnected");
            break;
        }

        ASCIIEncoding encoder = new ASCIIEncoding();
        Console.WriteLine(encoder.GetString(message, 0, bytesRead));

    }
}

由于某种原因,上面的代码不起作用!你看,

当我包围两条线时:

SCIIEncoding encoder = new ASCIIEncoding();
Console.WriteLine(encoder.GetString(message, 0, bytesRead));

无论我的客户端是否发送消息,clientStream.Read 都会通过并返回零字节,表示断开连接。我的客户根本没有发送任何东西,当然也没有断开连接。

当两行没有标注时,clientStream.read 永远不会通过。我的客户是否发送消息。

现在,如果这还不够奇怪,在我的 tcpClient 初始化之后直接使用上面的代码(使用与初始化 tcpClient 相同的方法),代码可以工作!我没有对 tcpClient 做任何事情,我只是将它直接传递给上面的函数,并带有一个使用 tcpClient 初始化的 Player 对象。 player 对象不对 tcpClient 做任何事情,它只是将它存储在它的一个变量中。我不明白将这段代码放在它自己的方法中的简单行为是如何导致行为上如此令人讨厌的差异的!

客户端代码很简单:

TcpClient client = new TcpClient();

IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 21255);

client.Connect(serverEndPoint);

NetworkStream clientStream = client.GetStream();

ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Server!");

clientStream.Write(buffer, 0 , buffer.Length);
clientStream.Flush();

【问题讨论】:

  • “当我将两条线包围起来时:[...] 带有符号”是什么意思?
  • 第 1 行;第2行;变成 /*line1; line2;*/
  • “我的客户端根本没有发送任何东西,当然也没有断开连接。”...但是服务器呢?您如何确定该连接的服务器端没有断开您的连接。在网络代码故障排除期间运行 Wireshark 是必须的。

标签: c# networking


【解决方案1】:

来源; http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.flush.aspx

在网络流上调用 Flush 没有任何效果,因此它不应该在您的代码中开始。

读取和写入操作通常是套接字上的阻塞操作。因此,他们将等到操作完全完成。在读取和写入网络流之前,您最好检查它们是否真的可以写入或读取。有它的属性。 (更多信息http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.write.aspx

另外,因为 Read 可以返回 0,这并不意味着客户端“断开连接”,如您的代码中所写。这只是意味着接收缓冲区上还没有可供读取的字节。这也意味着默认情况下 Read 会一直阻塞,直到实际发送数据。

如果您没有收到数据,请检查客户端和服务器上的端口和连接设置,以便它们匹配。

编辑 仅仅因为您在网络流上写了一些东西,并不意味着它会直接通过网络发送。您的代码和实际套接字之间有一个缓冲区。无法控制此缓冲区,只有 Windows 控制它。这就是为什么在您的代码和实际发送之间有时会有延迟。

它还解释了为什么某些设置或代码配置似乎没有直接影响。此信息被很好地隐藏,并且不在 MSDN 上,但如果您深入研究 Microsoft 的有关套接字通信的技术网文档,您会发现更多解释这一点的有趣内容。很遗憾,我目前无法分享链接。

【讨论】:

  • 奇怪的是,当代码在我执行的不同位置时,它可以完美运行——所以我肯定知道连接信息是正确的,检查零字节似乎会导致读取阻塞直到发送了一些东西,或者客户端断开连接,所以,不知道。然后是试图从流中读取以某种方式阻止它断开连接的事实!这是非常奇怪的行为,但我会尝试检查等待流准备好
  • 当没有要读取的内容时,读取流返回 0 字节是正确的!出于某种原因,现在一切正常,我添加了 while 循环来等待流激活,尽管它们还没有真正被调用。
  • 编辑了我的帖子,给你一个可能的解释。
【解决方案2】:

我正在开发一个小型网络服务器

你遇到过开源c#网络通信库networkComms.net吗?它与即插即用一样好,因此您可以立即启动并运行。另外,您遇到的大部分问题都已经解决了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 2012-11-01
    相关资源
    最近更新 更多