[問題]
System.Net.Sockets.Socket 的異步接收破壞了接收緩沖區,將隨機的位元組突發注入到接收資料的中間。
[背景]
我有一個 TCP 連接服務器。客戶端與服務器建立套接字連接,并向服務器發送加密資料包。
最初,服務器使用執行緒進行操作。每個傳入連接都由一個專用執行緒提供服務。通過套接字發送和接收的所有緩沖區都分配在堆上并收集垃圾。一切正常。
然后,為了提高性能,我做了兩個改變:
我沒有在堆上分配緩沖區,而是使用ArrayPool.Shared.Rent()進行分配。
我將套接字的發送和接收更改為async。
我沒有更改資料包的序列化、反序列化、加密和解密方式。
更改后,在我的開發 PC上一切正常。當部署到 AWS 上的生產服務器時,當資料包很小(50 位元組左右)時,一切都很好。
當資料包很大時,每個 16 KB,服務器會很好地接收一些這樣的資料包,然后,隨機地,收到的資料包中的一個會被破壞,中間會注入一千左右未知位元組的突發接收到的資料,導致AES解密拋出“invalid padding”例外:

我不知道注入的額外位元組來自哪里。
以下是套接字發送和接收代碼:
public static Task<int> ReceiveBytesAsync(this Socket socket, SocketAsyncEventArgs args) // , int offset, int count)
{
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<SocketAsyncEventArgs> handler = null;
handler = (s, e) =>
{
args.Completed -= handler;
if (args.SocketError != SocketError.Success)
tcs.SetException(new InvalidOperationException(args.SocketError.ToString()));
else
tcs.SetResult(args.BytesTransferred);
};
args.Completed = handler;
if (!socket.ReceiveAsync(args))
{
args.Completed -= handler;
if (args.SocketError != SocketError.Success)
tcs.SetException(new InvalidOperationException(args.SocketError.ToString()));
else
tcs.SetResult(args.BytesTransferred);
}
return tcs.Task;
}
private static async Task<bool> ReadBufferFromSocketAsync(Socket socket, SocketAsyncEventArgs socketArgs) // , int offset, int length)
{
for (int bytesRead = 0; bytesRead < socketArgs.Count;)
{
// If bytesRead is 0, SetBuffer would have been called by the caller of this method.
if (bytesRead > 0)
socketArgs.SetBuffer(socketArgs.Offset bytesRead, socketArgs.Count - bytesRead);
int nRead = await socket.ReceiveBytesAsync(socketArgs);
bytesRead = nRead;
if (nRead == 0)
{
socket.Close();
return false;
}
}
return true;
}
public static Task<int> SendBytesAsync(this Socket socket, SocketAsyncEventArgs args) // , int offset, int count)
{
var tcs = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
EventHandler<SocketAsyncEventArgs> handler = null;
handler = (s, e) =>
{
args.Completed -= handler;
if (args.SocketError != SocketError.Success)
tcs.SetException(new InvalidOperationException(args.SocketError.ToString()));
else
tcs.SetResult(args.BytesTransferred);
};
args.Completed = handler;
if (!socket.SendAsync(args)) // means the operation completed synchronously & Completed handler won't fire
{
args.Completed -= handler;
if (args.SocketError != SocketError.Success)
tcs.SetException(new InvalidOperationException(args.SocketError.ToString()));
else
tcs.SetResult(args.BytesTransferred);
}
return tcs.Task;
}
private static async Task<bool> SendBufferToSocketAsync(Socket socket, SocketAsyncEventArgs socketArgs)
{
int retry = 0;
for (int ixWrite = 0; ixWrite < socketArgs.Count;)
{
if (ixWrite > 0)
socketArgs.SetBuffer(socketArgs.Offset ixWrite, socketArgs.Count - ixWrite);
int nSent = await socket.SendBytesAsync(socketArgs);
ixWrite = nSent;
if (retry >= 10)
{
socket.Close();
return false;
}
if (nSent == 0)
{
retry ;
await Task.Delay(10 * retry);
}
else
retry = 0;
}
return true;
}
uj5u.com熱心網友回復:
將ReadBufferFromSocketAsync更改為以下內容后,問題消失了
private static async Task<bool> ReadBufferFromSocketAsync(Socket socket, byte[] buffer, int offset, int totalToRead)
{
using (SocketAsyncEventArgs socketArgs = new SocketAsyncEventArgs())
{
int iAllRead = 0, iOneRead = 0;
socketArgs.SetBuffer(buffer, offset, totalToRead);
while (true)
{
iOneRead = await socket.ReceiveBytesAsync(socketArgs);
if (iOneRead == 0)
{
socket.Close();
return false;
}
iAllRead = iOneRead;
if (iAllRead == totalToRead)
return true;
offset = iOneRead;
socketArgs.SetBuffer(offset, totalToRead - iAllRead);
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/464836.html
