我有一个客户端和一个通过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));
正如道格拉斯所发现的那样,缓冲区(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);
}
}
}
答案 0 :(得分:2)
string message = Encoding.ASCII.GetString(messageBytes);
上述调用每次都会对整个缓冲区进行解码,尽管每条消息只会被写入其中的第一个 n 字节(其中 n 是消息长度) 。您的第一条消息“aa”将写入缓冲区的前两个字节。您的第二条消息“b”仅写入第一个字节,覆盖第一个“a”字符,但保留第二个“a”完整。这就是为什么缓冲区在第二条消息后似乎包含“ba”的原因。
您可以通过将上面的调用更改为:
来轻松解决此问题string message = Encoding.ASCII.GetString(messageBytes, 0, bytesRead);
但是,您的代码仍然容易受到其他问题的影响:NetworkStream.Read
只能读取当前可用的数据。如果客户端仍在发送,则它可能返回部分消息。因此,您的服务器可能会将这两条消息读作“a”和“ab”。
在您的情况下,由于您似乎只传输单行短信,因此您可以将NetworkStream
包裹在服务器中的StreamReader
和StreamWriter
中。客户。然后,只需在服务器上调用StreamReader.ReadLine
,在客户端调用StreamWriter.WriteLine
即可。 ReadLine
将继续阅读,直至遇到换行符,并在到达流末尾时返回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));
}
如果您的客户可以在消息中发送新行,此解决方案将无效。