Cum se configurează timeout-ul de conectare a socket-ului (Programare, C#, Prize, Timeout)

ninikin a intrebat.

Când clientul încearcă să se conecteze la o adresă IP deconectată, există un timp de așteptare lung de peste 15 secunde… Cum putem reduce acest timeout? Care este metoda de configurare a acestuia?

Codul pe care îl folosesc pentru a configura o conexiune socket este următorul:

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);
}

11 răspunsuri
FlappySocks

Am găsit asta. Mai simplu decât răspunsul acceptat și funcționează cu .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.");
}

//... 

Comentarii

    20

  • OK, puțină contribuție la acesta… Îmi place acest lucru și este mult mai puțin cod… cu toate acestea, !success nu este condiția corectă. În schimb, adăugați if (!_socket.Connected) și funcționează mult mai bine. Îi dau un +1 pentru aspectul „mai puțin este mai mult”. –  > Por TravisWhidden.
  • Depinde de cele două puncte finale. Dacă ambele se află într-un centru de date, atunci 1 secundă ar trebui să fie suficientă, 3 pentru o măsură bună, 10 pentru a fi amabil. Dacă un capăt se află pe un dispozitiv mobil, cum ar fi un smartphone, atunci s-ar putea să aveți în vedere 30 de secunde. –  > Por FlappySocks.
  • Un alt lucru la care trebuie să fiți atenți… Dacă în loc să pui null în loc de callback și intenționați să EndConnect(), , dacă soclul a fost closed atunci acest lucru vă va da o excepție. Deci, asigurați-vă că verificați… –  > Por poy.
  • Ce se întâmplă dacă vreau să măresc timpul de așteptare în loc să-l micșorez? Cred că abordarea asincronă vă permite doar să faceți codul să nu aștepte 20 de secunde (timeout-ul intern setat în socket connect). Dar, în cazul în care conexiunea durează mai mult, BeginConnect se va opri oricum. Sau, BeginConnect așteaptă în mod intern la nesfârșit? Am o conexiune foarte lentă, când uneori este nevoie de până la 30-40 de secunde pentru a se conecta, iar timeout-urile la 21 de secunde apar foarte des. –  > Por Alex.
  • @TravisWhidden Pot confirma, acest lucru este foarte important! Din experiența mea, dacă punctul final poate fi atins, dar nu există un server pe punctul final capabil să primească conexiunea, atunci AsyncWaitHandle.WaitOne va fi semnalat, dar socket-ul va rămâne neconectat. –  > Por Nicholas Miller.
bevacqua

Părerea mea:

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.
        }
    }
}

Comentarii

  • Mi-am luat libertatea de a gestiona o condiție. Sper că nu vă deranjează. –  > Por Hemant.
  • Conform comentariilor la răspunsul cel mai bine cotat, care pare a fi o copie a acestuia, cu excepția faptului că este transformat într-o SocketExtension, , încă nu ați folosit .Connected pentru a vedea dacă da, iar tu nu folosești socket.Connected = true; pentru a defini success. –  > Por vapcguy.
picrap

Tocmai am scris o clasă de extensie pentru a permite timeout-uri în conexiuni. Folosiți-o exact așa cum ați folosi clasa standard Connect() cu un parametru suplimentar numit 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)
            { }
        }
    }

Comentarii

  • Ești fan al GhostDoc, nu-i așa? 😉 „Asincronizează conexiunea” – WTFness clasic GhostDoc. –  > Por KeithS.
  • 🙂 da, și uneori nici măcar un cititor a ceea ce a fost generat. –  > Por picrap.
  • Mai bine socket.EndConnect decât socket.Close ? –  > Por Kiquenet.
  • socket.EndConnect are nevoie de ~10 secunde pentru a se închide, astfel încât funcția se întoarce nu după intervalul de timp, ci după intervalul de timp + timpul de terminare a conexiunii –  > Por Royi Namir.
Oleg Bondarenko

s-ar putea să fie prea târziu, dar există o soluție îngrijită bazată pe Task.WaitAny (c# 5 +) :

 public static bool ConnectWithTimeout(this Socket socket, string host, int port, int timeout)
        {
            bool connected = false;
            Task result = socket.ConnectAsync(host, port);               
            int index = Task.WaitAny(new[] { result }, timeout);
            connected = socket.Connected;
            if (!connected) {
              socket.Close();
            }

            return connected;
        }

Comentarii

  • Există vreo supraîncărcare „ConnectAsync” care acceptă gazdă și port? –  > Por marsh-wiggle.
  • @marsh-wiggle, metoda „ConnectAsync” are 4 supraîncărcări docs.microsoft.com/en-us/dotnet/api/… Vă rugăm să consultați secțiunea Extension Methods –  > Por Oleg Bondarenko.
  • @OlegBondarenko ok, nu este disponibil pentru .net 4.5.1. Trebuie să o împachetez eu însumi. Mulțumesc! –  > Por marsh-wiggle.
Aditya Sehgal

Nu programez în C#, dar în C, rezolvăm aceeași problemă făcând socket-ul să nu fie blocant și apoi punând fd-ul într-o buclă select/poll cu o valoare de timeout egală cu timpul pe care suntem dispuși să îl așteptăm pentru ca conectarea să reușească.

Am constatat că acest pentru Visual C++ și explicația de acolo se înclină, de asemenea, spre mecanismul select/poll pe care l-am explicat mai devreme.

Din experiența mea, nu se pot modifica valorile timeout-ului de conectare pentru fiecare socket. Le schimbi pentru toate (prin reglarea parametrilor sistemului de operare).

ninikin

Am rezolvat problema folosind metoda Socket.ConnectAsync în loc de metoda Socket.Connect.După invocarea metodei Socket.ConnectAsync(SocketAsyncEventArgs), porniți un cronometru (timer_connection), dacă timpul a expirat, verificați dacă conexiunea socket-ului este conectată (if(m_clientSocket.Connected)), dacă nu, afișați eroarea de timeout.

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();
        }
    }

Comentarii

  • Când se oprește cronometrul, se afișează un mesaj de eroare, nu-i așa? Cum oprește acest lucru ca stiva TCP să se conecteze efectiv. Imaginați-vă un scenariu în care o gazdă la distanță este la mai mult de 2 secunde distanță, adică rto > 2. Cronometrul se va opri și veți afișa mesajul de eroare. Cu toate acestea, TCP nu este controlat de temporizatorul dvs. Acesta va încerca în continuare să se conecteze și s-ar putea conecta cu succes după 2 secunde. Oferă C# o facilitate de anulare a cererilor de „conectare” sau de închidere a unui socket. Soluția dvs. de temporizare este egală cu verificarea după 2 secunde dacă conexiunea a reușit sau nu. –  > Por Aditya Sehgal.
  • Am găsit acest lucru : splinter.com.au/blog/?p=28 Se pare că aceasta este soluția. Este similar cu al tău, dar cred că face ceea ce am explicat mai sus. –  > Por Aditya Sehgal.
  • Când se termină, trebuie să apelați m_clientSocket.Close(); –  > Por Vincent McNabb.
  • Actualizare, linkul blogului meu la care face referire Aditya s-a schimbat: splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t –  > Por Chris.
  • Aș rescrie logica legată de apelul „timer_connection.Dispose();”. Referința obiectului timer_connection este posibil să fie utilizată după ce obiectul este eliminat. –  > Por BoiseBaked.
eric.christensen

Verificați acest lucru pe MSDN. Nu se pare că puteți face acest lucru cu proprietățile implementate în clasa Socket.

Afișul de pe MSDN de fapt și-a rezolvat problema folosind threading. El a avut un fir principal care a apelat la alte fire care execută codul de conectare timp de câteva secunde și apoi verifică proprietatea Connected a socket-ului:

Am creat o altă metodă care a conectat efectiv socket-ul… am pus firul principal să doarmă timp de 2 secunde și apoi să verifice în metoda de conectare (care este executată într-un fir separat) dacă socket-ul a fost conectat bine, în caz contrar, să arunce o excepție „Timed out” și asta e tot. Mulțumesc din nou pentru repleies.

Ce încerci să faci și de ce nu poate aștepta 15-30 de secunde înainte de a se opri?

Tyronne Thomas

Am avut aceeași problemă la conectarea la un Socket și am găsit soluția de mai jos, care funcționează bine pentru mine.

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)
           {

           }
       } 

Hugo Zevetel

Am lucrat cu Unity și am avut unele probleme cu BeginConnect și alte metode asincrone din socket.

Există ceva decât nu înțeleg, dar mostrele de cod de mai înainte nu funcționează pentru mine.

Așa că am scris această bucată de cod pentru a o face să funcționeze. Am testat-o pe o rețea adhoc cu android și PC, de asemenea, în local pe computerul meu. Sper că vă poate ajuta.

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;
    }


}

și există un watchdog foarte simplu pe C# pentru a face să funcționeze:

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);
    }
}

vapcguy

Acesta este ca și răspunsul lui FlappySock, dar am adăugat un callback la el pentru că nu mi-a plăcut aspectul și modul în care Boolean era returnat. În comentariile acestui răspuns de la Nick Miller:

Din experiența mea, dacă punctul final poate fi atins, dar nu există un server pe punctul final capabil să primească conexiunea, atunci AsyncWaitHandle.WaitOne va fi semnalat, dar socket-ul va rămâne neconectat.

Așadar, mi se pare că este periculos să te bazezi pe ceea ce este returnat – prefer să folosesc socket.Connected. Am setat un boolean nul și îl actualizez în funcția de apelare. Am constatat, de asemenea, că nu termină întotdeauna de raportat rezultatul înainte de a se întoarce la funcția principală – mă ocup și de acest lucru și îl fac să aștepte rezultatul folosind timeout-ul:

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 
    }
}

Înrudite: Cum să verific dacă sunt conectat?

Colin

Ar trebui să existe o proprietate ReceiveTimeout în clasa Socket.

Proprietatea Socket.ReceiveTimeout

Comentarii

  • Am încercat. Pur și simplu nu funcționează. Am adăugat m_clientSocket.ReceiveTimeout = 1000; înainte de a invoca m_clientSocket.Connect(ipEnd). Cu toate acestea, încă așteaptă aproximativ 15-20 de secunde înainte de a afișa mesajul de excepție. –  > Por ninikin.
  • Aceasta setează timpul de așteptare pentru momentul în care socket-ul primește date după ce a fost realizată conexiunea. –  > Por eric.christensen.
  • Nu se poate utiliza ReceiveTimeout – aceasta este strict pentru când se primește cu BeginReceive și EndReceive. Nu există un echivalent pentru atunci când doar vezi dacă ești conectat. –  > Por vapcguy.