public SimplexAsyncTransitStrategy(INetworkConnection con) { m_OwningConnection = con; LastUDPACKReceived = DateTime.MinValue; m_TCPSockState = new SockState(null, 0, null); m_TCPSockState.AsyncEventArgs = new SocketAsyncEventArgs(); }
// Add a SocketAsyncEventArg instance to the pool. // "item" = SocketAsyncEventArgs instance to add to the pool. public void Push(SocketAsyncEventArgs item) { if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); } SockState state = item.UserToken as SockState; #if !SILVERLIGHT item.AcceptSocket = null; #endif if (state != null) { state.Reset(); } if (!state.IsCached) { return; } lock (this.pool) { this.pool.Push(item); } }
// Removes a SocketAsyncEventArgs instance from the pool. // returns SocketAsyncEventArgs removed from the pool. public SocketAsyncEventArgs Pop() { lock (this.pool) { SocketAsyncEventArgs args = null; if (pool.Count > 0) { args = this.pool.Pop(); } else { args = new SocketAsyncEventArgs(); byte[] buffer = new byte[1024]; args.SetBuffer(buffer, 0, buffer.Length); SockState recState = new SockState(args, buffer.Length, null); recState.IsCached = false; recState.ID = Interlocked.Decrement(ref SockStateID); args.UserToken = recState; recState.BufferBlockOffset = 0; recState.BufferBlockLength = 1024; } return(args); } }
public void AssembleInboundPacket(byte[] buffer, int bytesReceived, SockState state) { try { int incomingPointer = 0; // how much of the incoming data have we read while (incomingPointer < bytesReceived) { if (state.MessageLength == -1) // don't know how long the message is, still need to read the envelope { int leftForEnvelope = SockState.EnvelopeLength - state.PacketBufferPointer.Position; int numToCopy = leftForEnvelope; int leftInBlock = bytesReceived - incomingPointer; if (numToCopy > leftInBlock) { numToCopy = leftInBlock; } Util.Copy(buffer, state.BufferBlockOffset + incomingPointer, state.PacketBuffer, state.PacketBufferPointer.Advance(numToCopy), numToCopy); incomingPointer += numToCopy; if (state.PacketBufferPointer.Position >= SockState.EnvelopeLength) { state.MessageLength = BitConverter.ToInt32(state.PacketBuffer, 0); state.PacketTypeID = BitConverter.ToInt32(state.PacketBuffer, 4); state.PacketSubTypeID = BitConverter.ToInt32(state.PacketBuffer, 8);// state.PacketBuffer[8]; state.Flags = (PacketFlags)state.PacketBuffer[12]; state.PacketBufferPointer.Reset(); state.PacketBuffer = new byte[state.MessageLength]; continue; } return; } int bytesNeededToCompleteMessage = state.PacketBuffer.Length - state.PacketBufferPointer.Position; int bytesToRead = bytesNeededToCompleteMessage; if (bytesToRead > bytesReceived - incomingPointer) { bytesToRead = bytesReceived - incomingPointer; } Util.Copy(buffer, state.BufferBlockOffset + incomingPointer, state.PacketBuffer, state.PacketBufferPointer.Advance(bytesToRead), bytesToRead); incomingPointer += bytesToRead; if (state.PacketBufferPointer.Position >= state.MessageLength) { DeserializePacket(state); state.Reset(); } } } catch (Exception readExc) { Log.LogMsg(readExc.Message + ": Shutting down socket.\r\n" + readExc.StackTrace); KillConnection(""); } }
/// <summary> /// Builds a packet object, given a raw binary buffer and some misc info /// </summary> /// <param name="state"></param> public void DeserializePacket(SockState state) { try { OnPacketReceived(); // at this point, the envelope bytes have been chopped off byte[] body = OnPacketDataArrived(state.PacketBuffer, 0, state.PacketBuffer.Length, state.Flags); if (body == null || body.Length < 1) { KillConnection("Authentication/Encryption Key error."); return; } //Log.LogMsg("Deserializing packet type " + state.PacketTypeID.ToString()); Packet newPacket = CreatePacket(state.PacketTypeID, state.PacketSubTypeID, state.Flags, body); newPacket.PacketSubTypeID = state.PacketSubTypeID; newPacket.RemoteEndPoint = state.AsyncEventArgs.RemoteEndPoint as IPEndPoint; // Send delivery confirmation, check for duplicate packets, etc if (OnBeforePacketProcessed(newPacket)) { // do not ever delay clock sync packets, no matter what. clock sync packets are used to // calculate latencies and to sync game clocks. waiting to respond is not an option, but since // the handler for this packet is deep in the root NetworkConnection hiearchy, it shouldn't // intefere, even with a client that otherwise delays packet handling if (newPacket.PacketTypeID == (int)PacketType.ClockSync) { HandlePacket(newPacket); } // Determine if the packet should be handled immediately or late else if (ProcessIncomingPacketsImmediately) { HandlePacket(newPacket); } else { lock (m_InboxLocker) { m_Inbox.Enqueue(newPacket); } } } } catch (Exception e) { Log.LogMsg("Exception in DeserializePacket: " + e.Message); KillConnection("Deserialize error. " + e.Message); } }
public bool StartListening(AddressFamily family, int port, Func <IPEndPoint, INetworkConnection> getConMethod) { try { GetConnnection = getConMethod; if (Socket == null) { Socket = new System.Net.Sockets.Socket(family, SocketType.Dgram, ProtocolType.Udp); Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); Socket.ExclusiveAddressUse = false; if (family == AddressFamily.InterNetworkV6) { Socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, 0); // set IPV6Only to false. enables dual mode socket - V4+v6 Socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port)); } else { Socket.Bind(new IPEndPoint(IPAddress.Any, port)); } } Port = ((IPEndPoint)Socket.LocalEndPoint).Port; m_ListenArgs = new byte[1024]; m_ListenerThread = new Thread(new ThreadStart(DoListen)); m_ListenerThread.IsBackground = true; m_ListenerThread.Name = "UDPListenerSimplex Read Thread"; m_State = new SockState(null, 1024, null); if (family == AddressFamily.InterNetworkV6) { m_EndPoint = new IPEndPoint(IPAddress.IPv6Any, 0); } else { m_EndPoint = new IPEndPoint(IPAddress.Any, 0); } m_ListenerThread.Start(); } catch (Exception e) { Log.LogMsg("UDPListenerSimplex - error start listen: " + e.Message); return(false); } Listening = true; Log.LogMsg("UDPListenerSimplex - Listening for UDP traffic on port " + port.ToString()); return(true); }
public static void Init(int bufferSize, int maxClients, int maxAcceptSockets) { maxClients *= 2; // each client has two sockets if (IsInitialized) { return; } IsInitialized = true; int total = bufferSize * ((maxClients * 2) + maxAcceptSockets + 3); m_Buffer = new BufferManager(total, bufferSize); m_PoolOfReadEventArgs = new SocketAsyncEventArgsStack(maxClients + 1); m_PoolOfAcceptEventArgs = new SocketAsyncEventArgsStack(maxAcceptSockets + 1); m_PoolOfSendEventArgs = new SocketAsyncEventArgsStack(maxClients + 1); // Allocate one large byte buffer block, which all I/O operations will //use a piece of. This gaurds against memory fragmentation. m_Buffer.InitBuffer(); // Let's make 1 extra SAEA to spare for each operation. If we do that, then we have to // consider it when we specify the buffer block's size in SocketListener // constructor. int[] maxOps = new int[] { maxClients + 1, maxClients + 1, maxAcceptSockets + 1 }; SocketAsyncEventArgsStack[] pools = new SocketAsyncEventArgsStack[] { m_PoolOfSendEventArgs, m_PoolOfReadEventArgs, m_PoolOfAcceptEventArgs }; for (int x = 0; x < maxOps.Length; x++) { for (int i = 0; i < maxOps[x]; i++) { SocketAsyncEventArgs args = new SocketAsyncEventArgs(); // assign a byte buffer from the buffer block to //this particular SocketAsyncEventArg object m_Buffer.SetBuffer(args); SockState recState = new SockState(args, bufferSize, pools[x]); recState.ID = i + 1; args.UserToken = recState; recState.BufferBlockOffset = args.Offset; // add this SocketAsyncEventArg object to the pool. pools[x].Push(args); } } }
/// <summary> /// Gets called when a receive operation resolves. If we were listening for data and the connection /// closed, that also counts as a receive operation resolving. /// </summary> /// <param name="args"></param> private void OnReceiveResolved(SocketAsyncEventArgs args) { // Log.LogMsg("Testy 21"); // Log.LogMsg("==>++++ Async RECEIVE Op Completed - #" + ((SockState)args.UserToken).ID.ToString() + "#"); try { if (!OwningConnection.IsAlive && args.BytesTransferred > 0) { // Log.LogMsg("Testy 22"); return; } SockState state = args.UserToken as SockState; // If there was a socket error, close the connection. This is NOT a normal // situation, if you get an error here. if (args.SocketError != SocketError.Success) { // Log.LogMsg("Testy 222"); OwningConnection.KillConnection("Connection lost! Network receive error: " + args.SocketError); //Jump out of the ProcessReceive method. return; } // If no data was received, close the connection. This is a NORMAL // situation that shows when the client has finished sending data. if (args.BytesTransferred == 0) { // Log.LogMsg("Testy 223"); OwningConnection.KillConnection("Connection closed by remote host."); return; } //// Log.LogMsg("Testy 224"); OwningConnection.ReceivedBytes(args.BytesTransferred); // restart listening process //// Log.LogMsg("Testy 225"); OwningConnection.AssembleInboundPacket(args, state); //// Log.LogMsg("Testy 226"); ListenForDataOnSocket(); } catch (Exception ex) { //// Log.LogMsg("Testy 23"); Log.LogMsg("Error ProcessReceive. " + ex.Message); OwningConnection.KillConnection("Error receive. " + ex.Message); } }
public static void PushAcceptEventArg(SocketAsyncEventArgs args, EventHandler <SocketAsyncEventArgs> oldIOCompletedHandler) { args.Completed -= oldIOCompletedHandler; if (m_PoolOfAcceptEventArgs != null) { m_PoolOfAcceptEventArgs.Push(args); //Log.LogMsg("==> Pushed accept arg #" + ((SockState)args.UserToken).ID.ToString() + "#. There are " + m_PoolOfAcceptEventArgs.Count.ToString() + " left"); } else { #if !SILVERLIGHT args.AcceptSocket = null; #endif SockState state = args.UserToken as SockState; if (state != null) { state.Reset(); } } }
/// <summary> /// Gets called when a receive operation resolves. If we were listening for data and the connection /// closed, that also counts as a receive operation resolving. /// </summary> /// <param name="args"></param> private void OnReceiveResolved(bool isUDP, byte[] buffer, int bytesReceived, SocketError status, SockState sockState) { SocketDebug(10); //// Log.LogMsg("==>++++ Async RECEIVE Op Completed - #" + ((SockState)args.UserToken).ID.ToString() + "#"); try { if (!OwningConnection.IsAlive && bytesReceived > 0) { return; } // If there was a socket error, close the connection. This is NOT a normal // situation, if you get an error here. if (status != SocketError.Success) { if (!OwningConnection.ShuttingDown) { SocketDebug(11); OwningConnection.KillConnection("Connection lost! Network receive error: " + status); } //Jump out of the ProcessReceive method. return; } SocketDebug(12); m_TCPSockState.AsyncEventArgs.RemoteEndPoint = OwningConnection.MyTCPSocket.RemoteEndPoint; // If no data was received, close the connection. This is a NORMAL // situation that shows when the client has finished sending data. if (bytesReceived == 0) { if (!OwningConnection.ShuttingDown) { OwningConnection.KillConnection("Connection closed by remote host."); } return; } OwningConnection.ReceivedBytes(bytesReceived); // restart listening process OwningConnection.AssembleInboundPacket(buffer, bytesReceived, sockState); } catch (ThreadAbortException abort) { } catch (Exception ex) { Log.LogMsg("Error ProcessReceive. " + ex.Message); OwningConnection.KillConnection("Error receive. " + ex.Message); } }
/// <summary> /// Gets called when a send operation resolves. /// </summary> private void OnSendResolved(SocketAsyncEventArgs args, SockState state) { //// Log.LogMsg("Testy 13"); try { OwningConnection.SentBytes(state.PacketBufferPointer.Position); bool isUDP = (state.Flags & PacketFlags.UDP) != 0; //Log.LogMsg("==>#### Async SEND Op Completed - #" + ((SockState)args.UserToken).ID.ToString() + "#"); if (args.SocketError == SocketError.Success) { //// Log.LogMsg("Testy 14"); state.PacketBufferPointer.Advance(args.BytesTransferred); if (state.PacketBufferPointer.Position >= state.PacketBuffer.Length) { OwningConnection.PacketSent(); // Done sending packet. state.Reset(); //Log.LogMsg("==>Done sending packet. Sent " + state.PacketBufferPointer.Position.ToString() + " bytes."); // done sending packet, see if we have anything in the queue ready to go bool more = false; Queue <NetQItem> sendQ = isUDP ? m_SendQueueUDP : m_SendQueue; lock (sendQ) { if (sendQ.Count > 0) { NetQItem itm = sendQ.Dequeue(); state.PacketBuffer = itm.Data; state.Flags = itm.Flags; more = true; } else { // release the sending lock if (isUDP) { //Log.LogMsg("UDP send queue emptied."); Interlocked.Exchange(ref m_SendingUDP, 0); } else { //Log.LogMsg("TCP send queue emptied."); Interlocked.Exchange(ref m_Sending, 0); } } } if (more) { //// Log.LogMsg("Testy 15"); SendBuffer(args, state, isUDP); } return; } else { //// Log.LogMsg("Testy 16"); // not done sending. send again. //Log.LogMsg("==>Continuing send. " + state.PacketBufferPointer.Position.ToString() + " / " + state.PacketBuffer.Length.ToString() + " sent so far."); SendBuffer(args, state, isUDP); } } else { //If we are in this else-statement, there was a socket error. OwningConnection.KillConnection("Error sending packet. " + args.SocketError.ToString()); } } catch (Exception ex) { //// Log.LogMsg("Testy 17"); Log.LogMsg("Failed to ProcessSend. " + ex.Message); OwningConnection.KillConnection("Send error. " + ex.Message); } }
/// <summary> /// We dont usually send everything all at once in the buffer. This method sends what's in the buffer out in a piece by piece fashion. /// </summary> private void SendBuffer(SocketAsyncEventArgs args, SockState state, bool isUDP) { try { #if !SILVERLIGHT //Log.LogMsg("==>$$$$ Async SEND op started - #" + ((SockState)args.UserToken).ID.ToString() + "#"); if (!OwningConnection.BlockingMode) #endif { int toSend = state.PacketBuffer.Length - state.PacketBufferPointer.Position; if (toSend > state.BufferBlockLength) { toSend = state.BufferBlockLength; } args.SetBuffer(state.BufferBlockOffset, toSend); Util.Copy(state.PacketBuffer, state.PacketBufferPointer.Position, args.Buffer, state.BufferBlockOffset, toSend); Socket socket = OwningConnection.MyTCPSocket; #if !SILVERLIGHT if (isUDP) { socket = OwningConnection.MyUDPSocket; //Log.LogMsg("UDP Send Target = " + SendTarget.ToString()); args.RemoteEndPoint = OwningConnection.UDPSendTarget; if (!socket.SendToAsync(args)) { //// Log.LogMsg("Testy 7"); OnSendResolved(args, state); } } else #endif { if (!socket.SendAsync(args)) { //// Log.LogMsg("Testy 8"); OnSendResolved(args, state); } } } #if !SILVERLIGHT else { if (isUDP) { //// Log.LogMsg("Testy 9"); OwningConnection.MyUDPSocket.SendTo(state.PacketBuffer, OwningConnection.UDPSendTarget); } else { //// Log.LogMsg("Testy 10"); OwningConnection.MyTCPSocket.Blocking = true; OwningConnection.MyTCPSocket.Send(state.PacketBuffer); } //// Log.LogMsg("Testy 11"); OwningConnection.SentBytes(state.PacketBuffer); OwningConnection.PacketSent(); } #endif } catch (Exception e) { //// Log.LogMsg("Testy 12"); Log.LogMsg("Error SendBuffer. " + e.Message); OwningConnection.KillConnection("Send error. " + e.Message); } }
/// <summary> /// Sends arbitrary bytes of data across the wire. Note that if the remote endpoint can't decipher the data /// as a known packet, the connection will be dropped by that endpoint immediately. /// </summary> /// <param name="data">bytes to send</param> public virtual int Send(byte[] data, PacketFlags flags) { //Log.LogMsg("@__@ Monitor Enter"); if (!OwningConnection.IsAlive) { //// Log.LogMsg("Testy 3"); return(-1); } //Log.LogMsg("==>Sending " + data.Length.ToString() + " bytes."); try { bool isUDP = (flags & PacketFlags.UDP) != 0; #if SILVERLIGHT // Silverlight can't do UDP and it can only send Async, i.e. Non-Blocking isUDP = false; OwningConnection.BlockingMode = false; #endif if (isUDP) { //// Log.LogMsg("Testy 4"); if (!OwningConnection.CanSendUDP) { Log.LogMsg("!!! Tried sending UDP packet when the connection wasn't yet ready to send UDP. Dropping packet."); return(0); } // Don't allow fragmenting UDP packets if (data.Length > m_SendArgsUDP.Buffer.Length && data.Length > 1024) { Log.LogMsg("Message exceeded UDP size. Sending via TCP instead."); flags &= ~PacketFlags.UDP; } } if (!OwningConnection.BlockingMode) { // send via asyncsend, but only one send op can be in progress at one time // out of order packets can happen because the actual send is done with I/O completion ports and if multiple // packets get submitted to the I/O queue they could be processed out of order, especially if the packet are of wildly differing sizes if (isUDP) { //// Log.LogMsg("Testy 5"); lock (m_SendQueueUDP) { if (1 == Interlocked.Exchange(ref m_SendingUDP, 1)) { // failed to get the lock - a message is in progress. queue message. NetQItem qi = new NetQItem(); qi.Flags = flags; qi.Data = data; m_SendQueueUDP.Enqueue(qi); Log.LogMsg("Queueing UDP packet. Now " + m_SendQueueUDP.Count.ToString() + " in Queue."); return(1); } //Log.LogMsg("Acquired UDP send lock."); } } else { lock (m_SendQueue) { if (1 == Interlocked.Exchange(ref m_Sending, 1)) { // failed to get the lock - a message is in progress. queue message. NetQItem qi = new NetQItem(); qi.Flags = flags; qi.Data = data; m_SendQueue.Enqueue(qi); Log.LogMsg("Queueing TCP packet. Now " + m_SendQueue.Count.ToString() + " in Queue."); return(1); } //Log.LogMsg("Acquired TCP send lock."); } } } SocketAsyncEventArgs args = isUDP ? m_SendArgsUDP : m_SendArgs; SockState state = args.UserToken as SockState; state.Flags = flags; state.PacketBuffer = data; //// Log.LogMsg("Testy 6"); SendBuffer(args, state, isUDP); } catch (Exception sendExc) { OwningConnection.KillConnection(sendExc.Message); return(-1);; } return(1); }
/// <summary> /// Gets called when a receive operation resolves. If we were listening for data and the connection /// closed, that also counts as a receive operation resolving. /// </summary> /// <param name="args"></param> private void OnReceiveResolved(SocketAsyncEventArgs args) { //// Log.LogMsg("Testy 50"); //// Log.LogMsg("==>++++ Async RECEIVE Op Completed - #" + ((SockState)args.UserToken).ID.ToString() + "#"); INetworkConnection con = null; try { if (m_ShuttingDown || !Listening) { return; } //// Log.LogMsg("Testy 51"); con = GetConnnection(args.RemoteEndPoint as IPEndPoint); if (args.SocketError == SocketError.Success && con == null || !con.IsAlive) { //// Log.LogMsg("Testy 52"); throw new ArgumentException("UDPListener - received packet from unknown connection " + args.RemoteEndPoint.ToString()); } SockState state = args.UserToken as SockState; if (args.SocketError != SocketError.Success) { //// Log.LogMsg("Testy 53"); con.KillConnection("UDP Connection lost! Network receive error: " + args.SocketError.ToString()); //Jump out of the ProcessReceive method. return; } // If no data was received, close the connection. This is a NORMAL // situation that shows when the client has finished sending data. if (args.BytesTransferred == 0) { //// Log.LogMsg("Testy 54"); con.KillConnection("Connection closed by remote host."); return; } // restart listening process //// Log.LogMsg("Testy 55"); con.AssembleInboundPacket(args, state); } catch (Exception ex) { Log.LogMsg("UDPListener Error ProcessReceive. " + ex.Message); if (con != null) { con.KillConnection("UDPListener Error receive. " + ex.Message); } } finally { if (!m_ShuttingDown) { bool willRaiseEvent = false; // Post async receive operation on the socket. //Log.LogMsg("==><<<< TCP Async RECEIVE op started - #" + ((SockState)m_RecArgs.UserToken).ID.ToString() + "#"); willRaiseEvent = Socket.ReceiveFromAsync(args); if (!willRaiseEvent) { OnReceiveResolved(args); } } } }
/// <summary> /// Assembles the packet across however many frames it takes /// </summary> /// <param name="data"></param> public void AssembleInboundPacket(SocketAsyncEventArgs args, SockState state) { AssembleInboundPacket(args.Buffer, args.BytesTransferred, state); }