【问题标题】:TcpClient stream write doesn't apply until stream/connection is closedTcpClient 流写入在流/连接关闭之前不适用
【发布时间】:2017-02-13 21:33:56
【问题描述】:

我正在尝试制作一个简单的 TCP 程序。我可以让客户端连接到服务器,但是当我的客户端调用stream.Write时,服务器不会读取发送的数据,直到客户端关闭连接。

除此之外,当我的客户端尝试读取服务器的响应时,客户端会抛出一个 IOException。 (“连接方一段时间后没有正确响应,或者连接的主机没有响应,建立连接失败。”)

服务器代码:

listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream ns = client.GetStream();
//Wait for message
MemoryStream ms = new MemoryStream();
ns.CopyTo(ms);//server will hang here until connection is closed, then receive correct data

客户端代码:

client.Connect(endpt);
client.GetStream().Write(encodedMessage, 0, encodedMessage.Length);
NetworkStream ns = client.GetStream();
MemoryStream ms = new MemoryStream();
ns.CopyTo(ms); //client crashes here

【问题讨论】:

  • 你先运行服务器?
  • @VasilutLucian 是的

标签: c# tcp tcpclient


【解决方案1】:

不,服务器读取数据就好了。只是在您关闭连接之前流不会结束 - CopyTo 方法无法知道您希望它在某个时候停止读取。请记住,TCP 不发送消息,它维护一个双向字节流。如果需要消息,则需要在 TCP 之上构建消息协议。

同样的事情发生在客户端,所以你基本上有一个死锁 - 客户端在读取整个流之前无法关闭连接,而服务器在读取整个流之前无法关闭连接(实际上,它甚至不接受任何 new 连接,因为您在一个线程上完成所有这些操作)。但同样,TCP 流仅在连接关闭时结束。所以两人将永远等待对方。

不过,我当然不会崩溃。您是否尝试同时将两个客户端连接到同一台服务器?这不适用于您当前的代码,并且会导致像您这样的超时。问题是连接仍在服务器的队列中等待,并且在之前的连接关闭之前您不会调用AcceptTcpClient(同样,CopyTo 死锁)。

网络是相当困难的。我当然建议您使用经过良好测试、精心设计的通信框架,而不是使用您自己的基于 TCP 的协议。像 WCF 或 Lidgren 这样的东西可能会有所帮助。如果您真的想进行自己的基于 TCP 的通信,则需要进行大量学习-我真的很想推荐一个好的资源,但是我还没有找到 C#/.NET 的资源,所以远的。我已经在Networking 上开始了一些示例,但这远非生产就绪代码,而且相当不完整。它将向您展示如何在 C# 中构建异步 TCP 服务器的基本思想,以及如何实现简单的 HTTP 样式或消息样式的基于 TCP 的协议。

【讨论】:

    【解决方案2】:

    你可以试试这样的:

        //Server Part
        public void Start()
        {
            Console.WriteLine("Server started...");
    
            TcpListener listener = new TcpListener(System.Net.IPAddress.Loopback, 1234);
            listener.Start();
            while (true)
            {
                TcpClient client = listener.AcceptTcpClient();
                new Thread(new ThreadStart(() =>
                {
                    HandleClient(client);
                })).Start();
            }
        }
    
      private void HandleClient(TcpClient client)
        {
            NetworkStream stream = client.GetStream();
            StreamWriter writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true };
            StreamReader reader = new StreamReader(stream, Encoding.ASCII);
    
            string inputLine = reader.ReadLine();
            Console.WriteLine("The client with name " + " " + inputLine + " is conected");
    
        }
    
    
    //Client Part
     public void InitClient()
      {
            client = new TcpClient("localhost", 1234);
            stream = client.GetStream();
            writer = new StreamWriter(stream) { AutoFlush = true };
            reader = new StreamReader(stream);
      }
    
      public void SendMessage(string userName)
      {
         writer.WriteLine(userName);
      }
    
    
     //And here are the type of the variable that are used:
        TcpClient client;
        NetworkStream stream;
        StreamWriter writer;
        StreamReader reader;
    

    希望对您有所帮助!

    【讨论】:

      【解决方案3】:

      我通过在服务器上使用 AcceptSocket 而不是 AcceptTcpClient 并使用 Socket Receive 函数解决了这个问题。

      客户端现在也读入一个数组,而不是使用 CopyTo 函数。

      新的服务器代码:

      listener.Start();
      Socket socket = listener.AcceptSocket();
      byte[] b = new byte[999];
      socket.Receive(b);
      

      新客户端代码:

      client.Connect(endpt);
      client.GetStream().Write(encodedMessage, 0,encodedMessage.Length);
      NetworkStream ns = client.GetStream();
      byte[] bb = new byte[999];
      ns.Read(bb, 0, 999);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-12-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多