/// <summary> /// Read data from socket into buffer and process. Check if disposed after calling this, because /// this method will temporarily release the lock. /// </summary> private void ReceiveSocketData(SocketAsyncEventArgs args) { try { _syncLock.TakeRead(); // exit if we're disposing if (_disposing) { _syncLock.ReleaseRead(); return; } if (args.SocketError == SocketError.Success) { // how many bytes did we receive? int count = args.BytesTransferred; if (count > 0) { // enqueue these bytes to read _receivedBuffer.TakeItem().Enqueue(args.Buffer, 0, count); _receivedBuffer.Release(); if (_readLock.TryTake) { ProcessReceivedData(); } } // deallocate the arguments _syncLock.ReleaseRead(); } else { // deallocate the arguments _syncLock.ReleaseRead(); if (_disposing) { return; } ProcessError("Socket Receive error '" + args.SocketError + "'."); } SocketEventArgsCache.DeallocateForReceive(args, OnSocketReceive); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.ConnectionReset) { SocketEventArgsCache.DeallocateForReceive(args, OnSocketReceive); Log.Warning("Socket connection to '" + RemoteEndPoint + "' was reset."); } _syncLock.ReleaseRead(); ProcessError(ex); } catch (Exception ex) { // we want to catch exceptions outside of the lock _syncLock.ReleaseRead(); ProcessError(ex); } }
/// <summary> /// Start receiving data from the socket. /// </summary> public void Start() { // don't start if disposing if (_disposing) { return; } // ensure we don't start multiple times if (Interlocked.CompareExchange(ref _started, 1, 0) == 1) { // already started! can't call Start() twice. throw new InvalidOperationException("Cannot call AsyncSocket.Start() more than once."); } // configure for receive var args = SocketEventArgsCache.AllocateForReceive(OnSocketReceive); // start a receive request and immediately check to see if the receive is already complete // otherwise OnIOCompleted will get called when the receive is complete if (!Socket.ReceiveAsync(args)) { ReceiveSocketData(args); } }
/// <summary> /// Creates a new AsyncSocket. You must call Start() after creating the AsyncSocket /// in order to begin receive data. /// </summary> internal AsyncTcpSocket(Socket socket, IPEndPoint remoteEndpoint, IPEndPoint localEndpoint, Action <byte[], int> onReceive, Action <Exception> onError) { Socket = socket; OnReceive = onReceive; OnError = onError; RemoteEndPoint = remoteEndpoint; LocalEndPoint = localEndpoint; _sendLock = new Lock(); _readLock = new Lock(); _callbackLock = new Lock(); _sendBuffer = new Shared <BufferQueue>(new BufferQueue()); _syncLock = new LockReadWrite(); _receivedBuffer = new Shared <BufferQueue>(new BufferQueue()); _onReceive = new ActionSequence(); _callbacks = new Queue <Teple <int, IAction> >(); _sendSocketArgs = SocketEventArgsCache.AllocateForSend(OnSocketSend); _sendSocketArgs.SendPacketsFlags = TransmitFileOptions.UseKernelApc | TransmitFileOptions.UseSystemThread; _sendSocketArgs.RemoteEndPoint = RemoteEndPoint; _disposing = false; }
/// <summary> /// Complete processing a buffer of bytes read from the socket. /// </summary> private void CompleteReceive() { // ReadSocketData temporarily releases the lock (and then enters it again), so we need to ensure // we haven't disposed in the meantime if (_disposing) { _readLock.Release(); return; } if (_receivedBuffer.TakeItem().Length > 0) { _receivedBuffer.Release(); ManagerUpdate.Control.AddSingle(ProcessReceivedData); return; } _receivedBuffer.Release(); // flop the processing flag _readLock.Release(); // any bytes in the queue? if (_receivedBuffer.TakeItem().Length > 0 && _readLock.TryTake) { _receivedBuffer.Release(); ManagerUpdate.Control.AddSingle(ProcessReceivedData); return; } _receivedBuffer.Release(); // socket may have been closed already due to connection error. // if not try receiving more data if (Socket.Connected) { // receiveAsync returns true if the I/O operation is pending. An event will be raised upon completion. // returns false if the I/O operation completed synchronously. var args = SocketEventArgsCache.AllocateForReceive(OnSocketReceive); if (!Socket.ReceiveAsync(args)) { ReceiveSocketData(args); } } else { if (_disposing) { return; } ProcessError("Cannot receive, socket is not connected."); } }
/// <summary> /// Dispose this AsyncSocket. Closes the underlying socket if required. /// </summary> public void Dispose() { // ensure we haven't already disposed _syncLock.TakeWrite(); if (_disposing) { _syncLock.ReleaseWrite(); return; } _disposing = true; _syncLock.ReleaseWrite(); // run sender dispose code if (_sendSocketArgs != null) { SocketEventArgsCache.DeallocateForSend(_sendSocketArgs, OnSocketSend); _sendSocketArgs = null; } }