/// <summary>
        /// Create a connection to a remote endpoint
        /// </summary>
        public virtual NetConnection Connect(NetEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
        {
            if (remoteEndPoint == null)
            {
                throw new ArgumentNullException("remoteEndPoint");
            }
            if (m_configuration.DualStack)
            {
                remoteEndPoint = NetUtility.MapToIPv6(remoteEndPoint);
            }

            lock (m_connections)
            {
                if (m_status == NetPeerStatus.NotRunning)
                {
                    throw new NetException("Must call Start() first");
                }

                if (m_connectionLookup.ContainsKey(remoteEndPoint))
                {
                    throw new NetException("Already connected to that endpoint!");
                }

                NetConnection hs;
                if (m_handshakes.TryGetValue(remoteEndPoint, out hs))
                {
                    // already trying to connect to that endpoint; make another try
                    switch (hs.m_status)
                    {
                    case NetConnectionStatus.InitiatedConnect:
                        // send another connect
                        hs.m_connectRequested = true;
                        break;

                    case NetConnectionStatus.RespondedConnect:
                        // send another response
                        hs.SendConnectResponse(NetTime.Now, false);
                        break;

                    default:
                        // weird
                        LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status);
                        break;
                    }
                    return(hs);
                }

                NetConnection conn = new NetConnection(this, remoteEndPoint);
                conn.SetStatus(NetConnectionStatus.InitiatedConnect, "user called connect");
                conn.m_localHailMessage = hailMessage;

                // handle on network thread
                conn.m_connectRequested    = true;
                conn.m_connectionInitiator = true;

                m_handshakes.Add(remoteEndPoint, conn);

                return(conn);
            }
        }
Example #2
0
        internal bool ActuallySendPacket(byte[] data, int numBytes, NetEndPoint target, out bool connectionReset)
        {
            connectionReset = false;

            target = NetUtility.MapToIPv6(target);

            IPAddress ba = default(IPAddress);

            try
            {
                ba = NetUtility.GetCachedBroadcastAddress();

                // TODO: refactor this check outta here
                if (target.Address == ba)
                {
                    // Some networks do not allow
                    // a global broadcast so we use the BroadcastAddress from the configuration
                    // this can be resolved to a local broadcast addresss e.g 192.168.x.255
                    target.Address = m_configuration.BroadcastAddress;
                    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
                }

                int bytesSent = m_socket.SendTo(data, 0, numBytes, SocketFlags.None, target);
                if (numBytes != bytesSent)
                {
                    LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
                }

                // LogDebug("Sent " + numBytes + " bytes");
            }
            catch (SocketException sx)
            {
                if (sx.SocketErrorCode == SocketError.WouldBlock)
                {
                    // send buffer full?
                    LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
                    return(false);
                }
                if (sx.SocketErrorCode == SocketError.ConnectionReset)
                {
                    // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
                    connectionReset = true;
                    return(false);
                }
                LogError("Failed to send packet: " + sx);
            }
            catch (Exception ex)
            {
                LogError("Failed to send packet: " + ex);
            }
            finally
            {
                if (target.Address == ba)
                {
                    m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
                }
            }
            return(true);
        }
Example #3
0
        /// <summary>
        /// Create a connection to a remote endpoint.
        /// </summary>
        public virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage?hailMessage)
        {
            if (remoteEndPoint == null)
            {
                throw new ArgumentNullException(nameof(remoteEndPoint));
            }

            if (Configuration.DualStack)
            {
                remoteEndPoint = NetUtility.MapToIPv6(remoteEndPoint);
            }

            if (Status == NetPeerStatus.NotRunning)
            {
                throw new LidgrenException("Must call Start() first.");
            }

            if (ConnectionLookup.ContainsKey(remoteEndPoint))
            {
                throw new LidgrenException("Already connected to that endpoint!");
            }

            if (Handshakes.TryGetValue(remoteEndPoint, out NetConnection? hs))
            {
                // already trying to connect to that endpoint; make another try
                switch (hs.Status)
                {
                case NetConnectionStatus.InitiatedConnect:
                    // send another connect
                    hs._connectRequested = true;
                    break;

                case NetConnectionStatus.RespondedConnect:
                    // send another response
                    hs.SendConnectResponse(NetTime.Now, false);
                    break;

                default:
                    // weird
                    LogWarning(NetLogMessage.FromValues(NetLogCode.UnexpectedHandshakeStatus, value: (int)hs.Status));
                    break;
                }
                return(hs);
            }

            var conn = new NetConnection(this, remoteEndPoint);

            conn.Status           = NetConnectionStatus.InitiatedConnect;
            conn.LocalHailMessage = hailMessage;

            // handle on network thread
            conn._connectRequested    = true;
            conn._connectionInitiator = true;

            Handshakes.TryAdd(remoteEndPoint, conn);
            return(conn);
        }
        // TODO: replace byte[] with Memory<byte> in the future (held back by Socket.SendTo)
        // https://github.com/dotnet/runtime/issues/33418
        internal NetSocketResult ActuallySendPacket(byte[] data, int byteCount, IPEndPoint target)
        {
            Socket?socket = Socket;

            Debug.Assert(socket != null);

            bool broadcasting = false;

            try
            {
                IPAddress?ba = NetUtility.GetBroadcastAddress();

                // TODO: refactor this check outta here
                if (target.Address.Equals(ba))
                {
                    // Some networks do not allow
                    // a global broadcast so we use the BroadcastAddress from the configuration
                    // this can be resolved to a local broadcast addresss e.g 192.168.x.255
                    _targetCopy.Address = Configuration.BroadcastAddress;
                    _targetCopy.Port    = target.Port;

                    socket.EnableBroadcast = true;
                    broadcasting           = true;
                }
                else if (
                    Configuration.DualStack &&
                    Configuration.LocalAddress.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    // Maps to IPv6 for Dual Mode
                    NetUtility.MapToIPv6(target, _targetCopy);
                }
                else
                {
                    _targetCopy.Port    = target.Port;
                    _targetCopy.Address = target.Address;
                }

                int bytesSent = socket.SendTo(data, 0, byteCount, SocketFlags.None, _targetCopy);
                if (byteCount != bytesSent)
                {
                    LogWarning(NetLogMessage.FromValues(NetLogCode.FullSendFailure,
                                                        endPoint: target,
                                                        value: bytesSent,
                                                        maxValue: byteCount));
                }
                //LogDebug("Sent " + numBytes + " bytes");
            }
            catch (SocketException sx)
            {
                switch (sx.SocketErrorCode)
                {
                case SocketError.WouldBlock:
                    // send buffer full?
                    LogDebug(new NetLogMessage(NetLogCode.SocketWouldBlock, sx));
                    return(new NetSocketResult(false, false));

                case SocketError.ConnectionReset:
                    // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
                    return(new NetSocketResult(false, true));

                default:
                    LogError(new NetLogMessage(NetLogCode.SendFailure, sx, target));
                    break;
                }
            }
            catch (Exception ex)
            {
                LogError(new NetLogMessage(NetLogCode.SendFailure, ex, target));
            }
            finally
            {
                if (broadcasting)
                {
                    socket.EnableBroadcast = false;
                }
            }
            return(new NetSocketResult(true, false));
        }