/// <summary> /// Send a Packet through a reliable but latency prone way with sequence guarantee. /// <para>This call might have more latency then <see cref="SendReliable(Packet, Action)"/>.</para> /// </summary> /// <param name="packet"></param> public static void SendReliableSequenced(Packet packet, Action callback = null) { if (packet.id == -1) { packet.OverrideID(NUUtilities.GeneratePacketId()); } SendReliable(packet, callback); }
public static void SendReliable(Packet packet, Action callback = null) { if (!connected || !client.tcpClient.Connected) { Debug.LogError("Can't send Packet while not connected!"); return; } if (packet.data == null) { Debug.LogError("Data to be sent is null!"); return; } if (packet.data.Length == 0) { Debug.LogError("Data to be sent is empty!"); return; } //Must split packets if (packet.data.Length > NUUtilities.MTU) { Packet[] partPackets = NUUtilities.SplitPacket(packet); for (int i = 0; i < partPackets.Length - 1; i++) { SendReliable(partPackets[i]); } //Set callback on last packet send SendReliable(partPackets[partPackets.Length - 1], callback); return; } try { TcpTransmissionState transmissionState = new TcpTransmissionState(packet.data.Length, ref client.tcpClient, client, callback); client.tcpClient.GetStream().BeginWrite(packet.data, 0, packet.data.Length, new AsyncCallback(EndReliableSend), transmissionState); } catch (Exception ex) { if (!client.connected || !client.tcpClient.Connected) { return; } Debug.LogError("Error sending reliable Packet to Server: " + ex.ToString()); //Do proper disconnection handling hasDisconnected = true; } }
/// <summary> /// Setup the broadcast channel for the given adapter address and port. /// </summary> /// <param name="adapterAddress">The Network Adapter address to be used for broadcasting. /// See <see cref="NUUtilities.ListIPv4Addresses()"/> for valid sources.</param> /// <param name="broadcastPort">The port in which the broadcast will happen.</param> public static void SetupBroadcast(IPAddress adapterAddress = null, ushort broadcastServerPort = 56552, Action updateHook = null, int reservedBufferedPackets = NUUtilities.MaxBufferedPackets) { //Loopback address if none are given if (adapterAddress == null) { adapterAddress = IPAddress.Loopback; } NUClient.broadcastServerPort = broadcastServerPort; // Setup broadcast ranges and receiving queue IPAddress subnet = NUUtilities.GetSubnetMaskFromIPv4(adapterAddress); UInt32 subnetInt = NUUtilities.GetUIntFromIpAddress(subnet); UInt32 addressInt = NUUtilities.GetUIntFromIpAddress(adapterAddress); broadcastStartRange = (addressInt & subnetInt) + 1; broadcastFinalRange = (addressInt | (~subnetInt)) - 1; lock (broadcastDataQueueLock) { broadcastDataQueue = new Queue <BroadcastPacket>(reservedBufferedPackets); } IPEndPoint broadcastEp = new IPEndPoint(adapterAddress, 0); broadcaster = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) { ExclusiveAddressUse = true }; broadcaster.Bind(broadcastEp); BroadcastTransmissionState broadcastState = new BroadcastTransmissionState( NUUtilities.MTU, ref broadcaster, null); EndPoint broadcastSenderEp = broadcastState.senderEp; broadcaster.BeginReceiveFrom(broadcastState.data, 0, NUUtilities.MTU, SocketFlags.None, ref broadcastSenderEp, new AsyncCallback(EndBroadcastReceive), broadcastState); //Hook on updateHook otherwise instantiate NUClientComponent if (updateHook != null) { updateHook += ProcessQueues; } else { //Create MonoBehaviour instance if it doesn't exists if (clientComponent == null) { GameObject clientObject = new GameObject("NUClientObject"); //clientObject.hideFlags = HideFlags.HideAndDontSave; GameObject.DontDestroyOnLoad(clientObject); clientComponent = clientObject.AddComponent <NUClientComponent>(); //clientObject.hideFlags = HideFlags.HideInInspector; } } }
/// <summary> /// Broadcast a Packet (whose destination doesn't matter) /// </summary> /// <param name="emptyDestinationPacket">An packet whose destination doesn't matter</param> public static void Broadcast(Packet emptyDestinationPacket, Action callback = null) { if (broadcaster == null) { Debug.LogWarning("Broadcast not configured! Using default settings...\n" + "Call NUClient.SetupBroadcast() with corrent parameters."); SetupBroadcast(); } if (emptyDestinationPacket.data == null) { Debug.LogError("Data to be sent is null!"); return; } if (emptyDestinationPacket.data.Length == 0) { Debug.LogError("Data to be sent is empty!"); return; } // Set Broadcast Flag if not already if (emptyDestinationPacket.flag != Packet.TypeFlag.BROADCAST) { emptyDestinationPacket.OverridePacketFlag(Packet.TypeFlag.BROADCAST); } BroadcastTransmissionState transmissionState = new BroadcastTransmissionState( emptyDestinationPacket.data.Length, ref broadcaster, callback); // Send a broadcast transmission to the full range of IPs range IPEndPoint receivingEp = (IPEndPoint)broadcaster.LocalEndPoint; for (uint ip = broadcastStartRange + 1; ip < broadcastFinalRange; ip++) { IPAddress broadcastIp = NUUtilities.GetIpAddressFromUInt32(ip); IPEndPoint broadcastEp = new IPEndPoint(broadcastIp, broadcastServerPort); broadcaster.BeginSendTo(emptyDestinationPacket.data, 0, emptyDestinationPacket.data.Length, SocketFlags.None, broadcastEp, new AsyncCallback(EndBroadcastSend), transmissionState); } }
private static void TreatPacket(ref Packet packet) { if ((packet.flag ^ Packet.TypeFlag.DATA) == 0) { //Check if this is a sequential Packet if (packet.id >= 0) { lock (seqDataQueueLock) { //Treat out-of-order server packets that arrive //before GUID transaction is done if (lastPacketId == -1) { seqDataList.Add(packet); return; } //Check if it's dependency packet has been received if (packet.id == 0 && lastPacketId != int.MaxValue) { seqDataList.Add(packet); } else if (packet.id != (lastPacketId + 1) && packet.id != 0) { seqDataList.Add(packet); } else //If it does, enqueue it and all packets on hold { lock (dataQueueLock) { dataQueue.Enqueue(packet); } if (lastPacketId == int.MaxValue) { lastPacketId = -1; } lastPacketId++; //Add all packets in proper order List <Packet> packetsToAdd = ProcessSequentialPackets(); lock (dataQueueLock) { foreach (Packet p in packetsToAdd) { dataQueue.Enqueue(p); } } } } return; } //Enqueue proper callbacks lock (dataQueueLock) { dataQueue.Enqueue(packet); } } else if ((packet.flag ^ Packet.TypeFlag.GUID) == 0) { //Setup Last Packet ID lock (seqDataQueueLock) { lastPacketId = packet.id; //Process out-of-order messages List <Packet> packetsToAdd = ProcessSequentialPackets(); lock (dataQueueLock) { foreach (Packet p in packetsToAdd) { dataQueue.Enqueue(p); } } } //Set GUID given from the server int udpPortOffset = NUUtilities.GetGuidAndPort(packet.GetCleanData(), out client.guid); //Debug.LogFormat("Received GUID ({0}) and Port ({1}) from server!", client.guid, udpPortOffset); //Connect UDP client to correct port IPEndPoint remoteEndPoint = (IPEndPoint)client.tcpClient.Client.RemoteEndPoint; IPEndPoint endPoint = new IPEndPoint(remoteEndPoint.Address, remoteEndPoint.Port + udpPortOffset); client.udpClient.Connect(endPoint); client.connected = true; //Set hasConnected flag hasConnected = true; } else if ((packet.flag ^ Packet.TypeFlag.DCONNECT) == 0) { serverDisconnectMsg = packet.GetMessageData(); //Set hasDisconnected flag hasDisconnected = true; serverDisconnected = true; } else if ((packet.flag ^ Packet.TypeFlag.PING) == 0) { //Update ping value client.OverridePing(NUUtilities.GetInt32(packet.GetCleanData())); Packet pingPacket = new Packet("", null, Packet.TypeFlag.PING); if (connected) { SendUnreliable(pingPacket); } } else if ((packet.flag ^ Packet.TypeFlag.MPARTDATA) == 0) { //Get MultiPart packet Hash and ID Hash hash; byte[] packetData = packet.GetCleanData(); int partId = NUUtilities.GetHashAndPartId(packetData, out hash); //Check if there is a multipart packet registration MultiPartBuffer multiPartBuffer; lock (multiPartLock) { //Create a new multipart packet registration if not already if (!multiPartBuffers.TryGetValue(hash, out multiPartBuffer)) { byte[] bufferSizeData = new byte[4]; Array.Copy(packetData, 20, bufferSizeData, 0, 4); int bufferSize = NUUtilities.GetInt32(bufferSizeData); byte[] packetCountData = new byte[4]; Array.Copy(packetData, 24, packetCountData, 0, 4); int packetCount = NUUtilities.GetInt32(packetCountData); multiPartBuffer = new MultiPartBuffer(packetCount, bufferSize, hash); multiPartBuffers.Add(hash, multiPartBuffer); } } //Merge received data multiPartBuffer.MergePacketData(partId, packetData); //Assemble Original Packet and Enqueu it! if (multiPartBuffer.remainingSize == 0) { Packet originalPacket; if (multiPartBuffer.GetOriginalPacket(out originalPacket)) { //Debug.LogFormat("Multipart Packet Received from Server!"); lock (dataQueueLock) { dataQueue.Enqueue(originalPacket); } lock (multiPartLock) { multiPartBuffers.Remove(hash); } } } } }
private static void EndConnect(IAsyncResult asyncResult) { //Extract TcpClient from AsyncState TcpClient tcpClient = (TcpClient)asyncResult.AsyncState; //Try to connect to the given EndPoint try { //Finish connecting tcpClient.EndConnect(asyncResult); //Get Client EndPoint IPEndPoint localEndPoint = (IPEndPoint)tcpClient.Client.LocalEndPoint; IPEndPoint remoteEndPoint = (IPEndPoint)tcpClient.Client.RemoteEndPoint; //Create UdpClient Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp) { ExclusiveAddressUse = true }; udpClient.Bind(localEndPoint); //Bind it locally to the TCP Address //Create NUClientInfo if it doesn't exists already if (client == null) { client = new NUClientInfo(Guid.Empty, localEndPoint.Address, ref tcpClient, ref udpClient); } else { //Override information with last client id but current addresses and sockets client = new NUClientInfo(client.guid, localEndPoint.Address, ref tcpClient, ref udpClient); } //Debug.Log("Connected to server (" + remoteEndPoint.Address + ":" + remoteEndPoint.Port + ")!" + // "\nSending GUID " + client.id + " and Waiting for response..."); //Send Current GUID - Null if this is not a reconnect Packet guidPacket = new Packet(client.guid.ToByteArray(), null, Packet.TypeFlag.GUID, NUUtilities.GeneratePacketId()); TcpTransmissionState transmissionState = new TcpTransmissionState(guidPacket.data.Length, ref tcpClient, client, null); tcpClient.GetStream().BeginWrite(guidPacket.data, 0, guidPacket.data.Length, new AsyncCallback(EndReliableSend), transmissionState); //Start receiving messages from server TcpTransmissionState tcpTransmissionState = new TcpTransmissionState( NUUtilities.MTU, ref tcpClient, client, null); tcpClient.GetStream().BeginRead(tcpTransmissionState.data, 0, NUUtilities.MTU, new AsyncCallback(EndReliableReceive), tcpTransmissionState); UdpTransmissionState udpTransmissionState = new UdpTransmissionState( NUUtilities.MTU, ref udpClient, client, null); udpClient.BeginReceive(udpTransmissionState.data, 0, NUUtilities.MTU, SocketFlags.None, new AsyncCallback(EndUnreliableReceive), udpTransmissionState); Debug.Log("Connected to Server!"); } catch (Exception ex) { //Setup disconnection flag hasDisconnected = true; Debug.LogError("Could not connect to Server! " + ex.ToString()); return; } }
internal static void ProcessQueues() { m_onBroadcastResponse = NUUtilities.SanitizeAction(m_onBroadcastResponse); m_onConnected = NUUtilities.SanitizeAction(m_onConnected); m_onConnectionFailed = NUUtilities.SanitizeAction(m_onConnectionFailed); m_onDisconnected = NUUtilities.SanitizeAction(m_onDisconnected); m_onServerDisconnected = NUUtilities.SanitizeAction(m_onServerDisconnected); m_onConnectionTimeout = NUUtilities.SanitizeAction(m_onConnectionTimeout); m_onPacketReceived = NUUtilities.SanitizeAction(m_onPacketReceived); if (hasDisconnected) { //Reset flag hasDisconnected = false; bool connectionFailed = !connected; //Destroy client component object if (clientComponent != null) { GameObject.Destroy(clientComponent.gameObject); clientComponent = null; } //Clear dataQueue dataQueue.Clear(); //Clear sequential structures seqDataList.Clear(); lastPacketId = -1; //Clear MultiPart Buffers multiPartBuffers.Clear(); //Disconnect client instance if (client != null) { client.Disconnect(); } //Do proper callbacks if (calledDisconnect) { calledDisconnect = false; if (m_onDisconnected != null) { m_onDisconnected(); } return; } if (serverDisconnected) { serverDisconnected = false; if (m_onServerDisconnected != null) { m_onServerDisconnected(serverDisconnectMsg); } } if (connectionFailed) { if (m_onConnectionFailed != null) { m_onConnectionFailed(); } return; } else { if (m_onConnectionTimeout != null) { m_onConnectionTimeout(); } } } if (hasConnected) { //Reset flag hasConnected = false; //Do proper callback if (m_onConnected != null) { m_onConnected(); } } //Process broadcast callbacks if (broadcaster != null) { //Process broadcast callbacks lock (broadcastDataQueueLock) { while (broadcastDataQueue.Count > 0) { BroadcastPacket packet = broadcastDataQueue.Dequeue(); //Do proper callback if (m_onBroadcastResponse != null) { m_onBroadcastResponse(packet); } } } } if (!connected) { return; } //Process packet callbacks lock (dataQueueLock) { while (dataQueue.Count > 0) { Packet packet = dataQueue.Dequeue(); //Do proper callback if (m_onPacketReceived != null) { m_onPacketReceived(packet); } } } }
/// <summary> /// Take any object to serialize as byte array, packet destinations and packet message flag and build a Packet class /// </summary> /// <param name="obj"></param> /// <param name="destinationID"></param> /// <param name="messageFlag"></param> public Packet(object obj, Guid destinationID, TypeFlag messageFlag = TypeFlag.DATA, int packetId = -1) : this(NUUtilities.GetBytes(obj), new[] { destinationID }, messageFlag, packetId) { }
/// <summary> /// Take any object to serialize as byte array, packet destinations and packet message flag and build a Packet class /// </summary> /// <param name="obj"></param> /// <param name="destinationIDs"></param> /// <param name="messageFlag"></param> public Packet(object obj, Guid[] destinationIDs = null, TypeFlag messageFlag = TypeFlag.DATA, int packetId = -1) : this(NUUtilities.GetBytes(obj), destinationIDs, messageFlag, packetId) { }