/// <summary> /// Send data to the remote host /// </summary> public void Send(byte[] buffer, int index, int count, IAction onSent = null) { // skip 0 bytes if (count == 0) { return; } if (onSent != null) { _callbackLock.Take(); // enqueue bytes in the buffer var length = _sendBuffer.TakeItem().EnqueueGetCount(buffer, index, count); _sendBuffer.Release(); if (_currentCallbackAction == null) { _currentCallbackAction = onSent; _currentCallbackIndex = _lastCallbackIndex = length; } else { _lastCallbackIndex = length - _lastCallbackIndex; _callbacks.Enqueue(Teple.New(_lastCallbackIndex, onSent)); } _callbackLock.Release(); } else { // enqueue bytes from the stream _sendBuffer.TakeItem().Enqueue(buffer, index, count); _sendBuffer.Release(); } // is the socket currently sending? if (_sendLock.TryTake) { // ensure not disposing if (_disposing) { _sendLock.Release(); return; } try { 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> /// On socket data being received. /// </summary> private unsafe void OnReceiveSocketData(IAsyncResult ar) { int count = 0; try { // end the receive count = Socket.EndReceiveFrom(ar, ref _receiveEndPoint); } catch (Exception ex) { // release the read lock _syncLock.ReleaseRead(); ProcessError(ex); return; } if (count > 0) { // is the received buffer chunked? byte flag = _receiveBuffer[0]; if (flag < 60 || flag >= 188) { count -= ChunkHeaderSize; int chunkId; int chunkIndex; int chunkCount; // sanity check for correct number of bytes received if (count < 0) { _syncLock.ReleaseRead(); ProcessError("Socket didn't receive enough data for chunked information."); return; } // yes, read the chunk details fixed(byte *intP = &_receiveBuffer[1]) { chunkId = *(int *)intP; } fixed(byte *intP = &_receiveBuffer[5]) { chunkIndex = *(int *)intP; } fixed(byte *intP = &_receiveBuffer[9]) { chunkCount = *(int *)intP; } // sanity check for the chunk data being valid if (chunkIndex >= chunkCount) { _syncLock.ReleaseRead(); ProcessError("Socket received invalid chunk index and count information."); return; } // write ChunkedGram chunkedGram; if (_receivedChunks.TryGetValue(chunkId, out chunkedGram)) { chunkedGram.Length += count; chunkedGram.Chunks.Insert(Teple.New(count, _receiveBuffer), chunkIndex); // have all chunks been added? if (chunkedGram.Chunks.Count == chunkCount) { // yes, remove from the collection _receivedChunks.Remove(chunkId); // create a byte buffer for the entire message byte[] result = new byte[chunkedGram.Length]; int index = 0; foreach (var chunk in chunkedGram.Chunks) { int length = chunk.ArgA; Micron.CopyMemory(chunk.ArgB, ChunkHeaderSize, result, index, length); index += length; } // reference the endpoint from which the data was received IPEndPoint endpoint = (IPEndPoint)_receiveEndPoint; // run the callback _onReceive.AddRun(ActionSet.New(OnReceive, endpoint, result, chunkedGram.Length)); } else { // no, create a new receive buffer _receiveBuffer = BufferCache.Get(); } } else { chunkedGram = new ChunkedGram { Chunks = new ArrayRig <Teple <int, byte[]> >(chunkCount), Timestamp = Time.Timestamp }; _receivedChunks.Add(chunkId, chunkedGram); chunkedGram.Chunks.Add(Teple.New(count, _receiveBuffer)); chunkedGram.Length += count; // create a new receive buffer _receiveBuffer = BufferCache.Get(); } } else { // no, copy the received buffer --count; byte[] buffer = BufferCache.Get(count); Micron.CopyMemory(_receiveBuffer, 1, buffer, 0, count); // reference the endpoint from which the data was received IPEndPoint endpoint = (IPEndPoint)_receiveEndPoint; // run the callback _onReceive.AddRun(ActionSet.New(OnReceive, endpoint, buffer, count)); } } if (_receivedChunks.Count > 0) { // check for any chunked data timeouts ArrayRig <int> toRemove = null; foreach (var chunkedGram in _receivedChunks) { if (Time.Timestamp - chunkedGram.Value.Timestamp > ChunkedGramTimeout) { if (toRemove == null) { toRemove = new ArrayRig <int>(); } toRemove.Add(chunkedGram.Key); } } if (toRemove != null) { foreach (var chunkId in toRemove) { ChunkedGram chunked; if (_receivedChunks.TryGetValue(chunkId, out chunked)) { _receivedChunks.Remove(chunkId); chunked.Chunks.Dispose(); } } } } // release the read lock _syncLock.ReleaseRead(); // create the endpoint for receiving data _receiveEndPoint = new IPEndPoint(RemoteEndPoint.Address, RemoteEndPoint.Port); try { // start receiving again Socket.BeginReceiveFrom(_receiveBuffer, 0, Global.BufferSizeLocal, SocketFlags.None, ref _receiveEndPoint, OnReceiveSocketData, null); } catch (Exception ex) { ProcessError(ex); return; } }