Вопрос по c#, sockets, timeout – Как настроить время ожидания подключения к сокету

90

Когда Клиент пытается подключиться к отключенному IP-адресу, происходит длительный тайм-аут более 15 секунд ... Как мы можем уменьшить этот тайм-аут? Какой способ настроить это?

Код, который я использую для установки соединения через сокет, выглядит следующим образом:

try
{
    m_clientSocket = new Socket(
         AddressFamily.InterNetwork,
         SocketType.Stream,
         ProtocolType.Tcp);

    IPAddress ip = IPAddress.Parse(serverIp);
    int iPortNo = System.Convert.ToInt16(serverPort);
    IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);

    m_clientSocket.Connect(ipEnd);
    if (m_clientSocket.Connected)
    {
        lb_connectStatus.Text = "Connection Established";
        WaitForServerData();
    }
}
catch (SocketException se)
{
    lb_connectStatus.Text = "Connection Failed";
    MessageBox.Show(se.Message);
}

Ваш Ответ

10   ответов
1

Я работал с Unity и имел некоторые проблемы с BeginConnect и другими асинхронными методами из сокета.

Есть кое-что, что я не понимаю, но примеры кода, которые я раньше не работал для меня.

Поэтому я написал этот кусок кода, чтобы он работал. Я тестирую его в сети adhoc с android и pc, а также в локальной сети на моем компьютере. Надеюсь, это поможет.

using System.Net.Sockets;
using System.Threading;
using System.Net;
using System;
using System.Diagnostics;

class ConnexionParameter : Guardian
{
    public TcpClient client;
    public string address;
    public int port;
    public Thread principale;
    public Thread thisthread = null;
    public int timeout;

    private EventWaitHandle wh = new AutoResetEvent(false);

    public ConnexionParameter(TcpClient client, string address, int port, int timeout, Thread principale)
    {
        this.client = client;
        this.address = address;
        this.port = port;
        this.principale = principale;
        this.timeout = timeout;
        thisthread = new Thread(Connect);
    }


    public void Connect()
    {
        WatchDog.Start(timeout, this);
        try
        {
            client.Connect(IPAddress.Parse(address), port);

        }
        catch (Exception)
        {
            UnityEngine.Debug.LogWarning("Unable to connect service (Training mode? Or not running?)");
        }
        OnTimeOver();
        //principale.Resume();
    }

    public bool IsConnected = true;
    public void OnTimeOver()
    {
        try
        {
            if (!client.Connected)
            {
                    /*there is the trick. The abort method from thread doesn't
 make the connection stop immediately(I think it's because it rise an exception
 that make time to stop). Instead I close the socket while it's trying to
 connect , that make the connection method return faster*/
                IsConnected = false;

                client.Close();
            }
            wh.Set();

        }
        catch(Exception)
        {
            UnityEngine.Debug.LogWarning("Connexion already closed, or forcing connexion thread to end. Ignore.");
        }
    }


    public void Start()
    {

        thisthread.Start();
        wh.WaitOne();
        //principale.Suspend();
    }

    public bool Get()
    {
        Start();
        return IsConnected;
    }
}


public static class Connexion
{


    public static bool Connect(this TcpClient client, string address, int port, int timeout)
    {
        ConnexionParameter cp = new ConnexionParameter(client, address, port, timeout, Thread.CurrentThread);
        return cp.Get();
    }

//http://stackoverflow.com/questions/19653588/timeout-at-acceptsocket
    public static Socket AcceptSocket(this TcpListener tcpListener, int timeoutms, int pollInterval = 10)
    {
        TimeSpan timeout = TimeSpan.FromMilliseconds(timeoutms);
        var stopWatch = new Stopwatch();
        stopWatch.Start();
        while (stopWatch.Elapsed < timeout)
        {
            if (tcpListener.Pending())
                return tcpListener.AcceptSocket();

            Thread.Sleep(pollInterval);
        }
        return null;
    }


}

и есть очень простой сторожевой таймер на C #, чтобы он работал:

using System.Threading;

public interface Guardian
{
    void OnTimeOver();
}

public class WatchDog {

    int m_iMs;
    Guardian m_guardian;

    public WatchDog(int a_iMs, Guardian a_guardian)
    {
        m_iMs = a_iMs;
        m_guardian = a_guardian;
        Thread thread = new Thread(body);
        thread.Start(this);
    }


    private void body(object o)
    {
        WatchDog watchdog = (WatchDog)o;
        Thread.Sleep(watchdog.m_iMs);
        watchdog.m_guardian.OnTimeOver();
    }

    public static void Start(int a_iMs, Guardian a_guardian)
    {
        new WatchDog(a_iMs, a_guardian);
    }
}
124

Я нашел это. Проще, чем принятый ответ, и работает с .NET v2

Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

// Connect using a timeout (5 seconds)

IAsyncResult result = socket.BeginConnect( sIP, iPort, null, null );

bool success = result.AsyncWaitHandle.WaitOne( 5000, true );

if ( socket.Connected )
{
    socket.EndConnect( result );
}
else 
{
     // NOTE, MUST CLOSE THE SOCKET

     socket.Close();
     throw new ApplicationException("Failed to connect server.");
}

//... 
@TravisWhidden Можете подтвердить, это очень важно! По моему опыту, если конечная точка может быть достигнута, но на конечной точке нет сервера, способного принять соединение, тоAsyncWaitHandle.WaitOne будет сигнализироваться, но сокет останется неподключенным.
Другое дело, тоже обратите внимание ... Если вместо того, чтобы положитьnull в течениеcallback и вы планируетеEndConnect(), если розетка былаclosed тогда это даст вам исключение. Поэтому убедитесь, что вы проверите ...
Ладно, мало информации об этом - мне нравится это, и его код намного меньше ... однако! Success не является правильным условием. Вместо этого добавьте if (! _Socket.Connected), и он будет работать намного лучше. Я дам ему +1, потому что чем меньше, тем больше аспект.
Что делать, если я хочу увеличить тайм-аут, а не УМЕНЬШИТЬ его? Я думаю, что асинхронный подход просто позволяет вам заставить код не ждать 20 секунд (внутренний тайм-аут установлен в сокете соединения). Но в случае, если подключение займет больше времени, BeginConnect все равно остановится. Или BeginConnect внутренне ждет вечно? У меня очень медленное соединение, когда иногда требуется до 30-40 секунд для соединения, и тайм-ауты на 21-й секунде происходят очень часто.
Зависит от ваших двух конечных точек. Если они оба находятся в центре обработки данных, то 1 секунды должно быть достаточно, 3 - для хорошей меры, 10 - для добра. Если один конец находится на мобильном устройстве, таком как смартфон, то вы можете смотреть на 30 секунд.
2

Проверьте это наMSDN, Похоже, что вы не можете сделать это с помощью реализованных свойств в классе Socket.

Плакат на MSDN собственнорешил свою проблему используя потоки. У него был основной поток, который вызывал другие потоки, которые запускают код соединения в течение пары секунд, а затем проверяют свойство Connected сокета:

I created another method wich actually connected the socket ... had the main thread sleep for 2 seconds and then check on the connecting method (wich is run in a separate thread) if the socket was connected good otherwise throw an exception "Timed out " and that;s all. Thanks again for the repleies.

Что вы пытаетесь сделать, и почему он не может ждать 15-30 секунд до истечения времени ожидания?

8

Я не программирую на C #, но в C мы решаем ту же проблему, делая неблокирование сокета, а затем помещая fd в цикл выбора / опроса со значением времени ожидания, равным количеству времени, которое мы готовы ждать соединения преуспеть.

я нашелэтот для Visual C ++ и его объяснения также происходит движение к механизму выбора / опроса, который я объяснил ранее.

По моему опыту, вы не можете изменить значения времени ожидания подключения для сокета. Вы меняете его для всех (путем настройки параметров ОС).

2

I had the Same problem when connecting to a Socket and I came up with the below solution ,It works Fine for me. `

private bool CheckConnectivityForProxyHost(string hostName, int port)
       {
           if (string.IsNullOrEmpty(hostName))
               return false;

           bool isUp = false;
           Socket testSocket = null;

           try
           {

               testSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
               IPAddress ip = null;
               if (testSocket != null && NetworkingCollaboratorBase.GetResolvedConnecionIPAddress(hostName, out ip))//Use a method to resolve your IP
               {
                   IPEndPoint ipEndPoint = new IPEndPoint(ip, port);

                   isUp = false;
//time out 5 Sec
                  CallWithTimeout(ConnectToProxyServers, 5000, testSocket, ipEndPoint);

                       if (testSocket != null && testSocket.Connected)
                       {
                           isUp = true;
                       }
                   }

               }
           }
           catch (Exception ex)
           {
               isUp = false;
           }
           finally
           {
               try
               {
                   if (testSocket != null)
                   {
                       testSocket.Shutdown(SocketShutdown.Both);
                   }
               }
               catch (Exception ex)
               {

               }
               finally
               {
                   if (testSocket != null)
                       testSocket.Close();
               }

           }

           return isUp;
       }


 private void CallWithTimeout(Action<Socket, IPEndPoint> action, int timeoutMilliseconds, Socket socket, IPEndPoint ipendPoint)
       {
           try
           {
               Action wrappedAction = () =>
               {
                   action(socket, ipendPoint);
               };

               IAsyncResult result = wrappedAction.BeginInvoke(null, null);

               if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds))
               {
                   wrappedAction.EndInvoke(result);
               }

           }
           catch (Exception ex)
           {

           }
       }

  private void ConnectToProxyServers(Socket testSocket, IPEndPoint ipEndPoint)
       {
           try
           {
               if (testSocket == null || ipEndPoint == null)
                   return;

                   testSocket.Connect(ipEndPoint);

           }
           catch (Exception ex)
           {

           }
       } 
4

Я решил проблему, используя метод Socket.ConnectAsync вместо метода Socket.Connect. После вызова Socket.ConnectAsync (SocketAsyncEventArgs) запустите таймер (timer_connection), если время истекло, проверьте, подключено ли сокетное соединение (если (m_clientSocket.Connected)), если нет, всплывающее сообщение об ошибке тайм-аута.

private void connect(string ipAdd,string port)
    {
        try
        {
            SocketAsyncEventArgs e=new SocketAsyncEventArgs();


            m_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            IPAddress ip = IPAddress.Parse(serverIp);
            int iPortNo = System.Convert.ToInt16(serverPort);
            IPEndPoint ipEnd = new IPEndPoint(ip, iPortNo);

            //m_clientSocket.
            e.RemoteEndPoint = ipEnd;
            e.UserToken = m_clientSocket;
            e.Completed+=new EventHandler<SocketAsyncEventArgs>(e_Completed);                
            m_clientSocket.ConnectAsync(e);

            if (timer_connection != null)
            {
                timer_connection.Dispose();
            }
            else
            {
                timer_connection = new Timer();
            }
            timer_connection.Interval = 2000;
            timer_connection.Tick+=new EventHandler(timer_connection_Tick);
            timer_connection.Start();
        }
        catch (SocketException se)
        {
            lb_connectStatus.Text = "Connection Failed";
            MessageBox.Show(se.Message);
        }
    }
private void e_Completed(object sender,SocketAsyncEventArgs e)
    {
        lb_connectStatus.Text = "Connection Established";
        WaitForServerData();
    }
    private void timer_connection_Tick(object sender, EventArgs e)
    {
        if (!m_clientSocket.Connected)
        {
            MessageBox.Show("Connection Timeout");
            //m_clientSocket = null;

            timer_connection.Stop();
        }
    }
Когда он истекает, вы должны вызвать m_clientSocket.Close ();
Обновление, ссылка на мой блог, на которую ссылается aditya, изменилась:splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t
Когда таймер останавливается, вы показываете сообщение об ошибке, верно? Как это останавливает ваш стек TCP от фактического подключения. Представьте себе сценарий, в котором удаленный хост находится на расстоянии более 2 секунд, то есть rto & gt; 2. Ваш таймер остановится, и вы напечатаете сообщение об ошибке. Тем не менее, TCP не контролируется вашим таймером. Он все равно попытается подключиться и может успешно подключиться через 2 секунды. Предоставляет ли C # возможность отмены & quot; подключить & quot; запросы или закрытие на сокете. Ваше решение по таймеру равно проверке через 2 секунды, успешно ли установлено соединение.
Я нашел это :splinter.com.au/blog/?p=28  Похоже, это так. Это похоже на ваше, но я думаю, что оно делает то, что я объяснил выше.
-7

В классе Socket должно быть свойство ReceiveTimeout.

Свойство Socket.ReceiveTimeout

Это устанавливает тайм-аут, когда сокет получает данные после установления соединения.
Я старался. Это просто не работает. Я добавил m_clientSocket.ReceiveTimeout = 1000; перед вызовом m_clientSocket.Connect (ipEnd). Тем не менее, он все еще ждет около 15-20 секунд, прежде чем всплывет сообщение об исключении. ninikin
Не могу использоватьReceiveTimeout - это строго при получении сBeginReceive а такжеEndReceive, Нет никакого эквивалента тому, когда вы просто видите, подключены ли вы.
22

Я просто написал класс расширения, чтобы разрешить тайм-ауты в соединениях. Используйте его точно так же, как вы бы использовали стандартConnect() методы, с дополнительным параметром с именемtimeout.

using System;
using System.Net;
using System.Net.Sockets;

/// <summary>
/// Extensions to Socket class
/// </summary>
public static class SocketExtensions
{
    /// <summary>
    /// Connects the specified socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="host">The host.</param>
    /// <param name="port">The port.</param>
    /// <param name="timeout">The timeout.</param>
    public static void Connect(this Socket socket, string host, int port, TimeSpan timeout)
    {
        AsyncConnect(socket, (s, a, o) => s.BeginConnect(host, port, a, o), timeout);
    }

    /// <summary>
    /// Connects the specified socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="addresses">The addresses.</param>
    /// <param name="port">The port.</param>
    /// <param name="timeout">The timeout.</param>
    public static void Connect(this Socket socket, IPAddress[] addresses, int port, TimeSpan timeout)
    {
        AsyncConnect(socket, (s, a, o) => s.BeginConnect(addresses, port, a, o), timeout);
    }

    /// <summary>
    /// Asyncs the connect.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="connect">The connect.</param>
    /// <param name="timeout">The timeout.</param>
    private static void AsyncConnect(Socket socket, Func<Socket, AsyncCallback, object, IAsyncResult> connect, TimeSpan timeout)
    {
        var asyncResult = connect(socket, null, null);
        if (!asyncResult.AsyncWaitHandle.WaitOne(timeout))
        {
            try
            {
                socket.EndConnect(asyncResult);
            }
            catch (SocketException)
            { }
            catch (ObjectDisposedException)
            { }
        }
    }
Лучше socket.EndConnect, чем socket.Close?
socket.EndConnect Закрытие занимает ~ 10 секунд, поэтому функция возвращается не после промежутка времени, а после промежутка времени + время окончания соединения
Фанат GhostDoc, ты? ;-) & quot; Асинхронизирует соединение & quot; классический GhostDoc WTFness.
Любой пример исходного кода с использованием ваших расширений?
28

Мой дубль:

public static class SocketExtensions
{
    /// <summary>
    /// Connects the specified socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="endpoint">The IP endpoint.</param>
    /// <param name="timeout">The timeout.</param>
    public static void Connect(this Socket socket, EndPoint endpoint, TimeSpan timeout)
    {
        var result = socket.BeginConnect(endpoint, null, null);

        bool success = result.AsyncWaitHandle.WaitOne(timeout, true);
        if (success)
        {
            socket.EndConnect(result);
        }
        else
        {
            socket.Close();
            throw new SocketException(10060); // Connection timed out.
        }
    }
}
Любой пример исходного кода с его использованием?
Я взял на себя смелость справиться с условием. Надеюсь, вы не возражаете.
идеально. спасибо за этот ответ
В комментариях к ответу с самым высоким рейтингом, который выглядит как копия, за исключением того, что он превращен вSocketExtensionВы все еще не использовали.Connected чтобы увидеть, если вы, и вы не используетеsocket.Connected = true; определитьsuccess.
1

Это похоже на ответ FlappySock, но я добавил к нему обратный вызов, потому что мне не нравился макет и то, как возвращается логическое значение. В комментариях этого ответа от Ника Миллера:

In my experience, if the end point can be reached, but there is no server on the endpoint able to receive the connection, then AsyncWaitHandle.WaitOne will be signaled, but the socket will remain unconnected

Так что мне кажется, что полагаться на то, что возвращено, может быть опасно - я предпочитаю использоватьsocket.Connected, Я установил логическое значение Nullable и обновил его в функции обратного вызова. Я также обнаружил, что он не всегда заканчивает сообщать о результате, прежде чем вернуться к основной функции - я тоже обрабатываю это и заставляю его ждать результат, используя тайм-аут:

private static bool? areWeConnected = null;

private static bool checkSocket(string svrAddress, int port)
{
    IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(svrAddress), port);
    Socket socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

    int timeout = 5000; // int.Parse(ConfigurationManager.AppSettings["socketTimeout"].ToString());
    int ctr = 0;
    IAsyncResult ar = socket.BeginConnect(endPoint, Connect_Callback, socket);
    ar.AsyncWaitHandle.WaitOne( timeout, true );

    // Sometimes it returns here as null before it's done checking the connection
    // No idea why, since .WaitOne() should block that, but it does happen
    while (areWeConnected == null && ctr < timeout)
    {
        Thread.Sleep(100);
        ctr += 100;
    } // Given 100ms between checks, it allows 50 checks 
      // for a 5 second timeout before we give up and return false, below

    if (areWeConnected == true)
    {
        return true;
    }
    else
    {
        return false;
    }
}

private static void Connect_Callback(IAsyncResult ar)
{
    areWeConnected = null;
    try
    {
        Socket socket = (Socket)ar.AsyncState;
        areWeConnected = socket.Connected;
        socket.EndConnect(ar);
    }
    catch (Exception ex)
    {
      areWeConnected = false;
      // log exception 
    }
}

Related: Как проверить, подключен ли я?

Похожие вопросы