private void ReceiveCallback(Socket socket, SocketAsyncEventArgs socketAsyncEventArgs) { // Check for error if (socketAsyncEventArgs.SocketError != SocketError.Success) { HandleCommunicationError(socket, new Exception("Receive failed, error = " + socketAsyncEventArgs.SocketError)); } // Get the message state int bytesRead = socketAsyncEventArgs.BytesTransferred; // Read the data if (bytesRead > 0) { // Add to receive queue BlockingQueue <SocketAsyncEventArgs> receiveBufferQueue = null; _currentlyConnectedClientsReceiveQueuesLock.EnterReadLock(); try { if (!_currentlyConnectedClientsReceiveQueues.TryGetValue(socket, out receiveBufferQueue)) { // Peace out! return; } } finally { _currentlyConnectedClientsReceiveQueuesLock.ExitReadLock(); } receiveBufferQueue.Enqueue(socketAsyncEventArgs); } else { // 0 bytes means disconnect HandleCommunicationError(socket, new Exception("Received 0 bytes (graceful disconnect)")); _socketAsyncEventArgsReceivePool.Push(socketAsyncEventArgs); return; } socketAsyncEventArgs = _socketAsyncEventArgsReceivePool.Pop(); // Post a receive to the socket as the client will be continuously receiving messages to be pushed to the queue TryUnsafeSocketOperation(socket, SocketAsyncOperation.Receive, socketAsyncEventArgs); }
private void ProcessReceivedMessage(ConnectedClient connectedClient) { int bytesToRead = -1; int threadId = -1; int availableTest = 0; int controlBytesOffset = 0; byte[] protocolBuffer = new byte[ProtocolHelper.ControlBytesPlaceholder.Length]; byte[] resultBuffer = null; var handler = connectedClient.Socket; BlockingQueue <SocketAsyncEventArgs> receiveBufferQueue = null; _currentlyConnectedClientsReceiveQueuesLock.EnterReadLock(); try { if (!_currentlyConnectedClientsReceiveQueues.TryGetValue(handler, out receiveBufferQueue)) { // Peace out! return; } } finally { _currentlyConnectedClientsReceiveQueuesLock.ExitReadLock(); } // Loop until socket is done while (_isListening) { // If the socket is disposed, we're done try { availableTest = handler.Available; } catch (ObjectDisposedException) { // Peace out! return; } // Get the next buffer from the queue var socketAsyncEventArgs = receiveBufferQueue.Dequeue(); if (socketAsyncEventArgs == null) { continue; } var buffer = socketAsyncEventArgs.Buffer; int bytesRead = socketAsyncEventArgs.BytesTransferred; int currentOffset = 0; while (currentOffset < bytesRead) { // Check if we need to get our control byte values if (bytesToRead == -1) { var controlBytesNeeded = ProtocolHelper.ControlBytesPlaceholder.Length - controlBytesOffset; var controlBytesAvailable = bytesRead - currentOffset; var controlBytesToCopy = Math.Min(controlBytesNeeded, controlBytesAvailable); // Copy bytes to control buffer Buffer.BlockCopy(buffer, currentOffset, protocolBuffer, controlBytesOffset, controlBytesToCopy); controlBytesOffset += controlBytesToCopy; currentOffset += controlBytesToCopy; // Check if done if (controlBytesOffset == ProtocolHelper.ControlBytesPlaceholder.Length) { // Parse out control bytes ProtocolHelper.ExtractControlBytes(protocolBuffer, out bytesToRead, out threadId); // Reset control bytes offset controlBytesOffset = 0; // Ensure message is not larger than maximum message size if (bytesToRead > _maxMessageSize) { HandleCommunicationError(handler, new InvalidOperationException(string.Format("message of length {0} exceeds maximum message length of {1}", bytesToRead, _maxMessageSize))); return; } } // Continue the loop continue; } // Have control bytes, get message bytes // SPECIAL CASE: if empty message, skip a bunch of stuff if (bytesToRead != 0) { // Initialize buffer if needed if (resultBuffer == null) { resultBuffer = new byte[bytesToRead]; } var bytesAvailable = bytesRead - currentOffset; var bytesToCopy = Math.Min(bytesToRead, bytesAvailable); // Copy bytes to buffer Buffer.BlockCopy(buffer, currentOffset, resultBuffer, resultBuffer.Length - bytesToRead, bytesToCopy); currentOffset += bytesToCopy; bytesToRead -= bytesToCopy; } // Check if we're done if (bytesToRead == 0) { if (resultBuffer != null) { // Done, add to complete received messages CompleteMessage(handler, threadId, resultBuffer); // Reset message state resultBuffer = null; } bytesToRead = -1; threadId = -1; connectedClient.LastResponse = DateTime.UtcNow; } } // Push the buffer back onto the pool _socketAsyncEventArgsReceivePool.Push(socketAsyncEventArgs); } }
/// <summary> /// The constructor. /// </summary> /// <param name="socketFunc">The function that creates a new socket. Use this to specify your socket constructor and initialize settings.</param> /// <param name="messageBufferSize">The message buffer size to use for send/receive.</param> /// <param name="communicationTimeout">The communication timeout, in milliseconds.</param> /// <param name="maxMessageSize">The maximum message size.</param> /// <param name="useNagleAlgorithm">Whether or not to use the Nagle algorithm.</param> public SimplSocketClient(Func <Socket> socketFunc, int messageBufferSize = 65536, int communicationTimeout = 10000, int maxMessageSize = 10 * 1024 * 1024, bool useNagleAlgorithm = false) { // Sanitize if (socketFunc == null) { throw new ArgumentNullException("socketFunc"); } if (messageBufferSize < 512) { throw new ArgumentException("must be >= 512", "messageBufferSize"); } if (communicationTimeout < 5000) { throw new ArgumentException("must be >= 5000", "communicationTimeout"); } if (maxMessageSize < 1024) { throw new ArgumentException("must be >= 1024", "maxMessageSize"); } _socketFunc = socketFunc; _messageBufferSize = messageBufferSize; _communicationTimeout = communicationTimeout; _maxMessageSize = maxMessageSize; _useNagleAlgorithm = useNagleAlgorithm; _sendBufferQueue = new BlockingQueue <SocketAsyncEventArgs>(); _receiveBufferQueue = new BlockingQueue <SocketAsyncEventArgs>(); // Initialize the client multiplexer _clientMultiplexer = new Dictionary <int, MultiplexerData>(PREDICTED_THREAD_COUNT); // Create the pools _multiplexerDataPool = new Pool <MultiplexerData>(PREDICTED_THREAD_COUNT, () => new MultiplexerData { ManualResetEvent = new ManualResetEvent(false) }, multiplexerData => { multiplexerData.Message = null; multiplexerData.ManualResetEvent.Reset(); }); _socketAsyncEventArgsSendPool = new Pool <SocketAsyncEventArgs>(PREDICTED_THREAD_COUNT, () => { var poolItem = new SocketAsyncEventArgs(); poolItem.Completed += OperationCallback; return(poolItem); }); _socketAsyncEventArgsReceivePool = new Pool <SocketAsyncEventArgs>(PREDICTED_THREAD_COUNT, () => { var poolItem = new SocketAsyncEventArgs(); poolItem.SetBuffer(new byte[messageBufferSize], 0, messageBufferSize); poolItem.Completed += OperationCallback; return(poolItem); }); _socketAsyncEventArgsKeepAlivePool = new Pool <SocketAsyncEventArgs>(PREDICTED_THREAD_COUNT, () => { var poolItem = new SocketAsyncEventArgs(); poolItem.SetBuffer(ProtocolHelper.ControlBytesPlaceholder, 0, ProtocolHelper.ControlBytesPlaceholder.Length); poolItem.Completed += OperationCallback; return(poolItem); }); _receivedMessagePool = new Pool <ReceivedMessage>(PREDICTED_THREAD_COUNT, () => new ReceivedMessage(), receivedMessage => { receivedMessage.Message = null; receivedMessage.Socket = null; }); _messageReceivedArgsPool = new Pool <MessageReceivedArgs>(PREDICTED_THREAD_COUNT, () => new MessageReceivedArgs(), messageReceivedArgs => { messageReceivedArgs.ReceivedMessage = null; }); _socketErrorArgsPool = new Pool <SocketErrorArgs>(PREDICTED_THREAD_COUNT, () => new SocketErrorArgs(), socketErrorArgs => { socketErrorArgs.Exception = null; }); }