public override void Bind(EndPoint localEP) { if (this.ActsAsServer) { throw new Exception("Cannot call {nameof(Bind)} multiple times."); } UnderlyingSocket.Bind(localEP); this.ActsAsServer = true; this.Clients = new ConcurrentDictionary <IPEndPoint, UdpSocket>(); this.ServerTaskCancellationTokenSource = new CancellationTokenSource(); // All "receives" should be handled by this "server" instance. For now, let's try one background thread. ServerTask = Task.Run(async() => { try { var buffer = new byte[4096]; while (true) { // TODO: There should be a TTL on "acting as server" sockets that are "connected" to remote endpoints, since // UDP clients won't ever request a "close". var receive = await UnderlyingSocket.ReceiveFromAsync(buffer, SocketFlags.None, new IPEndPoint(IPAddress.Any, 0 /* any */)); var read = receive.ReceivedBytes; var remoteEndpoint = (IPEndPoint)receive.RemoteEndPoint; var memory = new byte[read].AsMemory(); buffer.AsMemory().Slice(0, read).CopyTo(memory); // See if this is for a "client" socket; if so, queue up for that socket's receive. if (Clients.TryGetValue(remoteEndpoint, out UdpSocket? actualReceiver)) { await actualReceiver.AwaitingReceives.Writer.WriteAsync(memory, ServerTaskCancellationTokenSource.Token); continue; } // Otherwise, queue up for "self" in awaiting accepts. var acceptingSocket = new UdpSocket(UnderlyingSocket) { TrackedRemote = remoteEndpoint }; await acceptingSocket.AwaitingReceives.Writer.WriteAsync(memory); await this.AwaitingAccepts.Writer.WriteAsync(acceptingSocket, ServerTaskCancellationTokenSource.Token); } } catch (Exception e) { Console.WriteLine(e.Message); throw e; } }, ServerTaskCancellationTokenSource.Token); }
public override void Bind(EndPoint localEP) => UnderlyingSocket.Bind(localEP);