Beispiel #1
0
		/// <summary>
		/// Handle the bytes received from a socket.
		/// </summary>
		private static void HandlePayload(PhysicalSocket physical, byte[] payload, int length, IPEndPoint sender,
				out RUDPPacketChannel channel,
				out int packetId,
				out int advertisedWindowSize,
				out SACKSlot slot1,
				out SACKSlot slot2,
				out SACKSlot slot3,
				out SACKSlot slot4,
				out byte[] packetPayload)
		{
			int offset = 0;

			//-- Protocol version
			byte version = payload[offset];
			offset++;

			//-- Header information
			byte sacksSlotCount = payload[offset];
			offset++;

			//-- Channel
			byte channelByte = payload[offset];
			offset++;
			switch (channelByte)
			{
				case 0:
					channel = RUDPPacketChannel.Undefined;
					break;
				case 10:
					channel = RUDPPacketChannel.Ping;
					break;
				case 20:
					channel = RUDPPacketChannel.PingRendezVous;
					break;
				case 30:
					channel = RUDPPacketChannel.TearDown;
					break;
				case 40:
					channel = RUDPPacketChannel.UserPacket;
					break;
				case 50:
					channel = RUDPPacketChannel.ACK;
					break;
				case 70:
					channel = RUDPPacketChannel.OutOfOrder;
					break;
				case 100:
					channel = RUDPPacketChannel.KeepAlive;
					break;
				case 120:
					channel = RUDPPacketChannel.MTUTuning;
					break;
				case 121:
					channel = RUDPPacketChannel.MTUTuningACK;
					break;
				case 150:
					channel = RUDPPacketChannel.Bandwidth01;
					break;
				case 151:
					channel = RUDPPacketChannel.BandwidthResponse01;
					break;
				case 155:
					channel = RUDPPacketChannel.Bandwidth02;
					break;
				case 156:
					channel = RUDPPacketChannel.BandwidthResponse02;
					break;
				default:
					throw new Exception("Unsupported channel type");
			}

			//-- Packet Id
			packetId = BinaryHelper.ReadInt(payload, offset);
			offset += 4;

			//-- Control information : Advertised window
			advertisedWindowSize = BinaryHelper.ReadInt(payload, offset);
			offset += 4;

			//-- Payload length
			int payloadLength = BinaryHelper.ReadInt(payload, offset);
			offset += 4;

			//---- SACK Slots
			slot1 = null;
			slot2 = null;
			slot3 = null;
			slot4 = null;
			int startPacketId;
			int endPacketId;
			if (sacksSlotCount > 0)
			{
				startPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				endPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				slot1 = new SACKSlot(startPacketId, endPacketId);
			}
			else offset += 8;
			if (sacksSlotCount > 1)
			{
				startPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				endPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				slot2 = new SACKSlot(startPacketId, endPacketId);
			}
			else offset += 8;
			if (sacksSlotCount > 2)
			{
				startPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				endPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				slot3 = new SACKSlot(startPacketId, endPacketId);
			}
			else offset += 8;
			if (sacksSlotCount > 3)
			{
				startPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				endPacketId = BinaryHelper.ReadInt(payload, offset);
				offset += 4;
				slot4 = new SACKSlot(startPacketId, endPacketId);
			}
			else offset += 8;

			//-- Payload
			packetPayload = new byte[payloadLength];
			if (payloadLength > 0)
				Buffer.BlockCopy(payload, offset, packetPayload, 0, payloadLength);
		}
Beispiel #2
0
		static private void RegisterPhysicalSocket(PhysicalSocket physical)
		{
			EndPoint tempEndPoint = (EndPoint)physical._canReceiveFromEndPoint;
			physical._socket.BeginReceiveFrom(physical._receiveBuffer, 0, physical._receiveBuffer.Length, SocketFlags.None, ref tempEndPoint, new AsyncCallback(OnEndReceive), physical);
		}
Beispiel #3
0
		private static bool SocketSendACK(RUDPSocket rudp,
											PhysicalSocket physical,
											IPEndPoint remoteEndPoint,
											byte[] rudpPayload)
		{
			try
			{
				physical._socket.SendTo(rudpPayload, remoteEndPoint);
				//physical._socket.BeginSendTo(rudpPayload, 0, rudpPayload.Length, SocketFlags.None, rudp._remoteEndPoint, null, null);
			}
			catch (SocketException exception)
			{
				if (rudp != null)
					OnSocketUnhandledError(rudp, SocketErrorToRUDPSocketError(exception.SocketErrorCode), null);

				return false;
			}

			if (rudp != null)
				rudp._lastACKSendTS = HiResTimer.MicroSeconds;

			return true;
		}
Beispiel #4
0
		/// <summary>
		/// Static method to get/create a PhysicalSocket
		/// </summary>
		/// <param name="endPoint">The binded end point</param>
		internal static PhysicalSocket GetInstance(IPEndPoint endPoint)
		{
			lock (_physicals)
			{
				PhysicalSocket physical;

				bool isAnyEndPoint = endPoint.Equals(new IPEndPoint(IPAddress.Any, 0));

				// Check if there is already an existing instance
				if (!isAnyEndPoint)
				{
					if (_physicals.TryGetValue(endPoint, out physical))
						return physical;

					// In case no instance exists, create one
					physical = new PhysicalSocket();
					physical.Bind(endPoint);
					RegisterPhysicalSocket(physical);
					_physicals.Add(endPoint, physical);
				}
				else
				{
					physical = new PhysicalSocket();

					while (true)
					{
						int port = new Random().Next(Int16.MaxValue);
						IPAddress localAddress = LocalIPAddress(ProtocolType.IPv4);
						endPoint = new IPEndPoint(localAddress, port);

						// In case no instance exists, create one
						try
						{
							physical.Bind(endPoint);
							break;
						}
						catch { }
					}

					RegisterPhysicalSocket(physical);
					_physicals.Add(endPoint, physical);
				}

				return physical;
			}
		}
Beispiel #5
0
		/// <summary>
		/// Release a PhysicalSocket instance and all its resources.
		/// </summary>
		/// <param name="physical">The socket to release</param>
		internal static void ReleaseInstance(PhysicalSocket physical)
		{
			lock (_physicals)
			{
				physical.Dispose();
				_physicals.Remove((IPEndPoint)physical._socket.LocalEndPoint);
			}
		}
Beispiel #6
0
		/// <summary>
		/// Called when we have an error on a socket.
		/// </summary>
		static internal void OnSocketUnhandledError(PhysicalSocket physical, IPEndPoint remoteEndPoint, SocketError error)
		{
			//---- Get the socket
			RUDPSocket rudp;
			physical._connectedRDUPsLock.EnterReadLock();
			try
			{
				if (!physical._connectedRDUPs.TryGetValue(remoteEndPoint, out rudp))
					return; // Released socket
			}
			finally
			{
				physical._connectedRDUPsLock.ExitReadLock();
			}

			//---- Handle the error
			OnSocketUnhandledError(rudp, SocketErrorToRUDPSocketError(error), null);
		}
Beispiel #7
0
		private static RUDPSocket HandlePing(PhysicalSocket physical, IPEndPoint sender, int packetId, RUDPPacketChannel channel)
		{
			RUDPSocket rudp = null;

			physical._connectedRDUPsLock.EnterReadLock();
			physical._connectedRDUPs.TryGetValue(sender, out rudp);
			physical._connectedRDUPsLock.ExitReadLock();

			//---- Ping
			if (channel == RUDPPacketChannel.Ping)
			{
				//-- This connection already exist, duplicated Ping
				if (rudp != null)
				{
					// Resend the ACK
					rudp._sackWindow.OnReceivePacket(packetId);
					return null;
				}

				//-- No accepting socket
				if (physical._acceptingRDUP == null)
				{
					// Maybe the socket is not yet ready for accepting, do nothing
					return null;
				}

				//-- Accept
				rudp = physical.OnEndAccept(sender, packetId);

				//-- ACK connection
				rudp._sackWindow.OnReceivePacket(packetId);

				//return physical._acceptingRDUP;
				return rudp;
			}

			//---- Ping , with Rendez vous
			if (rudp != null && rudp._status == RUDPSocketStatus.Connecting && rudp._isRendezVousMode)
			{
				//---- End of connection
				rudp._status = RUDPSocketStatus.Connected;
				rudp.OnEndConnect(RUDPSocketError.Success);

				//---- Accept the rendez vous connection
				rudp._sackWindow.OnReceivePacket(packetId);

				return rudp;
			}

			return null;
		}
Beispiel #8
0
		private static void HandlePacket(PhysicalSocket physical,
										IPEndPoint sender,
										RUDPPacketChannel channel,
										int packetId,
										int advertisedWindowSize,
										SACKSlot slot1, SACKSlot slot2, SACKSlot slot3, SACKSlot slot4,
										byte[] payload)
		{
			RUDPSocket rudp = null;

			//---- PING
			if (channel == RUDPPacketChannel.Ping || channel == RUDPPacketChannel.PingRendezVous)
			{
				rudp = HandlePing(physical, sender, packetId, channel);

				// Do not handle this message
				if (rudp == null)
					return;
			}

			//---- Search the socket
			if (rudp == null)
			{
				physical._connectedRDUPsLock.EnterReadLock();
				physical._connectedRDUPs.TryGetValue(sender, out rudp);
				physical._connectedRDUPsLock.ExitReadLock();
			}

			//---- Direct send of ACK, because socket can be shutdowned and removed
			if (channel == RUDPPacketChannel.TearDown)
			{
				byte[] packetPayload = MakePacketPayload(rudp, -1, RUDPPacketChannel.ACK, new SACKSlot(packetId, packetId), null, null, null, null, 0, 0);
				SocketSendACK(rudp, physical, sender, packetPayload);
				PayloadManager.Deallocate(RUDPPacketChannel.ACK, packetPayload);
			}

			//---- Released socket
			if (rudp == null)
				return;

#if CONSOLE_TRACE
			if (packetId > -1)
				Trace("Handle packet (" + rudp.Handle + ")(" + channel + "):" + packetId);
#endif

			//---- Advertised window
			rudp._controlWindow.OnReceiveAdvertisedWindow(advertisedWindowSize);

			//---- Handle ACKs
			HandleACKs(rudp, slot1, slot2, slot3, slot4);

			if (channel == RUDPPacketChannel.ACK)
				return;

			//---- Non reliable messages
			if (packetId < 0)
			{
				//-- Bandwidth
				if (channel == RUDPPacketChannel.Bandwidth01)
				{
					PushPacketToSend(rudp, false, RUDPPacketChannel.BandwidthResponse01, null, 0, 0);
					return;
				}
				else if (channel == RUDPPacketChannel.Bandwidth02)
				{
					PushPacketToSend(rudp, false, RUDPPacketChannel.BandwidthResponse02, payload, 0, 8);
					return;
				}
				else if (channel == RUDPPacketChannel.BandwidthResponse01)
				{
					rudp._bandwidthResponse01TS = HiResTimer.MicroSeconds;
				}
				else if (channel == RUDPPacketChannel.BandwidthResponse02)
				{
					//---- Calculate bandwidth
					// Bdw (Bytes / milli-sec)
					long now = HiResTimer.MicroSeconds;
					double delay = (now - rudp._bandwidthResponse01TS) / 1000;
					if (delay < 0.001)
						delay = 0.001;

					// Arrival Speed
					double arrivalSpeed = (RUDPHeaderLength + UDPHeaderLength) / delay;

					// RTT
					double currentRtt = (now - BinaryHelper.ReadInt(payload, 0)) / 1000;
					if (currentRtt < 0.001)
						currentRtt = 0.001;

					// BDP = Bandwidth(Byte / Ms) * RTT;
					double bandwidth = (long)(arrivalSpeed * currentRtt);
					rudp._bandwidth = (long)(rudp._bandwidth * 0.875f + bandwidth * 0.125f);
				}

				//-- MTU Tuning
				else if (channel == RUDPPacketChannel.MTUTuning)
				{
					rudp._pmtuDiscovery.OnReceiveProbe(payload.Length);
					return;
				}
				else if (channel == RUDPPacketChannel.MTUTuningACK)
				{
					rudp._pmtuDiscovery.OnReceiveProbeACK(payload);
					return;
				}

				//if ((rudp._incomingNonReliablePackets.Count * rudp._mtu) >= rudp._receiveSize)
				//return;

				RUDPIngoingPacket nonReliablePacket = new RUDPIngoingPacket(rudp, packetId, payload, channel, HiResTimer.MicroSeconds);
				rudp._incomingPackets.AddPacket(nonReliablePacket);

				rudp.HandleNextUserPacket(false);
				return;
			}

			//---- Do not process a duplicated packets
			bool isDuplicatedPacket;
			isDuplicatedPacket = (packetId <= rudp._incomingPackets.CurrentPacketId);
			if (!isDuplicatedPacket)
				isDuplicatedPacket = rudp._incomingPackets.ContainsPacket(packetId);

			//---- Can I receive the packet now ?
			bool canReceive = rudp._controlWindow.CanReceive(packetId, payload.Length);

			//---- Send the ACK
			if (channel != RUDPPacketChannel.Ping && channel != RUDPPacketChannel.PingRendezVous)
				if (canReceive ||	// Can receive, then we send ACK
					(!canReceive && isDuplicatedPacket))	// Is duplicated, then already in the list -> send another ACK
					rudp._sackWindow.OnReceivePacket(packetId);

			//---- Check if we can handle this message
			if (!canReceive || isDuplicatedPacket)
				return;

			//---- If we are not connected, we cannot hanlde messages ! We need a connection before.
			if (rudp._status != RUDPSocketStatus.Connected && channel == RUDPPacketChannel.UserPacket)
				return;

			//---- Check for Out of order packets
			if (rudp._incomingPackets.Count > 0)
			{
				int greaterPacketId = rudp._incomingPackets.LastPacketId;
				if (packetId != greaterPacketId + 1)
				{
					byte[] oooPayload = new byte[8];
					// start packet
					BinaryHelper.WriteInt(greaterPacketId + 1, oooPayload, 0);
					// end packet
					BinaryHelper.WriteInt(packetId - 1, oooPayload, 4);
					PushPacketToSend(rudp, false, RUDPPacketChannel.OutOfOrder, oooPayload, 0, 8);
				}
			}

			//---- Receive Out of order notification
			if (channel == RUDPPacketChannel.OutOfOrder)
			{
				rudp._controlWindow.OnOutOfOrder(BinaryHelper.ReadInt(payload, 0), BinaryHelper.ReadInt(payload, 4));
			}

			//---- TEAR DOWN
			if (channel == RUDPPacketChannel.TearDown)
			{
				// Initiate the close process
				if (rudp._status == RUDPSocketStatus.Connected)
				{
					// Notify control window
					rudp._controlWindow.OnReceive(null);

					// Start shutdown
					AsyncShutdown(rudp);
				}

				return;
			}

			//---- Add the packet to incoming list
			RUDPIngoingPacket packet = new RUDPIngoingPacket(rudp, packetId, payload, channel, HiResTimer.MicroSeconds);

			// Notify control window
			rudp._controlWindow.OnReceive(packet);
			rudp._incomingPackets.AddPacket(packet);

			//------ Handle the ordered ingoing packets
			rudp.HandleNextUserPacket(false);
		}
Beispiel #9
0
 public void Bind(IPEndPoint endPoint)
 {
     // Get the associated physical socket
     _physical = RUDPStack.GetInstance(endPoint);
 }
Beispiel #10
0
		public void Bind(IPEndPoint endPoint)
		{
			// Get the associated physical socket
			_physical = RUDPStack.GetInstance(endPoint);
		}