void ReadCallback(IAsyncResult result) { Interlocked.Decrement(ref ActiveListeners); Interlocked.Increment(ref PacketsReceived); var message = (MessageReader)result.AsyncState; int bytesReceived; EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0); //End the receive operation try { bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint); message.Offset = 0; message.Length = bytesReceived; } catch (SocketException) { // Client no longer reachable, pretend it didn't happen // TODO should this not inform the connection this client is lost??? // This thread suggests the IP is not passed out from WinSoc so maybe not possible // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054 message.Recycle(); StartListeningForData(); return; } catch (Exception ex) { //If the socket's been disposed then we can just end there. this.Logger?.Invoke("Stopped due to: " + ex.Message); return; } // I'm a little concerned about a infinite loop here, but it seems like it's possible // to get 0 bytes read on UDP without the socket being shut down. if (bytesReceived == 0) { message.Recycle(); StartListeningForData(); return; } //Begin receiving again StartListeningForData(); bool aware = true; bool hasHelloByte = message.Buffer[0] == (byte)UdpSendOption.Hello; bool isHello = hasHelloByte && message.Length >= MinConnectionLength; //If we're aware of this connection use the one already //If this is a new client then connect with them! UdpServerConnection connection; if (!this.allConnections.TryGetValue(remoteEndPoint, out connection)) { //Check for malformed connection attempts if (!isHello) { message.Recycle(); return; } lock (this.allConnections) { aware = this.allConnections.TryGetValue(remoteEndPoint, out connection); if (!aware) { connection = new UdpServerConnection(this, (IPEndPoint)remoteEndPoint, this.IPMode); if (!this.allConnections.TryAdd(remoteEndPoint, connection)) { throw new Exception(); } } } } //Inform the connection of the buffer (new connections need to send an ack back to client) connection.HandleReceive(message, bytesReceived); //If it's a new connection invoke the NewConnection event. if (!aware) { // Skip header and hello byte; message.Offset = 4; message.Length = bytesReceived - 4; message.Position = 0; InvokeNewConnection(message, connection); } else if (isHello || (!isHello && hasHelloByte)) { message.Recycle(); } }
/// <summary> /// Called when data has been received by the listener. /// </summary> /// <param name="result">The asyncronous operation's result.</param> void ReadCallback(IAsyncResult result) { int bytesReceived; EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0); //End the receive operation try { lock (listener) bytesReceived = listener.EndReceiveFrom(result, ref remoteEndPoint); } catch (ObjectDisposedException) { //If the socket's been disposed then we can just end there. return; } catch (SocketException e) { //Errrr... shit... //Not exactly much we can do if we've got here throw e; } //Exit if no bytes read, we've closed. if (bytesReceived == 0) { return; } //Copy to new buffer byte[] buffer = new byte[bytesReceived]; Buffer.BlockCopy((byte[])result.AsyncState, 0, buffer, 0, bytesReceived); //Begin receiving again StartListeningForData(); bool aware; UdpServerConnection connection; lock (connections) { aware = connections.ContainsKey(remoteEndPoint); //If we're aware of this connection use the one already if (aware) { connection = connections[remoteEndPoint]; } //If this is a new client then connect with them! else { //Check for malformed connection attempts if (buffer[0] != (byte)SendOptionInternal.Hello || buffer.Length != 3) { return; } connection = new UdpServerConnection(this, remoteEndPoint, IPMode); connections.Add(remoteEndPoint, connection); //Then ping back an ack to make sure they're happy (unless we rejected them...) connection.SendAck(buffer[1], buffer[2]); } } //And fire the corresponding event if (aware) { connection.InvokeDataReceived(buffer); } else { InvokeNewConnection(connection); } }
/// <summary> /// Called when data has been received by the listener. /// </summary> /// <param name="result">The asyncronous operation's result.</param> void ReadCallback(IAsyncResult result) { int bytesReceived; EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0); //End the receive operation try { lock (listener) bytesReceived = listener.EndReceiveFrom(result, ref remoteEndPoint); } catch (ObjectDisposedException) { //If the socket's been disposed then we can just end there. return; } catch (SocketException) { //Client no longer reachable, pretend it didn't happen //TODO should this not inform the connection this client is lost??? //This thread suggests the IP is not passed out from WinSoc so maybe not possible //http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054 StartListeningForData(); return; } //Exit if no bytes read, we've closed. if (bytesReceived == 0) { return; } //Copy to new buffer byte[] buffer = new byte[bytesReceived]; Buffer.BlockCopy((byte[])result.AsyncState, 0, buffer, 0, bytesReceived); //Begin receiving again StartListeningForData(); bool aware; UdpServerConnection connection; lock (connections) { aware = connections.ContainsKey(remoteEndPoint); //If we're aware of this connection use the one already if (aware) { connection = connections[remoteEndPoint]; } //If this is a new client then connect with them! else { if (ArrayCompare(buffer, 0, MagicSequence, 0, MagicSequence.Length)) { InvokeUnconnectedData(remoteEndPoint, buffer); return; } //Check for malformed connection attempts if (buffer[0] != (byte)UdpSendOption.Hello) { return; } connection = new UdpServerConnection(this, remoteEndPoint, IPMode); connections.Add(remoteEndPoint, connection); } } connection.LastMessage = DateTime.Now; //Inform the connection of the buffer (new connections need to send an ack back to client) connection.HandleReceive(buffer); //If it's a new connection invoke the NewConnection event. if (!aware) { byte[] dataBuffer = new byte[buffer.Length - 1]; Buffer.BlockCopy(buffer, 1, dataBuffer, 0, buffer.Length - 1); InvokeNewConnection(dataBuffer, connection); } }
void ReadCallback(IAsyncResult result) { var message = (MessageReader)result.AsyncState; int bytesReceived; EndPoint remoteEndPoint = new IPEndPoint(this.EndPoint.Address, this.EndPoint.Port); //End the receive operation try { bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint); message.Offset = 0; message.Length = bytesReceived; } catch (ObjectDisposedException) { message.Recycle(); return; } catch (SocketException sx) { // Client no longer reachable, pretend it didn't happen // TODO should this not inform the connection this client is lost??? // This thread suggests the IP is not passed out from WinSoc so maybe not possible // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054 message.Recycle(); this.Logger?.Invoke($"Socket Ex {sx.SocketErrorCode} in ReadCallback: {sx.Message}"); Thread.Sleep(10); StartListeningForData(); return; } catch (Exception ex) { //If the socket's been disposed then we can just end there. message.Recycle(); this.Logger?.Invoke("Stopped due to: " + ex.Message); return; } // I'm a little concerned about a infinite loop here, but it seems like it's possible // to get 0 bytes read on UDP without the socket being shut down. if (bytesReceived == 0) { message.Recycle(); this.Logger?.Invoke("Received 0 bytes"); Thread.Sleep(10); StartListeningForData(); return; } //Begin receiving again StartListeningForData(); bool aware = true; bool isHello = message.Buffer[0] == (byte)UdpSendOption.Hello; // If we're aware of this connection use the one already // If this is a new client then connect with them! UdpServerConnection connection; if (!this.allConnections.TryGetValue(remoteEndPoint, out connection)) { lock (this.allConnections) { if (!this.allConnections.TryGetValue(remoteEndPoint, out connection)) { // Check for malformed connection attempts if (!isHello) { message.Recycle(); return; } if (AcceptConnection != null) { if (!AcceptConnection((IPEndPoint)remoteEndPoint, message.Buffer, out var response)) { message.Recycle(); if (response != null) { SendData(response, response.Length, remoteEndPoint); } return; } } aware = false; connection = new UdpServerConnection(this, (IPEndPoint)remoteEndPoint, this.IPMode); if (!this.allConnections.TryAdd(remoteEndPoint, connection)) { throw new HazelException("Failed to add a connection. This should never happen."); } } } } // If it's a new connection invoke the NewConnection event. // This needs to happen before handling the message because in localhost scenarios, the ACK and // subsequent messages can happen before the NewConnection event sets up OnDataRecieved handlers if (!aware) { // Skip header and hello byte; message.Offset = 4; message.Length = bytesReceived - 4; message.Position = 0; InvokeNewConnection(message, connection); } // Inform the connection of the buffer (new connections need to send an ack back to client) connection.HandleReceive(message, bytesReceived); if (aware && isHello) { message.Recycle(); } }