在我的服务器应用程序的网络协议中,我在每个发送的缓冲区的开头都有 2 个字节,其中包含长度。TcpReadCallback 方法读取 BeginReceive 写入的字节,直到读取完整的缓冲区。但是,使用我的实现,数据包似乎丢失了,有时空缓冲区会发送到 ProcessReceiveBuffer,尤其是当我发送相隔不到 5 毫秒的消息时,在通过互联网发送时更常见(每 10 秒左右就会发生一次发送半秒)。有没有人能发现我在这里做错了什么?
谢谢!
private Socket _tcpWorkSocket;
private byte[] _tcpWorkBuffer;
private byte[] _tcpReceiveBuffer;
public void BeginTcpReceive() {
if (!Connected)
return;
_tcpWorkBuffer = new byte[BufferSize]; // 1024
_tcpWorkSocket.BeginReceive(_tcpWorkBuffer, 0, BufferSize, 0, new AsyncCallback(TcpReadCallback), this);
}
private void TcpReadCallback(IAsyncResult ar) {
try {
if (!Connected)
return;
int readBytes = _tcpWorkSocket.EndReceive(ar);
if (readBytes > 0) {
byte[] buffer = new byte[readBytes];
Array.Copy(_tcpWorkBuffer, 0, buffer, 0, readBytes);
if (User != null)
User.SessionTimerReset();
Proccess_Buffer(buffer);
}
BeginTcpReceive();
}
catch(SocketException ex) {
Close(false);
}
catch (Exception ex) {
Logger.LogError("{0}: {1}n{2}", ex.GetType(), ex.Message, ex.StackTrace);
Close(false);
}
}
private void Proccess_Buffer(byte[] buffer) {
if (!Receiving) {
Receiving = true;
_tcpPacketLength = BitConverter.ToUInt16(buffer, 0);
_tcpBytesNeeded = _tcpPacketLength;
_tcpCurIndex = 0;
_tcpReceiveBuffer = new byte[_tcpPacketLength];
}
int origSize = buffer.Length;
int copyLength = _tcpBytesNeeded;
if (_tcpBytesNeeded > BufferSize)
copyLength = BufferSize;
Array.Copy(buffer, 0, _tcpReceiveBuffer, _tcpCurIndex, copyLength);
_tcpCurIndex += buffer.Length;
_tcpBytesNeeded -= buffer.Length;
if (_tcpBytesNeeded <= 0) {
Receiving = false;
ProcessReceiveBuffer(_tcpReceiveBuffer, Protocal.Tcp);
}
if (origSize > buffer.Length) {
// next packet, incase 2 messages are sent in the same packet.
byte[] nextBuffer = new byte[buffer.Length - origSize ];
if (nextBuffer.Length > 1) {
Array.Copy(buffer, origSize , nextBuffer, 0, nextBuffer.Length);
Proccess_Buffer(nextBuffer);
}
else {
Logger.LogError("Next buffer length split!");
}
}
}
例:
"已收到"后的第一个数字是客户端发送的订单,第二个是大小。当通过互联网发送时,TCP中的一些数据包经常被丢弃
,突发。[17:29:49]: received: 1349, 514
[17:29:50]: received: 1350, 514
[17:29:50]: received: 1351, 514
[17:29:51]: received: 1352, 514
[17:29:51]: received: 1353, 514
[17:29:52]: received: 1355, 514
[17:29:52]: Skipped! expected 1354, got 1355
[17:29:53]: received: 1357, 514
[17:29:53]: Skipped! expected 1356, got 1357
[17:29:54]: received: 1359, 514
[17:29:54]: Skipped! expected 1358, got 1359
[17:29:56]: received: 1362, 514
[17:29:56]: Skipped! expected 1360, got 1362
[17:29:56]: received: 1363, 514
[17:29:57]: received: 1364, 514
由于网络拥塞,EndReceive
可能无法一次性收到完整的消息。因此,TcpReadCallback
应重复读取操作,直到不再接收到更多字节。
下面是一个相关的 MSDN 示例,它演示了这一点:
public static void Read_Callback(IAsyncResult ar){
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(ar);
if (read > 0) {
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AsyncCallback(Async_Send_Receive.Read_Callback), so);
}
else{
if (so.sb.Length > 1) {
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
}
在操作系统级别的基本tcp socket
send()
和recv()
调用中,将在相关文章中讨论类似的效果。
我已经通过使用 https://github.com/Luaancz/Networking 中的实现解决了这个问题