【问题标题】:TCP Server not receiving properly when data is dynamicaly typed动态输入数据时 TCP 服务器无法正确接收
【发布时间】:2016-05-11 12:43:36
【问题描述】:

我正在开发一个简单的 TCP 服务器应用程序,它从流中接收数据并将其存储在一个列表中。

当每个连接以一条消息发送数据时,我已成功获取数据。我的问题似乎是在命令行使用 telnet 对其进行测试时。我将开始打字,程序将根据我的打字速度抓取一两个字符,然后不会再从该流中接收任何内容。我真的不知道为什么。如果我在 stream.DataAvailable 循环中放置一个 thread.sleep,那么它将获得更多字符,但又会停止。任何帮助表示赞赏。我的班级如下:

public class TCPServer
{
    private TcpListener listener;

    public List<string> messages;

    public TCPServer(IPAddress ip, int port)
    {
        try
        {
            messages = new List<string>();

            listener = new TcpListener(ip, port);
            listener.Start();

            listener.BeginAcceptTcpClient(ClientConnected, listener);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
    }

    private void ClientConnected(IAsyncResult ar)
    {

        TcpClient client;
        try
        {
            client = listener.EndAcceptTcpClient(ar);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
            return;
        }

        try
        {
            NetworkStream stream = client.GetStream();
            string message = "";

            while (!stream.DataAvailable)
            {
            }

            while (stream.DataAvailable)
            {                 
                byte[] readBytes = new byte[65536];
                stream.Read(readBytes, 0, readBytes.Length);
                message += Encoding.ASCII.GetString(readBytes).TrimEnd('\0');
            }

            if (message != null)
            {
                messages.Add(message);
                message = "";
            }
            listener.BeginAcceptTcpClient(ClientConnected, listener);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error: " + ex.Message);
        }
    }

    public void Stop()
    {
        listener.Stop();
    }

}

【问题讨论】:

    标签: c# tcp .net-4.0 tcpclient networkstream


    【解决方案1】:

    阅读循环非常不正确。

    您误解了DataAvailable 没有告诉您将到达多少字节。它为您提供了一个下限,并且可能随时说 0。循环将提前中止。使用DataAvailable 几乎总是不正确的。

    这无济于事,而且是一个忙碌的等待,应该与你一起提出一个危险信号:

            while (!stream.DataAvailable)
            {
            }
    

    正确的阅读方式是调用Read。您需要使用来自Read 的返回值来查看已到达多少字节。修剪零是不正确的(您不能以这种方式接收零),并且再次应该引发心理危险信号的黑客攻击!这不是正确的做法。

    套接字很难正确处理。不要进行可疑的黑客攻击,这只会增加非确定性失败的机会。

    【讨论】:

    • 如果这不是正确的方法(我相信),你能展示正确的方法吗?这并没有真正回答问题。
    • 嗯,它回答了为什么它不起作用。您可能应该尝试设置一个读取循环。如果您的协议是基于行的,您可以尝试 StreamReader.ReadLine。在网络上搜索套接字教程(不包含 DataAvailable :) 一词)。此处无需重复该内容。
    • DataAvailable 是 NetworkStreams 中的布尔值,而不是整数值,usr。因此,检查它是真还是假肯定会起作用。 OP 甚至从未在检查要接收多少字节的上下文中使用它。 Socket.DataAvailableNetworkStream.DataAvailable 是有区别的。
    • 它可能会错误地返回 false 导致您停止阅读。如果您针对零进行测试,则 bool 和 int 版本是等效的。如果你理解这个问题,我不确定你是否理解,你会发现他的阅读循环可能会错误地提前中止。根据您所做的具体操作,检查 DataAvailable 可能是安全的,但始终没有必要。根据网络速度和时间,您也可能侥幸逃脱,除非很少见,否则不会出现故障。你可以称其为幸运,我称其为不幸,因为漏洞存在并且最终会发生。 @VisualVincent
    • 我已经使用检查NetworkStream.DataAvailable 的应用程序发送了 700 MB 的文件,并且所有数据都已正确发送和接收;)。此外,根据MSDN Documentation Socket.DataAvailable 实际上确实表明当前有多少数据可供读取(已在网络缓冲区中排队)。所以它不会表示所有数据,而是它目前拥有的数据。所以我不明白为什么Socket.DataAvailable 不好用?
    【解决方案2】:

    所以我设法通过更多地研究 NetworkStream 类来解决这个问题。在 BeginReceiveTcpClient 的回调中,我从 tcpclient 中提取了网络流,因此我可以访问 BeginReceive 异步方法。相关代码如下:

     private void ClientConnected(IAsyncResult ar)
        {
            byte[] readBytes = new byte[65536];
    
            TcpClient client;
            try
            {
                client = listener.EndAcceptTcpClient(ar);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
                return;
            }
    
            try
            {
                NetworkStream stream = client.GetStream();
                stream.BeginRead(buffer, 0, buffer.Length, ReadStream, stream);
    
                listener.BeginAcceptTcpClient(ClientConnected, listener);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
        }
    
        private void ReadStream(IAsyncResult ar)
        {
            try
            {
    
                NetworkStream stream = (NetworkStream)ar.AsyncState;
                int bytesRead = stream.EndRead(ar);
    
                content += Encoding.ASCII.GetString(buffer, 0, bytesRead);
                dataProcessor.ProcessData(messages);
                stream.BeginRead(buffer, 0, buffer.Length, ReadStream, stream);
    
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-14
      • 2020-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多