/// <summary> /// Disconnect from this simulator /// </summary> public void Disconnect(bool sendCloseCircuit) { if (connected) { connected = false; // Destroy the timers if (AckTimer != null) { AckTimer.Dispose(); } if (StatsTimer != null) { StatsTimer.Dispose(); } if (PingTimer != null) { PingTimer.Dispose(); } // Kill the current CAPS system if (Caps != null) { Caps.Disconnect(true); Caps = null; } if (sendCloseCircuit) { // Try to send the CloseCircuit notice CloseCircuitPacket close = new CloseCircuitPacket(); UDPPacketBuffer buf = new UDPPacketBuffer(ipEndPoint, false); buf.Data = close.ToBytes(); buf.DataLength = buf.Data.Length; AsyncBeginSend(buf); } // Shut the socket communication down Stop(); } }
/// <summary> /// Send a raw byte array payload as a packet /// </summary> /// <param name="payload">The packet payload</param> /// <param name="setSequence">Whether the second, third, and fourth bytes /// should be modified to the current stream sequence number</param> public void SendPacket(byte[] payload, bool setSequence) { try { if (setSequence && payload.Length > 3) { lock (SequenceLock) { payload[1] = (byte)(Sequence >> 16); payload[2] = (byte)(Sequence >> 8); payload[3] = (byte)(Sequence % 256); Sequence++; } } Stats.SentBytes += (ulong)payload.Length; Stats.SentPackets++; UDPPacketBuffer buf = new UDPPacketBuffer(ipEndPoint, false); buf.Data = payload; buf.DataLength = payload.Length; AsyncBeginSend(buf); } catch (SocketException) { Logger.Log("Tried to send a " + payload.Length + " byte payload on a closed socket, shutting down " + this.ToString(), Helpers.LogLevel.Info, Client); Network.DisconnectSim(this, false); return; } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } }
protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
// these abstract methods must be implemented in a derived class to actually do // something with the packets that are sent and received. protected abstract void PacketReceived(UDPPacketBuffer buffer);
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) { }
protected override void PacketReceived(UDPPacketBuffer buffer) { Packet packet = null; // Check if this packet came from the server we expected it to come from if (!ipEndPoint.Address.Equals(((IPEndPoint)buffer.RemoteEndPoint).Address)) { Logger.Log("Received " + buffer.DataLength + " bytes of data from unrecognized source " + ((IPEndPoint)buffer.RemoteEndPoint).ToString(), Helpers.LogLevel.Warning, Client); return; } // Update the disconnect flag so this sim doesn't time out DisconnectCandidate = false; #region Packet Decoding int packetEnd = buffer.DataLength - 1; packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData); // Fail-safe check if (packet == null) { Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning, Client); return; } Stats.RecvBytes += (ulong)buffer.DataLength; Stats.RecvPackets++; #endregion Packet Decoding #region Reliable Handling if (packet.Header.Reliable) { // Add this packet to the list of ACKs that need to be sent out lock (PendingAcks) { uint sequence = (uint)packet.Header.Sequence; if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; } } // Send out ACKs if we have a lot of them if (PendingAcks.Count >= Client.Settings.MAX_PENDING_ACKS) { SendAcks(); } if (packet.Header.Resent) { ++Stats.ReceivedResends; } } #endregion Reliable Handling #region Inbox Insertion NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = this; incomingPacket.Packet = packet; // TODO: Prioritize the queue Network.PacketInbox.Enqueue(incomingPacket); #endregion Inbox Insertion }
/// <summary> /// Send a prepared <code>UDPPacketBuffer</code> object as a packet /// </summary> /// <param name="buffer">The prepared packet structure to be sent out</param> public void SendPacket(UDPPacketBuffer buffer) { try { AsyncBeginSend(buffer); } catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } }
/// <summary> /// Sends a packet /// </summary> /// <param name="packet">Packet to be sent</param> /// <param name="incrementSequence">Increment sequence number?</param> /// public void SendPacket(Packet packet, bool incrementSequence) { byte[] buffer; int bytes; // Keep track of when this packet was sent out packet.TickCount = Environment.TickCount; if (incrementSequence) { // Set the sequence number lock (SequenceLock) { if (Sequence > Settings.MAX_SEQUENCE) { Sequence = 1; } else { Sequence++; } packet.Header.Sequence = Sequence; } if (packet.Header.Reliable) { // Add this packet to the list of ACK responses we are waiting on from the server lock (NeedAck) { if (!NeedAck.ContainsKey(packet.Header.Sequence)) { NeedAck.Add(packet.Header.Sequence, packet); } else { Logger.Log("Attempted to add a duplicate sequence number (" + packet.Header.Sequence + ") to the NeedAck dictionary for packet type " + packet.Type.ToString(), Helpers.LogLevel.Warning, Client); } } if (packet.Header.Resent) { // This packet has already been sent out once, strip any appended ACKs // off it and reinsert them into the outgoing ACK queue under the // assumption that this packet will continually be rejected from the // server or that the appended ACKs are possibly making the delivery fail if (packet.Header.AckList.Length > 0) { Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.", packet.Header.Sequence, packet.GetType())); lock (PendingAcks) { foreach (uint sequence in packet.Header.AckList) { if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; } } } packet.Header.AppendedAcks = false; packet.Header.AckList = new uint[0]; } } else { // This packet is not a resend, check if the conditions are favorable // to ACK appending if (packet.Type != PacketType.PacketAck && packet.Type != PacketType.LogoutRequest) { lock (PendingAcks) { if (PendingAcks.Count > 0 && PendingAcks.Count < Client.Settings.MAX_APPENDED_ACKS) { // Append all of the queued up outgoing ACKs to this packet packet.Header.AckList = new uint[PendingAcks.Count]; for (int i = 0; i < PendingAcks.Count; i++) { packet.Header.AckList[i] = PendingAcks.Values[i]; } PendingAcks.Clear(); packet.Header.AppendedAcks = true; } } } } } else if (packet.Header.AckList.Length > 0) { // Sanity check for ACKS appended on an unreliable packet, this is bad form Logger.Log("Sending appended ACKs on an unreliable packet", Helpers.LogLevel.Warning); } } // Serialize the packet buffer = packet.ToBytes(); bytes = buffer.Length; Stats.SentBytes += (ulong)bytes; Stats.SentPackets++; UDPPacketBuffer buf; // Zerocode if needed if (packet.Header.Zerocoded) { buf = new UDPPacketBuffer(ipEndPoint, true, false); bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data); buf.DataLength = bytes; } else { buf = new UDPPacketBuffer(ipEndPoint, false, false); buf.Data = buffer; buf.DataLength = bytes; } AsyncBeginSend(buf); }
/// <summary> /// Send a prepared <code>UDPPacketBuffer</code> object as a packet /// </summary> /// <param name="buffer">The prepared packet structure to be sent out</param> public void SendPacket(UDPPacketBuffer buffer) { try { AsyncBeginSend(buffer); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } }
/// <summary> /// Sends a packet /// </summary> /// <param name="packet">Packet to be sent</param> /// <param name="incrementSequence">Increment sequence number?</param> public void SendPacket(Packet packet, bool incrementSequence) { byte[] buffer; int bytes; // Keep track of when this packet was sent out packet.TickCount = Environment.TickCount; if (incrementSequence) { // Set the sequence number lock (SequenceLock) { if (Sequence > Settings.MAX_SEQUENCE) { Sequence = 1; } else { Sequence++; } packet.Header.Sequence = Sequence; } // Scrub any appended ACKs since all of the ACK handling is done here if (packet.Header.AckList.Length > 0) { packet.Header.AckList = new uint[0]; } packet.Header.AppendedAcks = false; if (packet.Header.Reliable) { lock (NeedAck) { if (!NeedAck.ContainsKey(packet.Header.Sequence)) { NeedAck.Add(packet.Header.Sequence, packet); } else { Client.Log("Attempted to add a duplicate sequence number (" + packet.Header.Sequence + ") to the NeedAck dictionary for packet type " + packet.Type.ToString(), Helpers.LogLevel.Warning); } } // Don't append ACKs to resent packets, in case that's what was causing the // delivery to fail if (!packet.Header.Resent) { // Append any ACKs that need to be sent out to this packet lock (PendingAcks) { if (PendingAcks.Count > 0 && PendingAcks.Count < Client.Settings.MAX_APPENDED_ACKS && packet.Type != PacketType.PacketAck && packet.Type != PacketType.LogoutRequest) { packet.Header.AckList = new uint[PendingAcks.Count]; for (int i = 0; i < PendingAcks.Count; i++) { packet.Header.AckList[i] = PendingAcks.Values[i]; } PendingAcks.Clear(); packet.Header.AppendedAcks = true; } } } } } // Serialize the packet buffer = packet.ToBytes(); bytes = buffer.Length; Stats.SentBytes += (ulong)bytes; Stats.SentPackets++; UDPPacketBuffer buf; // Zerocode if needed if (packet.Header.Zerocoded) { buf = new UDPPacketBuffer(ipEndPoint, true, false); bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data); buf.DataLength = bytes; } else { buf = new UDPPacketBuffer(ipEndPoint, false, false); buf.Data = buffer; buf.DataLength = bytes; } AsyncBeginSend(buf); }