/// <summary> /// Starts the <see cref="UdpServer"/> synchronously and begins accepting client connections asynchronously. /// </summary> /// <exception cref="InvalidOperationException">Attempt is made to <see cref="Start()"/> the <see cref="UdpServer"/> when it is running.</exception> public override void Start() { if (CurrentState == ServerState.NotRunning) { int maxSendQueueSize; // Initialize if unitialized if (!Initialized) Initialize(); // Overwrite config file if max send queue size exists in connection string. if (m_configData.ContainsKey("maxSendQueueSize") && int.TryParse(m_configData["maxSendQueueSize"], out maxSendQueueSize)) m_maxSendQueueSize = maxSendQueueSize; // Bind server socket to local end-point m_udpServer = new TransportProvider<Socket>(); m_udpServer.SetReceiveBuffer(ReceiveBufferSize); m_udpServer.Provider = Transport.CreateSocket(m_configData["interface"], int.Parse(m_configData["port"]), ProtocolType.Udp, m_ipStack, m_allowDualStackSocket); // Disable SocketError.ConnectionReset exception from being thrown when the endpoint is not listening m_udpServer.Provider.IOControl(SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null); // Notify that the server has been started successfully OnServerStarted(); if ((object)m_udpServer.Provider.LocalEndPoint != null) { m_receiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); m_receiveArgs.SocketFlags = SocketFlags.None; m_receiveArgs.Completed += m_receiveHandler; ReceivePayloadAsync(m_receiveArgs); } // We process the static list to clients. foreach (string clientString in m_configData["clients"].Replace(" ", "").Split(',')) { try { Match endpoint = Regex.Match(clientString, Transport.EndpointFormatRegex); if (endpoint != Match.Empty) { UdpClientInfo clientInfo; TransportProvider<EndPoint> udpClient = new TransportProvider<EndPoint>(); IPEndPoint clientEndpoint = Transport.CreateEndPoint(endpoint.Groups["host"].Value, int.Parse(endpoint.Groups["port"].Value), m_ipStack); udpClient.SetReceiveBuffer(ReceiveBufferSize); udpClient.SetSendBuffer(SendBufferSize); udpClient.Provider = clientEndpoint; // If the IP specified for the client is a multicast IP, subscribe to the specified multicast group. if (Transport.IsMulticastIP(clientEndpoint.Address)) { string multicastSource; if (m_configData.TryGetValue("multicastSource", out multicastSource)) { IPAddress sourceAddress = IPAddress.Parse(multicastSource); IPAddress localAddress = (clientEndpoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any); if (sourceAddress.AddressFamily != clientEndpoint.AddressFamily) throw new InvalidOperationException(string.Format("Source address \"{0}\" is not in the same IP format as server address \"{1}\"", sourceAddress, clientEndpoint.Address)); if (localAddress.AddressFamily != clientEndpoint.AddressFamily) throw new InvalidOperationException(string.Format("Local address \"{0}\" is not in the same IP format as server address \"{1}\"", localAddress, clientEndpoint.Address)); MemoryStream membershipAddresses = new MemoryStream(); byte[] serverAddressBytes = clientEndpoint.Address.GetAddressBytes(); byte[] sourceAddressBytes = sourceAddress.GetAddressBytes(); byte[] localAddressBytes = localAddress.GetAddressBytes(); membershipAddresses.Write(serverAddressBytes, 0, serverAddressBytes.Length); membershipAddresses.Write(sourceAddressBytes, 0, sourceAddressBytes.Length); membershipAddresses.Write(localAddressBytes, 0, localAddressBytes.Length); udpClient.MulticastMembershipAddresses = membershipAddresses.ToArray(); // Execute multicast subscribe for specific source SocketOptionLevel level = clientEndpoint.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP; m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddSourceMembership, udpClient.MulticastMembershipAddresses); m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"])); } else { // Execute multicast subscribe for any source SocketOptionLevel level = clientEndpoint.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP; m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddMembership, new MulticastOption(clientEndpoint.Address)); m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"])); } } clientInfo = new UdpClientInfo() { Client = udpClient, SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(), SendLock = new SpinLock(), SendQueue = new ConcurrentQueue<UdpServerPayload>() }; clientInfo.SendArgs.RemoteEndPoint = udpClient.Provider; clientInfo.SendArgs.SetBuffer(udpClient.SendBuffer, 0, udpClient.SendBufferSize); clientInfo.SendArgs.Completed += m_sendHandler; m_clientInfoLookup.TryAdd(udpClient.ID, clientInfo); OnClientConnected(udpClient.ID); } } catch (Exception ex) { string errorMessage = string.Format("Unable to connect to client {0}: {1}", clientString, ex.Message); OnClientConnectingException(new Exception(errorMessage, ex)); } } } else { throw new InvalidOperationException("Server is currently running"); } }
private TransportProvider<EndPoint> AddUdpClient(EndPoint udpClientEndPoint) { TransportProvider<EndPoint> udpClient = new TransportProvider<EndPoint>(); IPEndPoint udpClientIPEndPoint = udpClientEndPoint as IPEndPoint; UdpClientInfo udpClientInfo; // Set up client udpClient.SetReceiveBuffer(ReceiveBufferSize); udpClient.SetSendBuffer(SendBufferSize); udpClient.Provider = udpClientIPEndPoint; // If the IP specified for the client is a multicast IP, subscribe to the specified multicast group. if ((object)udpClientIPEndPoint != null && Transport.IsMulticastIP(udpClientIPEndPoint.Address)) { SocketOptionLevel level = udpClientIPEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP; string multicastSource; if (m_configData.TryGetValue("multicastSource", out multicastSource)) { IPAddress sourceAddress = IPAddress.Parse(multicastSource); IPAddress localAddress = (udpClientIPEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any); if (sourceAddress.AddressFamily != udpClientIPEndPoint.AddressFamily) throw new InvalidOperationException($"Source address \"{sourceAddress}\" is not in the same IP format as server address \"{udpClientIPEndPoint.Address}\""); if (localAddress.AddressFamily != udpClientIPEndPoint.AddressFamily) throw new InvalidOperationException($"Local address \"{localAddress}\" is not in the same IP format as server address \"{udpClientIPEndPoint.Address}\""); using (BlockAllocatedMemoryStream membershipAddresses = new BlockAllocatedMemoryStream()) { byte[] serverAddressBytes = udpClientIPEndPoint.Address.GetAddressBytes(); byte[] sourceAddressBytes = sourceAddress.GetAddressBytes(); byte[] localAddressBytes = localAddress.GetAddressBytes(); membershipAddresses.Write(serverAddressBytes, 0, serverAddressBytes.Length); membershipAddresses.Write(sourceAddressBytes, 0, sourceAddressBytes.Length); membershipAddresses.Write(localAddressBytes, 0, localAddressBytes.Length); udpClient.MulticastMembershipAddresses = membershipAddresses.ToArray(); } // Execute multicast subscribe for specific source m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddSourceMembership, udpClient.MulticastMembershipAddresses); m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"])); } else { // Execute multicast subscribe for any source m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddMembership, new MulticastOption(udpClientIPEndPoint.Address)); m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"])); } } // Create client info object udpClientInfo = new UdpClientInfo { Client = udpClient, SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(), SendLock = new object(), SendQueue = new ConcurrentQueue<UdpServerPayload>() }; udpClientInfo.DumpPayloadsOperation = new ShortSynchronizedOperation(() => { UdpServerPayload payload; // Check to see if the client has reached the maximum send queue size. if (m_maxSendQueueSize > 0 && udpClientInfo.SendQueue.Count >= m_maxSendQueueSize) { for (int i = 0; i < m_maxSendQueueSize; i++) { if (udpClientInfo.SendQueue.TryDequeue(out payload)) { payload.WaitHandle.Set(); payload.WaitHandle.Dispose(); payload.WaitHandle = null; } } throw new InvalidOperationException($"Client {udpClientInfo.Client.ID} connected to UDP server reached maximum send queue size. {m_maxSendQueueSize} payloads dumped from the queue."); } }, ex => OnSendClientDataException(udpClientInfo.Client.ID, ex)); // Set up SocketAsyncEventArgs udpClientInfo.SendArgs.RemoteEndPoint = udpClient.Provider; udpClientInfo.SendArgs.SetBuffer(udpClient.SendBuffer, 0, udpClient.SendBufferSize); udpClientInfo.SendArgs.Completed += m_sendHandler; // Add new client to the lookup m_clientInfoLookup.TryAdd(udpClient.ID, udpClientInfo); OnClientConnected(udpClient.ID); return udpClient; }
/// <summary> /// Callback method for asynchronous accept operation. /// </summary> private void ProcessAccept(SocketAsyncEventArgs acceptArgs) { TransportProvider<Socket> client = new TransportProvider<Socket>(); SocketAsyncEventArgs receiveArgs = null; WindowsPrincipal clientPrincipal = null; TcpClientInfo clientInfo; try { if (CurrentState == ServerState.NotRunning) return; // If acceptArgs was disposed, m_acceptArgs will either // be null or another instance of SocketAsyncEventArgs. // This check will tell us whether it's been disposed. if ((object)acceptArgs != m_acceptArgs) return; if (acceptArgs.SocketError != SocketError.Success) { // Error is unrecoverable. // We need to make sure to restart the // server before we throw the error. SocketError error = acceptArgs.SocketError; ThreadPool.QueueUserWorkItem(state => ReStart()); throw new SocketException((int)error); } // Process the newly connected client. client.Provider = acceptArgs.AcceptSocket; client.Provider.ReceiveBufferSize = ReceiveBufferSize; // Set up SocketAsyncEventArgs for receive operations. receiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); receiveArgs.Completed += ReceiveHandler; // Return to accepting new connections. acceptArgs.AcceptSocket = null; if (!m_tcpServer.AcceptAsync(acceptArgs)) { ThreadPool.QueueUserWorkItem(state => ProcessAccept(acceptArgs)); } #if !MONO // Authenticate the connected client Windows credentials. if (m_integratedSecurity) { NetworkStream socketStream = null; NegotiateStream authenticationStream = null; try { socketStream = new NetworkStream(client.Provider); authenticationStream = new NegotiateStream(socketStream); authenticationStream.AuthenticateAsServer(); if (authenticationStream.RemoteIdentity is WindowsIdentity) clientPrincipal = new WindowsPrincipal((WindowsIdentity)authenticationStream.RemoteIdentity); } catch (InvalidCredentialException) { if (!m_ignoreInvalidCredentials) throw; } finally { if (socketStream != null) socketStream.Dispose(); if (authenticationStream != null) authenticationStream.Dispose(); } } #endif if (MaxClientConnections != -1 && ClientIDs.Length >= MaxClientConnections) { // Reject client connection since limit has been reached. TerminateConnection(client, receiveArgs, false); } else { // We can proceed further with receiving data from the client. clientInfo = new TcpClientInfo { Client = client, SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(), SendLock = new object(), SendQueue = new ConcurrentQueue<TcpServerPayload>(), ClientPrincipal = clientPrincipal }; // Create operation to dump send queue payloads when the queue grows too large. clientInfo.DumpPayloadsOperation = new ShortSynchronizedOperation(() => { TcpServerPayload payload; // Check to see if the client has reached the maximum send queue size. if (m_maxSendQueueSize > 0 && clientInfo.SendQueue.Count >= m_maxSendQueueSize) { for (int i = 0; i < m_maxSendQueueSize; i++) { if (clientInfo.SendQueue.TryDequeue(out payload)) { payload.WaitHandle.Set(); payload.WaitHandle.Dispose(); payload.WaitHandle = null; } } throw new InvalidOperationException(string.Format("Client {0} connected to TCP server reached maximum send queue size. {1} payloads dumped from the queue.", clientInfo.Client.ID, m_maxSendQueueSize)); } }, ex => OnSendClientDataException(clientInfo.Client.ID, ex)); // Set up socket args. client.SetSendBuffer(SendBufferSize); clientInfo.SendArgs.Completed += m_sendHandler; clientInfo.SendArgs.SetBuffer(client.SendBuffer, 0, client.SendBufferSize); m_clientInfoLookup.TryAdd(client.ID, clientInfo); OnClientConnected(client.ID); if (!m_payloadAware) { receiveArgs.UserToken = client; } else { EventArgs<TransportProvider<Socket>, bool> userToken = FastObjectFactory<EventArgs<TransportProvider<Socket>, bool>>.CreateObjectFunction(); userToken.Argument1 = client; receiveArgs.UserToken = userToken; } ReceivePayloadAsync(client, receiveArgs); } } catch (ObjectDisposedException) { // m_acceptArgs may be disposed while in the middle of accepting a connection } catch (Exception ex) { // Notify of the exception. if ((object)client.Provider != null && (object)client.Provider.RemoteEndPoint != null) { string clientAddress = ((IPEndPoint)client.Provider.RemoteEndPoint).Address.ToString(); string errorMessage = string.Format("Unable to accept connection to client [{0}]: {1}", clientAddress, ex.Message); OnClientConnectingException(new Exception(errorMessage, ex)); } else { string errorMessage = string.Format("Unable to accept connection to client [unknown]: {0}", ex.Message); OnClientConnectingException(new Exception(errorMessage, ex)); } if ((object)receiveArgs != null) { TerminateConnection(client, receiveArgs, false); } } }
/// <summary> /// Callback method for asynchronous accept operation. /// </summary> private void ProcessAccept() { TransportProvider<Socket> client = new TransportProvider<Socket>(); SocketAsyncEventArgs receiveArgs = null; TcpClientInfo clientInfo; try { if (CurrentState == ServerState.NotRunning) return; if (m_acceptArgs.SocketError != SocketError.Success) { // Error is unrecoverable. // We need to make sure to restart the // server before we throw the error. SocketError error = m_acceptArgs.SocketError; ThreadPool.QueueUserWorkItem(state => ReStart()); throw new SocketException((int)error); } // Process the newly connected client. client.Provider = m_acceptArgs.AcceptSocket; // Set up SocketAsyncEventArgs for receive operations. receiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(); receiveArgs.Completed += ReceiveHandler; // Return to accepting new connections. m_acceptArgs.AcceptSocket = null; if (!m_tcpServer.AcceptAsync(m_acceptArgs)) { ThreadPool.QueueUserWorkItem(state => ProcessAccept()); } #if !MONO // Authenticate the connected client Windows credentials. if (m_integratedSecurity) { NetworkStream socketStream = null; NegotiateStream authenticationStream = null; try { socketStream = new NetworkStream(client.Provider); authenticationStream = new NegotiateStream(socketStream); authenticationStream.AuthenticateAsServer(); if (authenticationStream.RemoteIdentity is WindowsIdentity) Thread.CurrentPrincipal = new WindowsPrincipal((WindowsIdentity)authenticationStream.RemoteIdentity); } finally { if (socketStream != null) socketStream.Dispose(); if (authenticationStream != null) authenticationStream.Dispose(); } } #endif if (MaxClientConnections != -1 && ClientIDs.Length >= MaxClientConnections) { // Reject client connection since limit has been reached. TerminateConnection(client, receiveArgs, false); } else { // We can proceed further with receiving data from the client. clientInfo = new TcpClientInfo() { Client = client, SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(), SendLock = new SpinLock(), SendQueue = new ConcurrentQueue<TcpServerPayload>() }; // Set up socket args. client.SetSendBuffer(SendBufferSize); clientInfo.SendArgs.Completed += m_sendHandler; clientInfo.SendArgs.SetBuffer(client.SendBuffer, 0, client.SendBufferSize); m_clientInfoLookup.TryAdd(client.ID, clientInfo); OnClientConnected(client.ID); if (!m_payloadAware) { receiveArgs.UserToken = client; } else { EventArgs<TransportProvider<Socket>, bool> userToken = ReusableObjectPool<EventArgs<TransportProvider<Socket>, bool>>.Default.TakeObject(); userToken.Argument1 = client; receiveArgs.UserToken = userToken; } ReceivePayloadAsync(client, receiveArgs); } } catch (Exception ex) { // Notify of the exception. if ((object)client.Provider != null) { string clientAddress = ((IPEndPoint)client.Provider.RemoteEndPoint).Address.ToString(); string errorMessage = string.Format("Unable to accept connection to client [{0}]: {1}", clientAddress, ex.Message); OnClientConnectingException(new Exception(errorMessage, ex)); } if ((object)receiveArgs != null) { TerminateConnection(client, receiveArgs, false); } } }
private TransportProvider<EndPoint> AddUdpClient(EndPoint udpClientEndPoint) { TransportProvider<EndPoint> udpClient = new TransportProvider<EndPoint>(); IPEndPoint udpClientIPEndPoint = udpClientEndPoint as IPEndPoint; UdpClientInfo udpClientInfo; // Set up client udpClient.SetReceiveBuffer(ReceiveBufferSize); udpClient.SetSendBuffer(SendBufferSize); udpClient.Provider = udpClientIPEndPoint; // If the IP specified for the client is a multicast IP, subscribe to the specified multicast group. if ((object)udpClientIPEndPoint != null && Transport.IsMulticastIP(udpClientIPEndPoint.Address)) { SocketOptionLevel level = udpClientIPEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP; string multicastSource; if (m_configData.TryGetValue("multicastSource", out multicastSource)) { IPAddress sourceAddress = IPAddress.Parse(multicastSource); IPAddress localAddress = (udpClientIPEndPoint.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any); if (sourceAddress.AddressFamily != udpClientIPEndPoint.AddressFamily) throw new InvalidOperationException(string.Format("Source address \"{0}\" is not in the same IP format as server address \"{1}\"", sourceAddress, udpClientIPEndPoint.Address)); if (localAddress.AddressFamily != udpClientIPEndPoint.AddressFamily) throw new InvalidOperationException(string.Format("Local address \"{0}\" is not in the same IP format as server address \"{1}\"", localAddress, udpClientIPEndPoint.Address)); MemoryStream membershipAddresses = new MemoryStream(); byte[] serverAddressBytes = udpClientIPEndPoint.Address.GetAddressBytes(); byte[] sourceAddressBytes = sourceAddress.GetAddressBytes(); byte[] localAddressBytes = localAddress.GetAddressBytes(); membershipAddresses.Write(serverAddressBytes, 0, serverAddressBytes.Length); membershipAddresses.Write(sourceAddressBytes, 0, sourceAddressBytes.Length); membershipAddresses.Write(localAddressBytes, 0, localAddressBytes.Length); udpClient.MulticastMembershipAddresses = membershipAddresses.ToArray(); // Execute multicast subscribe for specific source m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddSourceMembership, udpClient.MulticastMembershipAddresses); m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"])); } else { // Execute multicast subscribe for any source m_udpServer.Provider.SetSocketOption(level, SocketOptionName.AddMembership, new MulticastOption(udpClientIPEndPoint.Address)); m_udpServer.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_configData["multicastTimeToLive"])); } } // Create client info object udpClientInfo = new UdpClientInfo() { Client = udpClient, SendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction(), SendLock = new SpinLock(), SendQueue = new ConcurrentQueue<UdpServerPayload>() }; // Set up SocketAsyncEventArgs udpClientInfo.SendArgs.RemoteEndPoint = udpClient.Provider; udpClientInfo.SendArgs.SetBuffer(udpClient.SendBuffer, 0, udpClient.SendBufferSize); udpClientInfo.SendArgs.Completed += m_sendHandler; // Add new client to the lookup m_clientInfoLookup.TryAdd(udpClient.ID, udpClientInfo); OnClientConnected(udpClient.ID); return udpClient; }