/// <summary> /// Creates a new AsyncSocket. You must call Start() after creating the AsyncSocket /// in order to begin receive data. /// </summary> internal AsyncUdpSocket(Socket socket, IPEndPoint localEndpoint, Action <IPEndPoint, byte[], int> onReceive, Action <Exception> onError) { Socket = socket; OnReceive = onReceive; OnError = onError; LocalEndPoint = localEndpoint; RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); TargetEndPoint = RemoteEndPoint; _sendLock = new Lock(); _readLock = new Lock(); _enqueueBuffer = new BufferQueue(); _sendQueue = new Shared <ArrayRig <QueuedBuffer> >(new ArrayRig <QueuedBuffer>()); _syncLock = new LockReadWrite(); _onReceive = new ActionSequence(); _receiveBuffer = BufferCache.Get(); _receivedChunks = new Dictionary <int, ChunkedGram>(); _disposing = false; }
/// <summary> /// Start sending the enqueued bytes to the remote endpoint. /// </summary> public void Send(IAction onSent = null) { if (!_sendLock.TryTake) { _sendQueue.Take(); _sendQueue.Item.Add(new QueuedBuffer { EndPoint = TargetEndPoint, Buffer = _enqueueBuffer }); _enqueueBuffer = null; _sendQueue.Release(); return; } // ensure not disposing if (_disposing) { _sendLock.Release(); return; } _onSent = onSent; SetSendBuffer(_enqueueBuffer, TargetEndPoint); _enqueueBuffer = null; try { // start sending the bytes in the buffer StartSend(); } catch (SocketException ex) { _sendLock.Release(); if (ex.SocketErrorCode == SocketError.ConnectionReset) { return; } ProcessError(ex); } catch (Exception ex) { _sendLock.Release(); // handle the error outside the lock ProcessError(ex); } }
/// <summary> /// Set the buffer to be sent. /// </summary> private void SetSendBuffer(BufferQueue buffer, IPEndPoint endPoint) { _sendBuffer = buffer; TargetEndPoint = endPoint; // should the buffer be chunked? if (_sendBuffer.Length > MaxDatagramSize) { // yes, set some of the chunked details _chunked = true; _chunkIndex = 0; _chunkedId = Randomize.Range(int.MinValue, int.MaxValue); _chunkCount = (int)Math.Ceiling((double)_sendBuffer.Length / (MaxDatagramSize - ChunkHeaderSize)); } else { // no _chunked = false; } }
/// <summary> /// Enqueue data to be sent to the remote host /// </summary> public void Enqueue(byte[] buffer, int index, int count) { // skip 0 bytes if (count == 0) { return; } // does the enqueue buffer need to be created? yes, create it if (_enqueueBuffer == null) { _sendQueue.Take(); if (_enqueueBuffer == null) { _enqueueBuffer = new BufferQueue(); } _sendQueue.Release(); } // enqueue bytes from the stream _enqueueBuffer.Enqueue(buffer, index, count); }
/// <summary> /// Enqueue bytes from the specified stream to be sent. /// </summary> public void Enqueue(Stream stream, int length) { // skip 0 length if (length == 0) { return; } // does the enqueue buffer need to be created? yes, create it if (_enqueueBuffer == null) { _sendQueue.Take(); if (_enqueueBuffer == null) { _enqueueBuffer = new BufferQueue(); } _sendQueue.Release(); } // enqueue bytes from the stream _enqueueBuffer.Enqueue(stream, length); }
/// <summary> /// Remove data that has been sent from the queue. Returns true if we should send more. /// Make sure slimLock is entered in read mode before calling this, and that disposing is not 1. /// Ensure an error handler is implemented which forwards to ProcessError. /// </summary> private bool CompleteSend(SocketAsyncEventArgs args) { // did it fail? if (args.SocketError != SocketError.Success) { _sendLock.Release(); BufferCache.Set(args.Buffer); // it failed, let's invoke the error handler if (!_disposing) { ProcessError("Socket error '" + args.SocketError + "'."); } args.Dispose(); return(false); } BufferCache.Set(args.Buffer); args.Dispose(); // is there a callback? yes, run it if (_onSent != null) { _onSent.Run(); } // more data to send? yes, return positive if (_sendBuffer.Length > 0) { return(true); } // more buffers in queue? if (_sendQueue.TakeItem().Count > 0) { // yes, recycle send buffer if appropriate if (_enqueueBuffer == null) { _enqueueBuffer = _sendBuffer; } // get the next buffer to send QueuedBuffer queuedBuffer = _sendQueue.Item.RemoveReturn(0); _sendQueue.Release(); // set the send buffer SetSendBuffer(queuedBuffer.Buffer, queuedBuffer.EndPoint); return(true); } _sendQueue.Release(); _sendLock.Release(); // more data to send? yes, take the lock and return positive if (_sendBuffer.Length > 0 && _sendLock.TryTake) { return(true); } // more buffers in the queue? yes, take the lock and return positive if (_sendQueue.TakeItem().Count > 0 && _sendLock.TryTake) { // yes, recycle send buffer if appropriate if (_enqueueBuffer == null) { _enqueueBuffer = _sendBuffer; } // get the next buffer to send QueuedBuffer queuedBuffer = _sendQueue.Item.RemoveReturn(0); _sendQueue.Release(); // set the send buffer SetSendBuffer(queuedBuffer.Buffer, queuedBuffer.EndPoint); return(true); } _sendQueue.Release(); return(false); }