UDP服务器的C#可等待SocketAsyncEventArgs包装器;socket.ReceiveAsync返回无效的RemoteEndPoint(0.0.0.0:0)
正如标题所说,我正在原始套接字上编写UDP服务器,并使用SocketAsyncEventArgs,因为我想快速编写一些东西 我知道UdpClient是存在的,并且有更简单的方法来编写服务器,但是我想学习如何正确地使用SocketAsyncEventArgs和socket.ReceiveFromAsync/socket.SendToAsync方法来实现它们声称的“提高吞吐量”和“更好的可伸缩性”() 我基本上遵循了MSDN示例,因为我认为这是学习这些方法如何工作的一个不错的起点,但遇到了一些问题。服务器最初工作,可以回显接收到的字节,但将随机“失败”从正确的地址接收字节。RemoteEndPoint将由UDP占位符端点{0.0.0.0:0}填充,而不是正确的本地主机客户端地址(例如127.0.0.1:7007)(因为缺少更好的术语)。(对不起,控制台是波兰语的。请相信我,SocketException消息是“所需地址在此上下文中无效”) 我曾多次从MSDN示例中批量删除块,仅更改socket.ReceiveFromAsync调用的SocketAsyncEventArgs实例上填充的字段(根据MSDN文档->),最终结果仍然相同。此外,这是一个间歇性的问题,而不是一个持续的问题。从我所注意到的情况来看,服务器不会一直出错 到目前为止,我的想法是UdpServer的状态问题、UdpClient端的一些不一致或TaskCompletionSource的滥用 编辑1: 我觉得我应该说明我为什么使用SocketAsyncEventArgs。我完全理解发送和接收数据有更简单的方法。async/await套接字扩展是实现这一点的一个好方法,也是我最初的做法。我想对async/await与旧的api SocketArgs进行基准测试,看看这两种方法有多大的不同UDP服务器的C#可等待SocketAsyncEventArgs包装器;socket.ReceiveAsync返回无效的RemoteEndPoint(0.0.0.0:0),c#,sockets,asynchronous,async-await,udp,C#,Sockets,Asynchronous,Async Await,Udp,正如标题所说,我正在原始套接字上编写UDP服务器,并使用SocketAsyncEventArgs,因为我想快速编写一些东西 我知道UdpClient是存在的,并且有更简单的方法来编写服务器,但是我想学习如何正确地使用SocketAsyncEventArgs和socket.ReceiveFromAsync/socket.SendToAsync方法来实现它们声称的“提高吞吐量”和“更好的可伸缩性”() 我基本上遵循了MSDN示例,因为我认为这是学习这些方法如何工作的一个不错的起点,但遇到了一些问题。
下面包括UdpClient、UdpServer和共享结构的代码。如果StackOverflow允许,我还可以尝试按需提供更多代码 谢谢你抽出时间来帮助我 测试代码
private static async Task TestNetworking()
{
EndPoint serverEndPoint = new IPEndPoint(IPAddress.Loopback, 12345);
await Task.Factory.StartNew(async () =>
{
SocketClient client = new UdpClient();
bool bound = client.Bind(new IPEndPoint(IPAddress.Any, 7007));
if (bound)
{
Console.WriteLine($"[Client] Bound client socket!");
}
if (bound && client.Connect(serverEndPoint))
{
Console.WriteLine($"[Client] Connected to {serverEndPoint}!");
byte[] message = Encoding.UTF8.GetBytes("Hello World!");
Stopwatch stopwatch = new Stopwatch();
const int packetsToSend = 1_000_000;
for (int i = 0; i < packetsToSend; i++)
{
try
{
stopwatch.Start();
int sentBytes = await client.SendAsync(serverEndPoint, message, SocketFlags.None);
//Console.WriteLine($"[Client] Sent {sentBytes} to {serverEndPoint}");
ReceiveResult result = await client.ReceiveAsync(serverEndPoint, SocketFlags.None);
//Console.WriteLine($"[{result.RemoteEndPoint} > Client] : {Encoding.UTF8.GetString(result.Contents)}");
serverEndPoint = result.RemoteEndPoint;
stopwatch.Stop();
}
catch (Exception ex)
{
Console.WriteLine(ex);
i--;
await Task.Delay(1);
}
}
double approxBandwidth = (packetsToSend * message.Length) / (1_000_000.0 * (stopwatch.ElapsedMilliseconds / 1000.0));
Console.WriteLine($"Sent {packetsToSend} packets of {message.Length} bytes in {stopwatch.ElapsedMilliseconds:N} milliseconds.");
Console.WriteLine($"Approximate bandwidth: {approxBandwidth} MBps");
}
}, TaskCreationOptions.LongRunning);
await Task.Factory.StartNew(async () =>
{
try
{
SocketServer server = new UdpServer();
bool bound = server.Bind(serverEndPoint);
if (bound)
{
//Console.WriteLine($"[Server] Bound server socket!");
//Console.WriteLine($"Starting server at {serverEndPoint}!");
await server.StartAsync();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}).Result;
}
private静态异步任务TestNetworking()
{
EndPoint serverEndPoint=新的IPEndPoint(IPAddress.Loopback,12345);
等待Task.Factory.StartNew(异步()=>
{
SocketClient=new UdpClient();
bool-bound=client.Bind(新的IPEndPoint(IPAddress.Any,7007));
如果(绑定)
{
WriteLine($“[Client]绑定的客户端套接字!”);
}
if(绑定和客户端连接(serverEndPoint))
{
WriteLine($“[Client]连接到{serverEndPoint}!”);
byte[]message=Encoding.UTF8.GetBytes(“helloworld!”);
秒表秒表=新秒表();
const int packetsToSend=1_000_000;
对于(int i=0;iClient]:{Encoding.UTF8.GetString(result.Contents)}”);
serverEndPoint=result.RemoteEndPoint;
秒表;
}
捕获(例外情况除外)
{
控制台写入线(ex);
我--;
等待任务。延迟(1);
}
}
double approxBandwidth=(packetsToSend*message.Length)/(1_000_000.0*(stopwatch.elapsedmillyses/1000.0));
WriteLine($“已发送{message.Length}字节的{packetsToSend}数据包,单位为{stopwatch.elapsedmillesons:N}毫秒。”);
WriteLine($“近似带宽:{approxBandwidth}MBps”);
}
},TaskCreationOptions.LongRunning);
等待Task.Factory.StartNew(异步()=>
{
尝试
{
SocketServer服务器=新的UdpServer();
boolbind=server.Bind(serverEndPoint);
如果(绑定)
{
//Console.WriteLine($“[Server]绑定服务器套接字!”);
//WriteLine($“正在{serverEndPoint}启动服务器!”);
等待server.StartAsync();
}
}
捕获(例外情况除外)
{
控制台写入线(ex);
}
}).结果;
}
共享代码
public readonly struct ReceiveResult
{
public const int PacketSize = 1024;
public readonly Memory<byte> Contents;
public readonly int ReceivedBytes;
public readonly EndPoint RemoteEndPoint;
public ReceiveResult(Memory<byte> contents, int receivedBytes, EndPoint remoteEndPoint)
{
Contents = contents;
ReceivedBytes = receivedBytes;
RemoteEndPoint = remoteEndPoint;
}
}
public readonly struct ReceiveResult
{
public const int PacketSize=1024;
公共只读存储器内容;
公共只读整型接收字节;
公共只读端点RemoteEndPoint;
公共ReceiveResult(内存内容、整数接收字节、端点remoteEndPoint)
{
内容=内容;
接收字节=接收字节;
RemoteEndPoint=RemoteEndPoint;
}
}
UDP客户端
public class UdpClient : SocketClient
{
/*
public abstract class SocketClient
{
protected readonly Socket socket;
protected SocketClient(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
socket = new Socket(addressFamily, socketType, protocolType);
}
public bool Bind(in EndPoint localEndPoint)
{
try
{
socket.Bind(localEndPoint);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public bool Connect(in EndPoint remoteEndPoint)
{
try
{
socket.Connect(remoteEndPoint);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public abstract Task<ReceiveResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags socketFlags);
public abstract Task<int> SendAsync(EndPoint remoteEndPoint, ArraySegment<byte> buffer, SocketFlags socketFlags);
}
*/
/// <inheritdoc />
public UdpClient() : base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
}
public override async Task<ReceiveResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags socketFlags)
{
byte[] buffer = new byte[ReceiveResult.PacketSize];
SocketReceiveFromResult result =
await socket.ReceiveFromAsync(new ArraySegment<byte>(buffer), socketFlags, remoteEndPoint);
return new ReceiveResult(buffer, result.ReceivedBytes, result.RemoteEndPoint);
/*
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.SetBuffer(new byte[ReceiveResult.PacketSize]);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
SocketTask awaitable = new SocketTask(args);
while (ReceiveResult.PacketSize > args.BytesTransferred)
{
await socket.ReceiveFromAsync(awaitable);
}
return new ReceiveResult(args.MemoryBuffer, args.RemoteEndPoint);
*/
}
public override async Task<int> SendAsync(EndPoint remoteEndPoint, ArraySegment<byte> buffer, SocketFlags socketFlags)
{
return await socket.SendToAsync(buffer.ToArray(), socketFlags, remoteEndPoint);
/*
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.SetBuffer(buffer);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
SocketTask awaitable = new SocketTask(args);
while (buffer.Length > args.BytesTransferred)
{
await socket.SendToAsync(awaitable);
}
return args.BytesTransferred;
*/
}
}
public类UdpClient:SocketClient
{
/*
公共抽象类SocketClient
{
public class UdpServer : SocketServer
{
/*
public abstract class SocketServer
{
protected readonly Socket socket;
protected SocketServer(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
socket = new Socket(addressFamily, socketType, protocolType);
}
public bool Bind(in EndPoint localEndPoint)
{
try
{
socket.Bind(localEndPoint);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public abstract Task StartAsync();
}
*/
private const int MaxPooledObjects = 100;
private readonly ConcurrentDictionary<EndPoint, ConcurrentQueue<byte[]>> clients;
private readonly ArrayPool<byte> receiveBufferPool = ArrayPool<byte>.Create(ReceiveResult.PacketSize, MaxPooledObjects);
private readonly ObjectPool<SocketAsyncEventArgs> receiveSocketAsyncEventArgsPool =
new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), MaxPooledObjects);
private readonly ObjectPool<SocketAsyncEventArgs> sendSocketAsyncEventArgsPool =
new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(), MaxPooledObjects);
private void HandleIOCompleted(object? sender, SocketAsyncEventArgs eventArgs)
{
eventArgs.Completed -= HandleIOCompleted;
bool closed = false;
/*
Original (local) methods in ReceiveAsync and SendAsync,
these were assigned to eventArgs.Completed instead of HandleIOCompleted
=======================================================================
void ReceiveCompletedHandler(object? sender, SocketAsyncEventArgs eventArgs)
{
AsyncReadToken asyncReadToken = (AsyncReadToken)eventArgs.UserToken;
eventArgs.Completed -= ReceiveCompletedHandler;
if (eventArgs.SocketError != SocketError.Success)
{
asyncReadToken.CompletionSource.TrySetException(new SocketException((int)eventArgs.SocketError));
}
else
{
eventArgs.MemoryBuffer.CopyTo(asyncReadToken.OutputBuffer);
asyncReadToken.CompletionSource.TrySetResult(
new ReceiveResult(asyncReadToken.OutputBuffer, eventArgs.BytesTransferred, eventArgs.RemoteEndPoint));
}
receiveBufferPool.Return(asyncReadToken.RentedBuffer);
receiveSocketAsyncEventArgsPool.Return(eventArgs);
}
void SendCompletedHandler(object? sender, SocketAsyncEventArgs eventArgs)
{
AsyncWriteToken asyncWriteToken = (AsyncWriteToken)eventArgs.UserToken;
eventArgs.Completed -= SendCompletedHandler;
if (eventArgs.SocketError != SocketError.Success)
{
asyncWriteToken.CompletionSource.TrySetException(new SocketException((int)eventArgs.SocketError));
}
else
{
asyncWriteToken.CompletionSource.TrySetResult(eventArgs.BytesTransferred);
}
sendSocketAsyncEventArgsPool.Return(eventArgs);
}
*/
switch (eventArgs.LastOperation)
{
case SocketAsyncOperation.SendTo:
AsyncWriteToken asyncWriteToken = (AsyncWriteToken)eventArgs.UserToken;
if (eventArgs.SocketError != SocketError.Success)
{
asyncWriteToken.CompletionSource.TrySetException(new SocketException((int)eventArgs.SocketError));
}
else
{
asyncWriteToken.CompletionSource.TrySetResult(eventArgs.BytesTransferred);
}
sendSocketAsyncEventArgsPool.Return(eventArgs);
break;
case SocketAsyncOperation.ReceiveFrom:
AsyncReadToken asyncReadToken = (AsyncReadToken)eventArgs.UserToken;
if (eventArgs.SocketError != SocketError.Success)
{
asyncReadToken.CompletionSource.TrySetException(new SocketException((int)eventArgs.SocketError));
}
else
{
eventArgs.MemoryBuffer.CopyTo(asyncReadToken.OutputBuffer);
asyncReadToken.CompletionSource.TrySetResult(
new ReceiveResult(asyncReadToken.OutputBuffer, eventArgs.BytesTransferred, eventArgs.RemoteEndPoint));
}
receiveBufferPool.Return(asyncReadToken.RentedBuffer);
receiveSocketAsyncEventArgsPool.Return(eventArgs);
break;
case SocketAsyncOperation.Disconnect:
closed = true;
break;
case SocketAsyncOperation.Accept:
case SocketAsyncOperation.Connect:
case SocketAsyncOperation.None:
break;
}
if (closed)
{
// handle the client closing the connection on tcp servers at some point
}
}
private Task<ReceiveResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags socketFlags, Memory<byte> outputBuffer)
{
TaskCompletionSource<ReceiveResult> tcs = new TaskCompletionSource<ReceiveResult>();
byte[] buffer = receiveBufferPool.Rent(ReceiveResult.PacketSize);
Memory<byte> memoryBuffer = new Memory<byte>(buffer);
SocketAsyncEventArgs args = receiveSocketAsyncEventArgsPool.Get();
args.SetBuffer(memoryBuffer);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
args.UserToken = new AsyncReadToken(buffer, outputBuffer, tcs);
args.Completed += HandleIOCompleted;
if (socket.ReceiveFromAsync(args)) return tcs.Task;
byte[] bufferCopy = new byte[ReceiveResult.PacketSize];
args.MemoryBuffer.CopyTo(bufferCopy);
ReceiveResult result = new ReceiveResult(bufferCopy, args.BytesTransferred, args.RemoteEndPoint);
receiveBufferPool.Return(buffer);
receiveSocketAsyncEventArgsPool.Return(args);
return Task.FromResult(result);
}
private Task<int> SendAsync(EndPoint remoteEndPoint, Memory<byte> buffer, SocketFlags socketFlags)
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
SocketAsyncEventArgs args = sendSocketAsyncEventArgsPool.Get();
args.SetBuffer(buffer);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
args.UserToken = new AsyncWriteToken(buffer, tcs);
args.Completed += HandleIOCompleted;
if (socket.SendToAsync(args)) return tcs.Task;
int result = args.BytesTransferred;
sendSocketAsyncEventArgsPool.Return(args);
return Task.FromResult(result);
}
private readonly struct AsyncReadToken
{
public readonly TaskCompletionSource<ReceiveResult> CompletionSource;
public readonly Memory<byte> OutputBuffer;
public readonly byte[] RentedBuffer;
public AsyncReadToken(byte[] rentedBuffer, Memory<byte> outputBuffer, TaskCompletionSource<ReceiveResult> tcs)
{
RentedBuffer = rentedBuffer;
OutputBuffer = outputBuffer;
CompletionSource = tcs;
}
}
private readonly struct AsyncWriteToken
{
public readonly TaskCompletionSource<int> CompletionSource;
public readonly Memory<byte> OutputBuffer;
public AsyncWriteToken(Memory<byte> outputBuffer, TaskCompletionSource<int> tcs)
{
OutputBuffer = outputBuffer;
CompletionSource = tcs;
}
}
public UdpServer() : base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
clients = new ConcurrentDictionary<EndPoint, ConcurrentQueue<byte[]>>();
}
/// <inheritdoc />
public override async Task StartAsync()
{
EndPoint nullEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] receiveBuffer = new byte[ReceiveResult.PacketSize];
Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer);
while (true)
{
ReceiveResult result = await ReceiveAsync(nullEndPoint, SocketFlags.None, receiveBufferMemory);
Console.WriteLine($"[{result.RemoteEndPoint} > Server] : {Encoding.UTF8.GetString(result.Contents.Span)}");
int sentBytes = await SendAsync(result.RemoteEndPoint, result.Contents, SocketFlags.None);
Console.WriteLine($"[Server > {result.RemoteEndPoint}] Sent {sentBytes} bytes to {result.RemoteEndPoint}");
}
}
}
private static async Task TestNetworking()
{
EndPoint serverEndPoint = new IPEndPoint(IPAddress.Loopback, 12345);
await Task.Factory.StartNew(async () =>
{
try
{
SocketServer server = new UdpServer();
bool bound = server.Bind(serverEndPoint);
if (bound)
{
Console.WriteLine($"[Server] Bound server socket!");
Console.WriteLine($"[Server] Starting server at {serverEndPoint}!");
await server.StartAsync();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
await Task.Factory.StartNew(async () =>
{
SocketClient client = new UdpClient();
bool bound = client.Bind(new IPEndPoint(IPAddress.Any, 7007));
if (bound)
{
Console.WriteLine($"[Client] Bound client socket!");
}
if (bound && client.Connect(serverEndPoint))
{
Console.WriteLine($"[Client] Connected to {serverEndPoint}!");
byte[] message = Encoding.UTF8.GetBytes("Hello World!");
Memory<byte> messageBuffer = new Memory<byte>(message);
byte[] response = new byte[ReceiveResult.PacketSize];
Memory<byte> responseBuffer = new Memory<byte>(response);
Stopwatch stopwatch = new Stopwatch();
const int packetsToSend = 1_000_000, statusPacketThreshold = 10_000;
Console.WriteLine($"Started sending packets (total packet count: {packetsToSend})");
for (int i = 0; i < packetsToSend; i++)
{
if (i % statusPacketThreshold == 0)
{
Console.WriteLine($"Sent {i} packets out of {packetsToSend} ({((double)i / packetsToSend) * 100:F2}%)");
}
try
{
//Console.WriteLine($"[Client > {serverEndPoint}] Sending packet {i}");
stopwatch.Start();
int sentBytes = await client.SendAsync(serverEndPoint, messageBuffer, SocketFlags.None);
//Console.WriteLine($"[Client] Sent {sentBytes} to {serverEndPoint}");
ReceiveResult result = await client.ReceiveAsync(serverEndPoint, SocketFlags.None, responseBuffer);
//Console.WriteLine($"[{result.RemoteEndPoint} > Client] : {Encoding.UTF8.GetString(result.Contents)}");
serverEndPoint = result.RemoteEndPoint;
stopwatch.Stop();
}
catch (Exception ex)
{
Console.WriteLine(ex);
i--;
await Task.Delay(1);
}
}
double approxBandwidth = (packetsToSend * ReceiveResult.PacketSize) / (1_000_000.0 * (stopwatch.ElapsedMilliseconds / 1000.0));
Console.WriteLine($"Sent {packetsToSend} packets of {ReceiveResult.PacketSize} bytes in {stopwatch.ElapsedMilliseconds:N} milliseconds.");
Console.WriteLine($"Approximate bandwidth: {approxBandwidth} MBps");
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default).Result;
}
internal readonly struct AsyncReadToken
{
public readonly CancellationToken CancellationToken;
public readonly TaskCompletionSource<ReceiveResult> CompletionSource;
public readonly byte[] RentedBuffer;
public readonly Memory<byte> UserBuffer;
public AsyncReadToken(byte[] rentedBuffer, Memory<byte> userBuffer, TaskCompletionSource<ReceiveResult> tcs,
CancellationToken cancellationToken = default)
{
RentedBuffer = rentedBuffer;
UserBuffer = userBuffer;
CompletionSource = tcs;
CancellationToken = cancellationToken;
}
}
internal readonly struct AsyncWriteToken
{
public readonly CancellationToken CancellationToken;
public readonly TaskCompletionSource<int> CompletionSource;
public readonly byte[] RentedBuffer;
public readonly Memory<byte> UserBuffer;
public AsyncWriteToken(byte[] rentedBuffer, Memory<byte> userBuffer, TaskCompletionSource<int> tcs,
CancellationToken cancellationToken = default)
{
RentedBuffer = rentedBuffer;
UserBuffer = userBuffer;
CompletionSource = tcs;
CancellationToken = cancellationToken;
}
}
public readonly struct ReceiveResult
{
public const int PacketSize = 1024;
public readonly SocketAsyncEventArgs ClientArgs;
public readonly Memory<byte> Contents;
public readonly int Count;
public readonly EndPoint RemoteEndPoint;
public ReceiveResult(SocketAsyncEventArgs clientArgs, Memory<byte> contents, int count, EndPoint remoteEndPoint)
{
ClientArgs = clientArgs;
Contents = contents;
Count = count;
RemoteEndPoint = remoteEndPoint;
}
}
public abstract class SocketServer
{
protected readonly Socket socket;
protected SocketServer(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
socket = new Socket(addressFamily, socketType, protocolType);
}
public bool Bind(in EndPoint localEndPoint)
{
try
{
socket.Bind(localEndPoint);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public abstract Task StartAsync();
}
public class UdpServer : SocketServer
{
private const int MaxPooledObjects = 1;
private readonly ConcurrentDictionary<EndPoint, ConcurrentQueue<byte[]>> clients;
private readonly ArrayPool<byte> receiveBufferPool =
ArrayPool<byte>.Create(ReceiveResult.PacketSize, MaxPooledObjects);
private readonly ArrayPool<byte> sendBufferPool =
ArrayPool<byte>.Create(ReceiveResult.PacketSize, MaxPooledObjects);
private readonly ObjectPool<SocketAsyncEventArgs> socketAsyncEventArgsPool =
new DefaultObjectPool<SocketAsyncEventArgs>(new DefaultPooledObjectPolicy<SocketAsyncEventArgs>(),
MaxPooledObjects);
private void HandleIOCompleted(object? sender, SocketAsyncEventArgs eventArgs)
{
bool closed = false;
switch (eventArgs.LastOperation)
{
case SocketAsyncOperation.SendTo:
AsyncWriteToken asyncWriteToken = (AsyncWriteToken)eventArgs.UserToken;
if (asyncWriteToken.CancellationToken.IsCancellationRequested)
{
asyncWriteToken.CompletionSource.TrySetCanceled();
}
else
{
if (eventArgs.SocketError != SocketError.Success)
{
asyncWriteToken.CompletionSource.TrySetException(
new SocketException((int)eventArgs.SocketError));
}
else
{
asyncWriteToken.CompletionSource.TrySetResult(eventArgs.BytesTransferred);
}
}
sendBufferPool.Return(asyncWriteToken.RentedBuffer, true);
socketAsyncEventArgsPool.Return(eventArgs);
break;
case SocketAsyncOperation.ReceiveFrom:
AsyncReadToken asyncReadToken = (AsyncReadToken)eventArgs.UserToken;
if (asyncReadToken.CancellationToken.IsCancellationRequested)
{
asyncReadToken.CompletionSource.SetCanceled();
}
else
{
if (eventArgs.SocketError != SocketError.Success)
{
asyncReadToken.CompletionSource.SetException(
new SocketException((int)eventArgs.SocketError));
}
else
{
eventArgs.MemoryBuffer.CopyTo(asyncReadToken.UserBuffer);
ReceiveResult result = new ReceiveResult(eventArgs, asyncReadToken.UserBuffer,
eventArgs.BytesTransferred, eventArgs.RemoteEndPoint);
asyncReadToken.CompletionSource.SetResult(result);
}
}
receiveBufferPool.Return(asyncReadToken.RentedBuffer, true);
break;
case SocketAsyncOperation.Disconnect:
closed = true;
break;
case SocketAsyncOperation.Accept:
case SocketAsyncOperation.Connect:
case SocketAsyncOperation.None:
case SocketAsyncOperation.Receive:
case SocketAsyncOperation.ReceiveMessageFrom:
case SocketAsyncOperation.Send:
case SocketAsyncOperation.SendPackets:
throw new NotImplementedException();
default:
throw new ArgumentOutOfRangeException();
}
if (closed)
{
// handle the client closing the connection on tcp servers at some point
}
}
private Task<ReceiveResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags socketFlags,
Memory<byte> outputBuffer, CancellationToken cancellationToken = default)
{
TaskCompletionSource<ReceiveResult> tcs = new TaskCompletionSource<ReceiveResult>();
byte[] rentedBuffer = receiveBufferPool.Rent(ReceiveResult.PacketSize);
Memory<byte> memoryBuffer = new Memory<byte>(rentedBuffer);
SocketAsyncEventArgs args = socketAsyncEventArgsPool.Get();
args.SetBuffer(memoryBuffer);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
args.UserToken = new AsyncReadToken(rentedBuffer, outputBuffer, tcs, cancellationToken);
// if the receive operation doesn't complete synchronously, returns the awaitable task
if (socket.ReceiveFromAsync(args)) return tcs.Task;
args.MemoryBuffer.CopyTo(outputBuffer);
ReceiveResult result = new ReceiveResult(args, outputBuffer, args.BytesTransferred, args.RemoteEndPoint);
receiveBufferPool.Return(rentedBuffer, true);
return Task.FromResult(result);
}
private Task<int> SendAsync(SocketAsyncEventArgs clientArgs, Memory<byte> inputBuffer, SocketFlags socketFlags,
CancellationToken cancellationToken = default)
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
byte[] rentedBuffer = sendBufferPool.Rent(ReceiveResult.PacketSize);
Memory<byte> memoryBuffer = new Memory<byte>(rentedBuffer);
inputBuffer.CopyTo(memoryBuffer);
SocketAsyncEventArgs args = clientArgs;
args.SetBuffer(memoryBuffer);
args.SocketFlags = socketFlags;
args.UserToken = new AsyncWriteToken(rentedBuffer, inputBuffer, tcs, cancellationToken);
// if the send operation doesn't complete synchronously, return the awaitable task
if (socket.SendToAsync(args)) return tcs.Task;
int result = args.BytesTransferred;
sendBufferPool.Return(rentedBuffer, true);
socketAsyncEventArgsPool.Return(args);
return Task.FromResult(result);
}
public UdpServer() : base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
clients = new ConcurrentDictionary<EndPoint, ConcurrentQueue<byte[]>>();
for (int i = 0; i < MaxPooledObjects; i++)
{
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.Completed += HandleIOCompleted;
socketAsyncEventArgsPool.Return(args);
}
}
/// <inheritdoc />
public override async Task StartAsync()
{
EndPoint nullEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] receiveBuffer = new byte[ReceiveResult.PacketSize];
Memory<byte> receiveBufferMemory = new Memory<byte>(receiveBuffer);
while (true)
{
ReceiveResult result = await ReceiveAsync(nullEndPoint, SocketFlags.None, receiveBufferMemory);
//Console.WriteLine($"[{result.RemoteEndPoint} > Server] : {Encoding.UTF8.GetString(result.Contents.Span)}");
int sentBytes = await SendAsync(result.ClientArgs, result.Contents, SocketFlags.None);
//Console.WriteLine($"[Server > {result.RemoteEndPoint}] Sent {sentBytes} bytes to {result.RemoteEndPoint}");
}
}
public abstract class SocketClient
{
protected readonly Socket socket;
protected SocketClient(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
{
socket = new Socket(addressFamily, socketType, protocolType);
}
public bool Bind(in EndPoint localEndPoint)
{
try
{
socket.Bind(localEndPoint);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public bool Connect(in EndPoint remoteEndPoint)
{
try
{
socket.Connect(remoteEndPoint);
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return false;
}
}
public abstract Task<ReceiveResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags socketFlags,
Memory<byte> outputBuffer);
public abstract Task<int> SendAsync(EndPoint remoteEndPoint, Memory<byte> inputBuffer, SocketFlags socketFlags);
}
public class UdpClient : SocketClient
{
/// <inheritdoc />
public UdpClient() : base(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
}
public override async Task<ReceiveResult> ReceiveAsync(EndPoint remoteEndPoint, SocketFlags socketFlags,
Memory<byte> outputBuffer)
{
byte[] buffer = new byte[ReceiveResult.PacketSize];
SocketReceiveFromResult result =
await socket.ReceiveFromAsync(new ArraySegment<byte>(buffer), socketFlags, remoteEndPoint);
buffer.CopyTo(outputBuffer);
return new ReceiveResult(default, outputBuffer, result.ReceivedBytes, result.RemoteEndPoint);
/*
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.SetBuffer(new byte[ReceiveResult.PacketSize]);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
SocketTask awaitable = new SocketTask(args);
while (ReceiveResult.PacketSize > args.BytesTransferred)
{
await socket.ReceiveFromAsync(awaitable);
}
return new ReceiveResult(args.MemoryBuffer, args.RemoteEndPoint);
*/
}
public override async Task<int> SendAsync(EndPoint remoteEndPoint, Memory<byte> buffer, SocketFlags socketFlags)
{
return await socket.SendToAsync(buffer.ToArray(), socketFlags, remoteEndPoint);
/*
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.SetBuffer(buffer);
args.SocketFlags = socketFlags;
args.RemoteEndPoint = remoteEndPoint;
SocketTask awaitable = new SocketTask(args);
while (buffer.Length > args.BytesTransferred)
{
await socket.SendToAsync(awaitable);
}
return args.BytesTransferred;
*/
}
}