public static void Read(Stream stream, Bcp.ReadState readState, BcpDelegate.ProcessRead processRead, BcpDelegate.ExceptionHandler exceptionHandler) { var headBuffer = new byte[1]; AsyncCallback asyncCallback = null; asyncCallback = asyncResult => { try { int numBytesRead = stream.EndRead(asyncResult); if (numBytesRead != 1) { throw new EndOfStreamException(); } switch (headBuffer[0]) { case Bcp.Data.HeadByte: { ProcessReadVarint processReadLength = delegate(uint length) { if (length > Bcp.MaxDataSize) { throw new BcpException.DataTooBig(); } var buffer = new byte[length]; ProcessReadAll processReadAll = delegate() { processRead(new Bcp.Data(new[] { (new ArraySegment <byte>(buffer)) })); }; ReadAll(stream, readState, buffer, 0, (int)length, processReadAll, exceptionHandler); }; ReadUnsignedVarint(stream, readState, processReadLength, exceptionHandler); break; } case Bcp.RetransmissionData.HeadByte: { ProcessReadVarint processReadConnectionId = delegate(uint connectionId) { ProcessReadVarint processReadPackId = delegate(uint packId) { ProcessReadVarint processReadLength = delegate(uint length) { if (length > Bcp.MaxDataSize) { throw new BcpException.DataTooBig(); } var buffer = new byte[length]; ProcessReadAll processReadAll = delegate() { processRead(new Bcp.RetransmissionData(connectionId, packId, new[] { (new ArraySegment <byte>(buffer)) })); }; ReadAll(stream, readState, buffer, 0, (int)length, processReadAll, exceptionHandler); }; ReadUnsignedVarint(stream, readState, processReadLength, exceptionHandler); }; ReadUnsignedVarint(stream, readState, processReadPackId, exceptionHandler); }; ReadUnsignedVarint(stream, readState, processReadConnectionId, exceptionHandler); break; } case Bcp.RetransmissionFinish.HeadByte: { ProcessReadVarint processReadConnectionId = delegate(uint connectionId) { ProcessReadVarint processReadPackId = delegate(uint packId) { processRead(new Bcp.RetransmissionFinish(connectionId, packId)); }; ReadUnsignedVarint(stream, readState, processReadPackId, exceptionHandler); }; ReadUnsignedVarint(stream, readState, processReadConnectionId, exceptionHandler); break; } case Bcp.Acknowledge.HeadByte: processRead(new Bcp.Acknowledge()); break; case Bcp.Finish.HeadByte: processRead(new Bcp.Finish()); break; case Bcp.ShutDown.HeadByte: processRead(new Bcp.ShutDown()); break; case Bcp.HeartBeat.HeadByte: processRead(new Bcp.HeartBeat()); break; default: throw new BcpException.UnknownHeadByte(); } } catch (Exception e) { exceptionHandler(e); } }; try { stream.BeginRead(headBuffer, 0, 1, asyncCallback, readState); } catch (Exception e) { exceptionHandler(e); } }
private void StartReceive(uint connectionId, Connection connection) { BcpDelegate.ProcessRead processRead = delegate(Bcp.IPacket packet) { var readTimer = connection.readState.readTimeoutTimer; readTimer.Change(Timeout.Infinite, Timeout.Infinite); readTimer.Dispose(); readTimer = null; if (packet is Bcp.HeartBeat) { Debug.WriteLine("Receive heart beat!"); StartReceive(connectionId, connection); } else if (packet is Bcp.Data) { Debug.WriteLine("Receive data: " + packet); lock (sessionLock) { BcpIO.Write(connection.stream, new Bcp.Acknowledge()); var data = (Bcp.Data)packet; var buffer = data.Buffers; ResetHeartBeatTimer(connection); var packId = connection.numDataReceived; connection.numDataReceived = packId + 1; DataReceived(connectionId, connection, packId, buffer); connection.stream.Flush(); } StartReceive(connectionId, connection); } else if (packet is Bcp.RetransmissionData) { Debug.WriteLine("Receive retransmission data: " + packet); lock (sessionLock) { BcpIO.Write(connection.stream, new Bcp.Acknowledge()); var retransmissionData = (Bcp.RetransmissionData)packet; var dataConnectionId = retransmissionData.ConnectionId; var packId = retransmissionData.PackId; var data = retransmissionData.Buffers; ResetHeartBeatTimer(connection); Connection dataConnection; if (connections.TryGetValue(dataConnectionId, out dataConnection)) { DataReceived(dataConnectionId, dataConnection, packId, data); } else { var oldLastConnectionId = lastConnectionId; if (dataConnectionId - oldLastConnectionId + connections.Count >= Bcp.MaxConnectionsPerSession) { InternalInterrupt(); } else { if (oldLastConnectionId <= dataConnectionId) { lastConnectionId = dataConnectionId; for (var id = oldLastConnectionId + 1; id < dataConnectionId; ++id) { Connection c = NewConnection(); connections.Add(id, c); } DataReceived(dataConnectionId, GetLastConnection(), packId, data); } else { } } } connection.stream.Flush(); } StartReceive(connectionId, connection); } else if (packet is Bcp.Acknowledge) { lock (sessionLock) { Debug.WriteLine("Before receive acknowledge, sendingConnectionQueue: " + sendingConnectionQueue.Count); var originalPack = connection.unconfirmedPackets.Dequeue(); if (connection.unconfirmedPackets.Count == 0) { switch (connectionState) { case SessionState.Available: { foreach (var openConnections in sendingConnectionQueue) { if (openConnections.Value.Contains(connection)) { openConnections.Value.Remove(connection); if (openConnections.Value.Count == 0) { sendingConnectionQueue.Remove(openConnections.Key); } HashSet <Connection> allConfirmedConnections; if (sendingConnectionQueue.TryGetValue(allConfirmed, out allConfirmedConnections)) { allConfirmedConnections.Add(connection); } else { allConfirmedConnections = new HashSet <Connection>(); allConfirmedConnections.Add(connection); sendingConnectionQueue.Add(allConfirmed, allConfirmedConnections); } break; } } break; } case SessionState.Unavailable: break; } Idle(connection); } if (originalPack is Bcp.Data) { connection.numAcknowledgeReceivedForData += 1; } else if (originalPack is Bcp.RetransmissionData || originalPack is Bcp.Finish || originalPack is Bcp.RetransmissionFinish) { } CheckConnectionFinish(connectionId, connection); Debug.WriteLine("After receive acknowledge, sendingConnectionQueue: " + sendingConnectionQueue.Count); } StartReceive(connectionId, connection); } else if (packet is Bcp.Finish) { Debug.WriteLine("receive finish, connectionId: " + connectionId.ToString()); lock (sessionLock) { BcpIO.Write(connection.stream, new Bcp.Acknowledge()); if (!connection.isFinishSent) { EnqueueFinish(connection); } var packId = connection.numDataReceived; connection.finishID = packId; CleanUp(connectionId, connection); connection.stream.Dispose(); connection.stream = null; } } else if (packet is Bcp.RetransmissionFinish) { lock (sessionLock) { Debug.WriteLine("Before receive retransmission finish, sendingConnectionQueue: " + sendingConnectionQueue.Count); BcpIO.Write(connection.stream, new Bcp.Acknowledge()); var retransmissionFinishPack = (Bcp.RetransmissionFinish)packet; var finishConnectionId = retransmissionFinishPack.ConnectionId; var packId = retransmissionFinishPack.PackId; ResetHeartBeatTimer(connection); Connection finishConnection; if (connections.TryGetValue(finishConnectionId, out finishConnection)) { RetransmissionFinishReceived(finishConnectionId, finishConnection, packId); CleanUp(finishConnectionId, finishConnection); } else { var oldLastConnectionId = lastConnectionId; if (finishConnectionId - oldLastConnectionId + connections.Count >= Bcp.MaxConnectionsPerSession) { InternalInterrupt(); } else { if (oldLastConnectionId < finishConnectionId) { lastConnectionId = finishConnectionId; for (var id = oldLastConnectionId + 1; id <= finishConnectionId; ++id) { Connection c = NewConnection(); connections.Add(id, c); } RetransmissionFinishReceived(finishConnectionId, GetLastConnection(), packId); CleanUp(finishConnectionId, GetLastConnection()); } else { } } } connection.stream.Flush(); Debug.WriteLine("After receive retransmission finish, sendingConnectionQueue: " + sendingConnectionQueue.Count); } StartReceive(connectionId, connection); } else if (packet is Bcp.ShutDown) { Debug.WriteLine("Receive shut down!"); lock (sessionLock) { CheckShutDown(); } } }; BcpDelegate.ExceptionHandler exceptionHandler = delegate(Exception e) { lock (sessionLock) { if (!connection.readState.isCancel) { connection.readState.Cancel(); Console.WriteLine("Received exception: " + e.Message); if (connection.stream != null) { connection.stream.Dispose(); } CleanUp(connectionId, connection); } } }; connection.readState.readTimeoutTimer = StartReadTimer(connection.stream, exceptionHandler); BcpIO.Read(connection.stream, connection.readState, processRead, exceptionHandler); }