使用c#套接字的接收短于预期
我使用TCP套接字在项目的两个组件(都在本地运行)之间进行通信。一个充当服务器,处理数据并向另一个发送消息,另一个充当客户端 从服务器发送时,我首先发送一个4字节长的头,然后发送消息:使用c#套接字的接收短于预期,c#,sockets,C#,Sockets,我使用TCP套接字在项目的两个组件(都在本地运行)之间进行通信。一个充当服务器,处理数据并向另一个发送消息,另一个充当客户端 从服务器发送时,我首先发送一个4字节长的头,然后发送消息: public void SendMessage(string message) { var binaryMessage = Encoding.UTF8.GetBytes(message); var messageLength = binaryMessage.Length; var mess
public void SendMessage(string message) {
var binaryMessage = Encoding.UTF8.GetBytes(message);
var messageLength = binaryMessage.Length;
var messageLengthBytes = BitConverter.GetBytes(messageLength);
_tcpClient.Client.Send(messageLengthBytes);
var bytesSent = _tcpClient.Client.Send(binaryMessage);
if (bytesSent != messageLength) {
_log.Warn($"Incorrect byte count send ({bytesSent} instead of {messageLength}).");
}
}
在客户端,当接收时,我读取4个字节以获取长度头,然后读取4KB块直到完成:
private const int RecBufferSize = 4096;
private readonly byte[] _recBuffer = new byte[RecBufferSize];
private void HandleNextIncomingMessage(IAsyncResult ar) {
var bytesReceived = _tcp.Client.EndReceive(ar);
if (bytesReceived != 4) {
_log.Error("Could not figure out the size of the incoming message - forcing reconnect.");
Reconnect();
return;
}
var array = _recBuffer.Take(4).ToArray();
var messageSize = BitConverter.ToInt32(array, 0);
var messageBuffer = new byte[messageSize];
var totalBytesFedIntoMessageBuffer = 0;
while (totalBytesFedIntoMessageBuffer < messageSize) {
var bytesToRead = messageSize - totalBytesFedIntoMessageBuffer;
if (bytesToRead > RecBufferSize) {
bytesToRead = RecBufferSize;
}
var bytesRead = _tcp.Client.Receive(_recBuffer, bytesToRead, SocketFlags.None);
if (bytesRead != bytesToRead) {
_log.Error($"Did not read the correct number of bytes {bytesToRead} from the network - read {bytesRead} - forcing reconnect.");
Reconnect();
return;
}
Buffer.BlockCopy(_recBuffer, 0, messageBuffer, totalBytesFedIntoMessageBuffer, bytesRead);
totalBytesFedIntoMessageBuffer += bytesRead;
}
HandleMessage(Encoding.UTF8.GetString(messageBuffer));
}
private const int RecBufferSize=4096;
私有只读字节[]u recBuffer=新字节[RecBufferSize];
私有无效句柄消息(IAsyncResult ar){
var bytesserved=_tcp.Client.EndReceive(ar);
如果(字节数已接收!=4){
_错误(“无法计算传入消息的大小-强制重新连接”);
重新连接();
返回;
}
var数组=_recBuffer.Take(4.ToArray();
var messageSize=BitConverter.ToInt32(数组,0);
var messageBuffer=新字节[messageSize];
var totalBytesFedIntoMessageBuffer=0;
while(totalBytesFedIntoMessageBufferRecBufferSize){
bytesToRead=RecBufferSize;
}
var bytesRead=_tcp.Client.Receive(_recBuffer,bytesToRead,SocketFlags.None);
if(bytesRead!=bytesToRead){
_log.Error($“没有从网络读取正确的字节数{bytesToRead},读取{bytesRead},强制重新连接。”);
重新连接();
返回;
}
Buffer.BlockCopy(_recBuffer,0,messageBuffer,totalBytesFedIntoMessageBuffer,bytesRead);
totalBytesFedIntoMessageBuffer+=字节读取;
}
HandleMessage(Encoding.UTF8.GetString(messageBuffer));
}
大多数消息都很小(<1KB),从来不会有任何问题,但有一条消息承载大量数据,大小可能为100-200KB。大多数情况下,发送和接收时都没有问题,但偶尔一个数据块不包含正确的字节数,我看到日志行没有从网络读取正确的字节数4096-读取3472-强制重新连接。
奇怪的是,这个数字并不总是3472,但经常是
如果我将数据发送到服务器端的套接字中,然后在客户端调用
Receive
,它应该阻塞直到接收到完整的4096字节,这样的想法对吗?如果是这样的话,为什么它有时会收到较少的字节数?该消息是否记录在最后收到的数据块上?如果有效载荷不是X块那么大,就会发生这种情况,对吗?在这种情况下这不会是一个错误,它只是流的结尾。每个块要读取的字节数是在while块内计算的,因此如果还有>chunksize字节要读取,则取一个块大小(4096),否则取剩余的。我注意到它更多地发生在第一个块上。你应该使用bytesRead
,而不是将它与请求的计数进行比较。接收少于请求量不是问题,如果已经分配了x字节的messagebuffer,为什么要使用4k的接收缓冲区?使用该缓冲区接收消息。从套接字请求200k不是问题。您可能会收到较少的请求,但它将与下一个请求一起收到。socket.Receive()方法有一个重载,因此您可以传递接收缓冲区的起始偏移量以继续接收。我总是重用接收缓冲区并构造一个类来保存来自它的消息,这样您就可以重用相同的bytebuffer。这会阻止许多分配。(和块拷贝)即使是小到4个字节的读取,为了获得长度,数据可能最终会在数据包边界上碎片化(TCP只传递字节),因此对于所有读取,您应该在循环中读取,并使用返回的值来了解需要多少,而不是试图预先计算并期望返回许多字节。正如Jeroen所说,这个中间固定大小的缓冲区似乎在这里没有任何好处。这个消息记录在最后接收到的数据块上吗?如果有效载荷不是X块那么大,就会发生这种情况,对吗?在这种情况下这不会是一个错误,它只是流的结尾。每个块要读取的字节数是在while块内计算的,因此如果还有>chunksize字节要读取,则取一个块大小(4096),否则取剩余的。我注意到它更多地发生在第一个块上。你应该使用bytesRead
,而不是将它与请求的计数进行比较。接收少于请求量不是问题,如果已经分配了x字节的messagebuffer,为什么要使用4k的接收缓冲区?使用该缓冲区接收消息。从套接字请求200k不是问题。您可能会收到较少的请求,但它将与下一个请求一起收到。socket.Receive()方法有一个重载,因此您可以传递接收缓冲区的起始偏移量以继续接收。我总是重用接收缓冲区并构造一个类来保存来自它的消息,这样您就可以重用相同的bytebuffer。这会阻止许多分配。(和块拷贝)即使是小到4个字节的读取,为了获得长度,数据可能最终会在数据包边界上碎片化(TCP只传递字节),因此对于所有读取,您应该在循环中读取,并使用返回的值来了解需要多少,而不是试图预先计算并期望返回许多字节。正如Jeroen所说,这种中间固定大小的缓冲区在这里似乎没有任何好处。