NetworkStream在客户端/服务器TCP通信期间脏

本文关键字:通信 TCP 服务器 客户端 NetworkStream | 更新日期: 2023-09-27 18:20:15

我有一个客户端和一个服务器通过TCP进行通信。客户端使用NetworkStream将信息发送到服务器,服务器将读取该信息,然后该过程继续进行,直到用户想要退出并关闭连接。问题是NetworkStream由于以前的写入而变脏。因此,假设客户端在第一次发送字符串"aa",在第二次发送"b"。第二次读取时,服务器将获得"ba"。这里少了什么?服务器读取期间不应该消耗NetworkStream吗?这是相关代码。。。

服务器

        while (true)
        {
            try
            {
                NetworkStream clientStream = tcpClient.GetStream();
                bytesRead = clientStream.Read(messageBytes, 0, messageBytes.Length);
            }
            catch (Exception ex)
            {
                LogToConsole(clientEndPoint, String.Format("[ERROR] Exception: {0}", ex.Message));
                break;
            }
            if (bytesRead == 0)
            {
                LogToConsole(clientEndPoint, "Client has disconnected");
                break;
            }
            messageCounter++;
            string message = Encoding.ASCII.GetString(messageBytes);
            message = message.Substring(0, message.IndexOf(''0'));
            LogToConsole(clientEndPoint, String.Format(" >> [{0}] Message received: {1}", messageCounter, message));
        }

客户端

            string infoToSend = null;
            do
            {
                Console.Write(" >> Info to send: ");
                infoToSend = Console.ReadLine();
                if (!String.IsNullOrEmpty(infoToSend))
                {
                    NetworkStream serverStream = client.GetStream();
                    byte[] buffer = Encoding.ASCII.GetBytes(infoToSend);
                    serverStream.Write(buffer, 0, buffer.Length);
                    serverStream.Flush();
                }
            } while (!String.IsNullOrEmpty(infoToSend));

解决方案

正如Douglas所发现的,缓冲区(messageBytes)因之前的读取而变脏。我最终得到了服务器的这段代码(我发布了整个代码,因为它可能对其他人有用):

namespace Gateway
{
    class Program
    {
        static void Main(string[] args)
        {
            int requestCount = 0;
            TcpListener serverSocket = new TcpListener(IPAddress.Any, 8888);
            serverSocket.Start();
            LogToConsole("Server Started. Waiting for clients ...");
            while ((true))
            {
                try
                {
                    TcpClient client = serverSocket.AcceptTcpClient();
                    requestCount++;
                    LogToConsole(String.Format("Connection from {0} accepted. Request #{1}", client.Client.RemoteEndPoint.ToString(), requestCount));
                    Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientConnection));
                    clientThread.IsBackground = true;
                    clientThread.Start(client);
                    LogToConsole(String.Format("Thread #{0} created to handle connection from {1}", clientThread.ManagedThreadId, client.Client.RemoteEndPoint.ToString()));
                    LogToConsole("Waiting for next client ...");
                }
                catch (Exception ex)
                {
                    LogToConsole(ex.ToString());
                    break;
                }
            }
        }
        static void HandleClientConnection(object client) 
        {
            TcpClient tcpClient = (TcpClient)client;
            byte[] messageBytes = new byte[1024];
            int bytesRead;
            int messageCounter = 0;
            string clientEndPoint = tcpClient.Client.RemoteEndPoint.ToString();
            while (true)
            {
                try
                {
                    NetworkStream clientStream = tcpClient.GetStream();
                    bytesRead = clientStream.Read(messageBytes, 0, messageBytes.Length);
                }
                catch (Exception ex)
                {
                    LogToConsole(clientEndPoint, String.Format("[ERROR] Exception: {0}", ex.Message));
                    break;
                }
                if (bytesRead == 0)
                {
                    LogToConsole(clientEndPoint, "Client has disconnected");
                    break;
                }
                messageCounter++;
                string message = Encoding.ASCII.GetString(messageBytes, 0, bytesRead);
                LogToConsole(clientEndPoint, String.Format(" >> [{0}] Message received: {1}", messageCounter, message));
            }
            LogToConsole(clientEndPoint, "Closed connection to client");
            tcpClient.Close();
        }
        static void LogToConsole(string clientEndPoint, string message)
        {
            int threadId = Thread.CurrentThread.ManagedThreadId;
            string time = DateTime.Now.ToString("HH:mm:ss");
            Console.WriteLine("{0} [{1}, {2}] {3}", time, threadId, clientEndPoint, message);
        }
        static void LogToConsole(string message)
        {
            int threadId = Thread.CurrentThread.ManagedThreadId;
            string time = DateTime.Now.ToString("HH:mm:ss");
            Console.WriteLine("{0} [{1}] {2}", time, threadId, message);
        }
    }
}

NetworkStream在客户端/服务器TCP通信期间脏

string message = Encoding.ASCII.GetString(messageBytes);

上述调用每次都会解码整个缓冲区,尽管每条消息只会写入其中的前n个字节(其中n是消息长度)。您的第一条消息"aa"被写入缓冲区的前两个字节。您的第二条消息"b"只写入第一个字节,覆盖第一个"a"字符,但保留第二个"a"不变。这就是为什么缓冲区在第二条消息之后显示为包含"ba"的原因。

你可以通过将上面的调用更改为:来简单地解决这个问题

string message = Encoding.ASCII.GetString(messageBytes, 0, bytesRead);

然而,您的代码仍然容易受到另一个问题的影响:NetworkStream.Read只读取当前可用的数据。如果客户端仍在传输,那么它可能会返回部分消息。因此,您的服务器可能会将这两条消息读取为"a"answers"ab"。

在您的情况下,由于您似乎只传输单行文本消息,因此可以将NetworkStream封装在服务器的StreamReader中,并封装在客户端的StreamWriter中。然后,只需在服务器上调用StreamReader.ReadLine,在客户端调用StreamWriter.WriteLineReadLine将继续读取,直到遇到换行符,并在到达流的末尾时返回null

服务器:

using (NetworkStream clientStream = tcpClient.GetStream())
using (StreamReader reader = new StreamReader(clientStream))
{
    while (true)
    {
        message = reader.ReadLine();
        if (message == null)
        {
            LogToConsole(clientEndPoint, "Client has disconnected");
            break;
        }
        messageCounter++;
        LogToConsole(clientEndPoint, String.Format(" >> [{0}] Message received: {1}", messageCounter, message));
    }
}

客户:

using (NetworkStream serverStream = client.GetStream())
using (StreamWriter writer = new StreamWriter(serverStream))
{
    do
    {
        Console.Write(" >> Info to send: ");
        infoToSend = Console.ReadLine();
        if (!String.IsNullOrEmpty(infoToSend))
            writer.WriteLine(infoToSend);
    } while (!String.IsNullOrEmpty(infoToSend));
}

如果您的客户端可以在您的消息中发送换行符,则此解决方案将不起作用。