/// <summary> /// Reads the next block of data from the socket. /// </summary> private void ReadNextBlock() { Socket socket = null; // check if already closed. lock (m_socketLock) { if (m_socket == null) { if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "ReadNextBlock"); m_receiveBuffer = null; } return; } socket = m_socket; // avoid stale ServiceException when socket is disconnected if (!socket.Connected) { return; } } BufferManager.LockBuffer(m_receiveBuffer); ServiceResult error = ServiceResult.Good; SocketAsyncEventArgs args = new SocketAsyncEventArgs(); try { args.SetBuffer(m_receiveBuffer, m_bytesReceived, m_bytesToReceive - m_bytesReceived); args.Completed += m_ReadComplete; if (!socket.ReceiveAsync(args)) { // I/O completed synchronously if ((args.SocketError != SocketError.Success) || (args.BytesTransferred < (m_bytesToReceive - m_bytesReceived))) { throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, args.SocketError.ToString()); } args.Dispose(); } } catch (ServiceResultException sre) { args.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw sre; } catch (Exception ex) { args.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, ex, "BeginReceive failed."); } }
/// <summary> /// Reads the next block of data from the socket. /// </summary> private void ReadNextBlock() { Socket socket = null; // check if already closed. lock (m_socketLock) { if (m_socket == null) { if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "ReadNextBlock"); m_receiveBuffer = null; } return; } socket = m_socket; } BufferManager.LockBuffer(m_receiveBuffer); try { socket.BeginReceive( m_receiveBuffer, m_bytesReceived, m_bytesToReceive - m_bytesReceived, SocketFlags.None, m_ReadComplete, null); } catch (Exception e) { BufferManager.UnlockBuffer(m_receiveBuffer); throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, e, "BeginReceive failed."); } }
/// <summary> /// Cancels all outstanding I/O operations. /// </summary> private void CancelOperations() { // cancel any outstanding write operations. WriteOperation operation = null; do { operation = null; lock (m_writeQueue) { if (m_writeQueue.Count > 0) { operation = m_writeQueue.First.Value; m_writeQueue.RemoveFirst(); } } if (operation != null) { operation.Fault(StatusCodes.BadConnectionClosed); } } while (operation != null); // cancel any outstanding read operations. byte[] buffer = null; do { buffer = null; lock (m_readQueue) { if (m_readQueue.Count > 0) { buffer = m_readQueue.First.Value.Array; m_readQueue.RemoveFirst(); // check for graceful shutdown. if (buffer != null) { BufferManager.UnlockBuffer(buffer); #if TRACK_MEMORY int cookie = BitConverter.ToInt32(buffer, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (CancelOperations): Cookie={0:X8}", cookie); } #endif } } } if (buffer != null) { m_bufferManager.ReturnBuffer(buffer, "CancelOperations"); } } while (buffer != null); }
/// <summary> /// Removes the next message from the queue. /// </summary> private bool GetNextMessage(out ArraySegment <byte> messageChunk) { lock (m_readQueue) { // check if queue has been cleared. if (m_readQueue.Count == 0) { messageChunk = new ArraySegment <byte>(); m_readThreadActive = false; return(false); } messageChunk = m_readQueue.First.Value; m_readQueue.RemoveFirst(); // return message. if (messageChunk.Array != null) { BufferManager.UnlockBuffer(messageChunk.Array); m_readThreadActive = true; #if TRACK_MEMORY int cookie = BitConverter.ToInt32(messageChunk.Array, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (ReadQueuedMessages1): Cookie={0:X8}", cookie); } #endif return(true); } m_readThreadActive = false; } // handle graceful shutdown. if (m_sink != null) { try { m_sink.OnReceiveError(this, StatusCodes.BadSecureChannelClosed); } catch (Exception e) { Utils.Trace(e, "Unexpected error invoking OnReceiveError callback."); } } return(false); }
/// <summary> /// Handles a read complete event. /// </summary> private ServiceResult DoReadComplete(SocketAsyncEventArgs e) { // complete operation. int bytesRead = e.BytesTransferred; lock (m_socketLock) { BufferManager.UnlockBuffer(m_receiveBuffer); } Utils.TraceDebug("Bytes read: {0}", bytesRead); if (bytesRead == 0) { // Remote end has closed the connection // free the empty receive buffer. if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "DoReadComplete"); m_receiveBuffer = null; } return(ServiceResult.Create(StatusCodes.BadConnectionClosed, "Remote side closed connection")); } m_bytesReceived += bytesRead; // check if more data left to read. if (m_bytesReceived < m_bytesToReceive) { ReadNextBlock(); return(ServiceResult.Good); } // start reading the message body. if (m_incomingMessageSize < 0) { m_incomingMessageSize = BitConverter.ToInt32(m_receiveBuffer, 4); if (m_incomingMessageSize <= 0 || m_incomingMessageSize > m_receiveBufferSize) { Utils.Trace( "BadTcpMessageTooLarge: BufferSize={0}; MessageSize={1}", m_receiveBufferSize, m_incomingMessageSize); return(ServiceResult.Create( StatusCodes.BadTcpMessageTooLarge, "Messages size {1} bytes is too large for buffer of size {0}.", m_receiveBufferSize, m_incomingMessageSize)); } // set up buffer for reading the message body. m_bytesToReceive = m_incomingMessageSize; ReadNextBlock(); return(ServiceResult.Good); } // notify the sink. if (m_sink != null) { try { // send notification (implementor responsible for freeing buffer) on success. ArraySegment <byte> messageChunk = new ArraySegment <byte>(m_receiveBuffer, 0, m_incomingMessageSize); // must allocate a new buffer for the next message. m_receiveBuffer = null; m_sink.OnMessageReceived(this, messageChunk); } catch (Exception ex) { Utils.Trace(ex, "Unexpected error invoking OnMessageReceived callback."); } } // free the receive buffer. if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "DoReadComplete"); m_receiveBuffer = null; } // start receiving next message. ReadNextMessage(); return(ServiceResult.Good); }
/// <summary> /// Reads the next block of data from the socket. /// </summary> private void ReadNextBlock() { Socket socket = null; // check if already closed. lock (m_socketLock) { if (m_socket == null) { if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "ReadNextBlock"); m_receiveBuffer = null; } m_readState = ReadState.NotConnected; return; } socket = m_socket; // avoid stale ServiceException when socket is disconnected if (!socket.Connected) { m_readState = ReadState.NotConnected; return; } } BufferManager.LockBuffer(m_receiveBuffer); var args = new SocketAsyncEventArgs(); try { m_readState = ReadState.Receive; args.SetBuffer(m_receiveBuffer, m_bytesReceived, m_bytesToReceive - m_bytesReceived); args.Completed += m_readComplete; if (!socket.ReceiveAsync(args)) { // I/O completed synchronously if (args.SocketError != SocketError.Success) { throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, args.SocketError.ToString()); } // set state to inner complete m_readState = ReadState.ReadComplete; m_readComplete(null, args); } } catch (ServiceResultException) { args?.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw; } catch (Exception ex) { args?.Dispose(); BufferManager.UnlockBuffer(m_receiveBuffer); throw ServiceResultException.Create(StatusCodes.BadTcpInternalError, ex, "BeginReceive failed."); } }
/// <summary> /// Handles a read complete event. /// </summary> private ServiceResult DoReadComplete(IAsyncResult result) { // complete operation. int bytesRead = 0; lock (m_socketLock) { try { if (m_socket != null) { bytesRead = m_socket.EndReceive(result); // Utils.Trace("EndReceive {0} bytes", bytesRead); } #if TRACK_MEMORY int cookie = BitConverter.ToInt32(m_receiveBuffer, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (EndReceive): Cookie={0:X8}", cookie); } #endif } finally { BufferManager.UnlockBuffer(m_receiveBuffer); } } if (bytesRead == 0) { // free the empty receive buffer. if (m_receiveBuffer != null) { m_bufferManager.ReturnBuffer(m_receiveBuffer, "DoReadComplete"); m_receiveBuffer = null; } // put a null buffer to ensure that all queued messages are processed before close. lock (m_readQueue) { m_readQueue.AddLast(new ArraySegment <byte>()); if (m_readQueue.Count == 1 && !m_readThreadActive) { ThreadPool.QueueUserWorkItem(ReadQueuedMessages, null); } } return(ServiceResult.Good); } // Utils.Trace("Bytes read: {0}", bytesRead); m_bytesReceived += bytesRead; // check if more data left to read. if (m_bytesReceived < m_bytesToReceive) { ReadNextBlock(); #if TRACK_MEMORY int cookie = BitConverter.ToInt32(m_receiveBuffer, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (ReadNextBlock): Cookie={0:X8}", cookie); } #endif return(ServiceResult.Good); } // start reading the message body. if (m_incomingMessageSize < 0) { m_incomingMessageSize = BitConverter.ToInt32(m_receiveBuffer, 4); if (m_incomingMessageSize <= 0 || m_incomingMessageSize > m_receiveBufferSize) { Utils.Trace( "BadTcpMessageTooLarge: BufferSize={0}; MessageSize={1}", m_receiveBufferSize, m_incomingMessageSize); return(ServiceResult.Create( StatusCodes.BadTcpMessageTooLarge, "Messages size {1} bytes is too large for buffer of size {0}.", m_receiveBufferSize, m_incomingMessageSize)); } // set up buffer for reading the message body. m_bytesToReceive = m_incomingMessageSize; ReadNextBlock(); return(ServiceResult.Good); } // add message to queue. ArraySegment <byte> messageChunk = new ArraySegment <byte>(m_receiveBuffer, 0, m_incomingMessageSize); // must allocate a new buffer for the next message. m_receiveBuffer = null; lock (m_readQueue) { #if TRACK_MEMORY int cookie = BitConverter.ToInt32(messageChunk.Array, 0); if (cookie < 0) { Utils.Trace("BufferCookieError (DoReadComplete): Cookie={0:X8}", cookie); } #endif BufferManager.LockBuffer(messageChunk.Array); m_readQueue.AddLast(messageChunk); if (m_readQueue.Count == 1 && !m_readThreadActive) { ThreadPool.QueueUserWorkItem(ReadQueuedMessages, null); } } // start receiving next message. ReadNextMessage(); return(ServiceResult.Good); }