C# TCP 读取所有数据 - 服务器端问题
C# TCP Read All Data - Server Side Issues
我正在使用 C# v4.0 (.Net Framework v4) 开发一个简单的 TCP 服务器应用程序:
我想完成这两个步骤:
- 客户端发送message1到服务器(客户端可以是.net或java应用程序)
- 服务器将 message2 作为对 message1
的 响应 发送回客户端
我的服务器有问题,无法正确读取 message1,除非我使用这些不合适的解决方案之一:
1) 使用只有 1 字节缓冲区的 MemoryStream(有效但速度慢):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[1]; // works but slow in case of big messages
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
示例: 如果 message1.Length == 12501 并且我使用 1024 的缓冲区 NetworkStream.Read() 循环只读取 2048 字节的消息 1,我认为 NetworkStream.DataAvailable 不是 return 正确的值!
2) 从 NetworkStream 读取到缓冲区后使用 Thread.Sleep(1000)(有效但速度慢):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[8192];
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
Thread.Sleep(1000); // works but receiving gets slow
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
3) 使用 StreamReader.ReadToEnd() 并在发送消息 1 后关闭客户端的套接字(有效但服务器无法使用消息 2 响应客户端):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream, true);
string message1 = streamReader.ReadToEnd(); // blocks until client close its socket
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
4) 使用StreamReader.ReadLine() 循环并关闭客户端的套接字
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream);
StringBuilder stringBuilder = new StringBuilder();
while (!streamReader.EndOfStream)
{
stringBuilder.AppendLine(streamReader.ReadLine()); // blocks until client close its socket
}
string message1 = stringBuilder.ToString();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
5) 给 message1 加上长度前缀(可行,但需要客户端向消息添加额外的字节,这不适用于现有的 java 客户端)
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
byte[] bufferMessageLength = new byte[4]; // sizeof(int)
networkStream.Read(bufferMessageLength, 0, bufferMessageLength.Length);
int messageLength = BitConverter.ToInt32(bufferMessageLength, 4);
byte[] bufferMessage = new byte[messageLength];
networkStream.Read(bufferMessage, 0, bufferMessage.Length);
memoryStream.Write(buffer, 0, bufferMessage.Length);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
关于这些问题,在不使用上述解决方案的情况下,从客户端读取所有数据的最佳方法是什么?
而不是使用 networkStream.DataAvailable
在消息的开头附加数据大小。例如,您的消息长度是 12501 使用前 4 个字节作为消息长度。
首先定义一个从buffer中读取数据的方法
public static void ReadStream(NetworkStream reader, byte[] data)
{
var offset = 0;
var remaining = data.Length;
while (remaining > 0)
{
var read = reader.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
然后从流中读取数据。
var bytesRead = 0;
var offset = 0;
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
var bufferMessageSize = new byte[4]; // int32
ReadStream(networkStream, bufferMessageSize);
var messageSize = BitConverter.ToInt32(bufferMessageSize, 4); // bytesToRead
var bufferMessage = new byte[messageSize];
ReadStream(networkStream, bufferMessage);
// Now Respond back Client here
// networkStream.Write();
如果通信是面向线路的,那么StreamReader.ReadLine()
可能是合适的。
ReadLine() Reads a line of characters from the current stream and returns the
data as a string.
我正在使用 C# v4.0 (.Net Framework v4) 开发一个简单的 TCP 服务器应用程序:
我想完成这两个步骤:
- 客户端发送message1到服务器(客户端可以是.net或java应用程序)
- 服务器将 message2 作为对 message1 的 响应 发送回客户端
我的服务器有问题,无法正确读取 message1,除非我使用这些不合适的解决方案之一:
1) 使用只有 1 字节缓冲区的 MemoryStream(有效但速度慢):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[1]; // works but slow in case of big messages
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
示例: 如果 message1.Length == 12501 并且我使用 1024 的缓冲区 NetworkStream.Read() 循环只读取 2048 字节的消息 1,我认为 NetworkStream.DataAvailable 不是 return 正确的值!
2) 从 NetworkStream 读取到缓冲区后使用 Thread.Sleep(1000)(有效但速度慢):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
int numberOfBytesRead = 0;
byte[] buffer = new byte[8192];
do
{
numberOfBytesRead = networkStream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, numberOfBytesRead);
Thread.Sleep(1000); // works but receiving gets slow
} while (networkStream.DataAvailable);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
3) 使用 StreamReader.ReadToEnd() 并在发送消息 1 后关闭客户端的套接字(有效但服务器无法使用消息 2 响应客户端):
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream, true);
string message1 = streamReader.ReadToEnd(); // blocks until client close its socket
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
4) 使用StreamReader.ReadLine() 循环并关闭客户端的套接字
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
StreamReader streamReader = new StreamReader(networkStream);
StringBuilder stringBuilder = new StringBuilder();
while (!streamReader.EndOfStream)
{
stringBuilder.AppendLine(streamReader.ReadLine()); // blocks until client close its socket
}
string message1 = stringBuilder.ToString();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2); // if client close its sockets, the server cannot send this message
streamWriter.Flush();
}
}
}
5) 给 message1 加上长度前缀(可行,但需要客户端向消息添加额外的字节,这不适用于现有的 java 客户端)
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
MemoryStream memoryStream = new MemoryStream();
byte[] bufferMessageLength = new byte[4]; // sizeof(int)
networkStream.Read(bufferMessageLength, 0, bufferMessageLength.Length);
int messageLength = BitConverter.ToInt32(bufferMessageLength, 4);
byte[] bufferMessage = new byte[messageLength];
networkStream.Read(bufferMessage, 0, bufferMessage.Length);
memoryStream.Write(buffer, 0, bufferMessage.Length);
if (memoryStream.Length > 0)
{
string message1 = new StreamReader(memoryStream).ReadToEnd();
if (message1 == "message1")
{
using (StreamWriter streamWriter = new StreamWriter(networkStream))
{
string message2 = "message2";
streamWriter.Write(message2);
streamWriter.Flush();
}
}
}
}
关于这些问题,在不使用上述解决方案的情况下,从客户端读取所有数据的最佳方法是什么?
而不是使用 networkStream.DataAvailable
在消息的开头附加数据大小。例如,您的消息长度是 12501 使用前 4 个字节作为消息长度。
首先定义一个从buffer中读取数据的方法
public static void ReadStream(NetworkStream reader, byte[] data)
{
var offset = 0;
var remaining = data.Length;
while (remaining > 0)
{
var read = reader.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
然后从流中读取数据。
var bytesRead = 0;
var offset = 0;
TcpClient tcpClient = tcpListener.AcceptTcpClient();
NetworkStream networkStream = tcpClient.GetStream();
var bufferMessageSize = new byte[4]; // int32
ReadStream(networkStream, bufferMessageSize);
var messageSize = BitConverter.ToInt32(bufferMessageSize, 4); // bytesToRead
var bufferMessage = new byte[messageSize];
ReadStream(networkStream, bufferMessage);
// Now Respond back Client here
// networkStream.Write();
如果通信是面向线路的,那么StreamReader.ReadLine()
可能是合适的。
ReadLine() Reads a line of characters from the current stream and returns the data as a string.