/// <summary> /// Callback for asynchronous send, if data is left to send, then send the remaining data, otherwise go into wait for acknowledgement state. /// </summary> /// <param name="ar">An IAsyncResult that references the asynchronous send.</param> private void SendCallback(IAsyncResult ar) { ClientState state = (ClientState)ar.AsyncState; int sentData = state.Socket.EndSend(ar, out SocketError socketError); Diagnostics.LogInfo(Helper.ContextLogger(string.Format("BytesSent : {0} TotalBytes: {1}", sentData, state.DataToSend().Length), 2)); if (socketError != SocketError.Success) { Diagnostics.LogError(Helper.ContextLogger("SocketError EndPoint :: " + Helper.GetEndPoint(state.Socket), 0)); // Socket Failed this.ResetClient(Helper.GetEndPointAddress(state.Socket), state.IPPort); // Remove any lock-objects,sockets,status bit associated to failed socket. lock (this.ackLock) { this.acknowledgeStatus.Remove(Helper.MD5Hash(state.Message) + state.IPPort); } this.DataStatusNotify(state.Message, StatusCode.Failure); if (this.isStudent) { this.isRunning = false; } return; } state.DataSent += sentData; if (state.DataSent != state.DataToSend().Length) { // not all data was sent so send remaining bytes state.Socket.BeginSend( state.DataToSend(), state.DataSent, state.DataToSend().Length - state.DataSent, SocketFlags.None, new AsyncCallback(this.SendCallback), state); } else { if (state.DataToSend()[4] == 0) { Diagnostics.LogSuccess(Helper.ContextLogger(string.Format("EndPoint {0} Message Sent :: {1}", Helper.GetEndPoint(state.Socket), Helper.ShortLog(state.Message)), 1)); } else { Diagnostics.LogSuccess(Helper.ContextLogger(string.Format("Acknowledgement EndPoint {0} Message Sent :: {1}", Helper.GetEndPoint(state.Socket), Helper.MD5Hash(state.Message)), 2)); } // Now that all data has been sent we release the lock. this.ReleaseLock(state.IPPort); if (state.DataToSend()[4] == 0) { new Thread(() => this.WaitForAcknowledgement(Helper.GetEndPointAddress(state.Socket), state.IPPort, state.Message, state.Retries + 1)) { Name = Thread.CurrentThread.Name + " # Target IP : " + Helper.GetEndPoint(state.Socket) }.Start(); } } }
/// <summary> /// Sends a given message by formatting it. /// </summary> /// <param name="message">The message to be sent</param> /// <param name="retries">Tells which retry is this to send the message</param> /// <param name="socket">The socket to which send the data on</param> /// <param name="isAck">Tells if the message being sent is acknowledgment.</param> private void SendHelper(string message, int retries, Socket socket, int isAck) { // Create a new state object. ClientState currentState = new ClientState { Socket = socket }; if (currentState.IPPort == "0.0.0.0") { Diagnostics.LogError(Helper.ContextLogger("Called with null socket, not supposed to happen", 1)); throw new InvalidOperationException(); } byte[] byteData = Encoding.ASCII.GetBytes(message); byte[] lengthBytes = Helper.GetBytes(byteData.Length); byte[] ackBits = new byte[] { (byte)isAck }; // Every message sent over network has format (prefix = length + ackBit) + message bytes // length in prefix excludes the bytes used for length and reservedBits(ackBit) byte[] prefix = Helper.Combine(lengthBytes, ackBits); currentState.Retries = retries; currentState.DataSent = 0; currentState.Message = message; currentState.SetDataToSend(Helper.Combine(prefix, byteData)); Diagnostics.LogInfo(Helper.ContextLogger("Waiting To Acquire Lock IP EndPoint" + Helper.GetEndPoint(socket), 7)); // We maintain locks per socket so that more than one thread don't send at the same time on same socket, otherwise data would overlap. while (!this.GetLock(currentState.IPPort)) { } Diagnostics.LogInfo(Helper.ContextLogger("Lock Acquired. Calling BeginSend EndPoint" + Helper.GetEndPoint(socket), 5)); lock (this.ackLock) { this.acknowledgeStatus[Helper.MD5Hash(message) + currentState.IPPort] = new AutoResetEvent(false); } try { socket.BeginSend(currentState.DataToSend(), 0, currentState.DataToSend().Length, 0, new AsyncCallback(this.SendCallback), currentState); } catch { Diagnostics.LogError(Helper.ContextLogger("SocketError EndPoint :: " + Helper.GetEndPoint(currentState.Socket), 0)); // Socket Failed this.ResetClient(Helper.GetEndPointAddress(currentState.Socket), currentState.IPPort); // Remove any lock-objects,sockets,status bit associated to failed socket. lock (this.ackLock) { this.acknowledgeStatus.Remove(Helper.MD5Hash(currentState.Message) + currentState.IPPort); } this.DataStatusNotify(currentState.Message, StatusCode.Failure); if (this.isStudent) { this.isRunning = false; } } Diagnostics.LogInfo(Helper.ContextLogger("Returned EndPoint" + Helper.GetEndPoint(socket), 5)); }