Soket bağlantı zaman aşımı nasıl yapılandırılır


104

İstemci, bağlantısı kesilmiş bir IP adresine bağlanmaya çalıştığında, 15 saniyenin üzerinde uzun bir zaman aşımı oluyor ... Bu zaman aşımını nasıl azaltabiliriz? Yapılandırma yöntemi nedir?

Bir soket bağlantısı kurmak için kullandığım kod aşağıdaki gibidir:

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

Yanıtlar:


146

Bunu buldum. Kabul edilen cevaptan daha basittir ve .NET v2 ile çalışır

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

//... 

20
Tamam, bu konuda çok az girdi-- Bunu beğendim ve çok daha az kodu var .. ancak! Başarı doğru koşul değil. Bunun yerine if (! _Socket.Connected) ekleyin ve çok daha iyi çalışır. Daha az ve daha fazla görünüm için ona +1 vereceğim.
TravisWhidden

2
İki uç noktanıza bağlıdır. Her ikisi de bir veri merkezindeyse, 1 saniye bol, iyi ölçü için 3, nazik olmak için 10 olmalıdır. Bir ucu akıllı telefon gibi bir mobil cihazdaysa, 30 saniyeye bakıyor olabilirsiniz.
FlappySocks

3
Başka bir şey de, bunun yerine koyma ... dikkat nulliçin de callbackve plan EndConnect()soket olduysa, closedo zaman bu size bir istisna verecektir. Bu yüzden kontrol ettiğinizden emin olun ...
poy

9
Zaman aşımını KESMEKTEN ARTIRMAK istersem ne olur? Bence zaman uyumsuz yaklaşım, kodu 20 saniye beklememenizi sağlar (soket bağlantısında ayarlanan dahili zaman aşımı). Ancak bağlantının daha uzun sürmesi durumunda, BeginConnect yine de duracaktır. Veya, BeginConnect dahili olarak sonsuza kadar mı bekliyor? Bazen bağlanmak için 30-40 saniyeye kadar gerekli olduğunda çok yavaş bir bağlantım var ve 21. saniyede zaman aşımları çok sık oluyor.
Alex

3
@TravisWhidden Doğrulayabilir, bu çok önemli! Deneyimlerime göre, uç noktaya ulaşılabiliyorsa, ancak uç noktada bağlantıyı alabilecek bir sunucu yoksa, o AsyncWaitHandle.WaitOnezaman sinyal verilecek, ancak soket bağlantısız kalacaktır.
Nicholas Miller

29

Benim almam:

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

Bir koşulu halletmek için özgürdüm. Umarım aldırmazsın.
Hemant

En çok oy alan yanıttaki yorumlar başına, bunun bir kopyası gibi görünüyor, ancak bunun bir kopyası olması dışında SocketExtension, hala olup olmadığınızı .Connectedgörmeye alışkın değilsiniz socket.Connected = true;ve tanımlamak için kullanmıyorsunuz success.
vapcguy

22

Bağlantılarda zaman aşımına izin vermek için bir uzantı sınıfı yazdım. Adında Connect()ekstra bir parametre ile , standart yöntemleri kullandığınız gibi kullanın 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)
            { }
        }
    }

8
GhostDoc hayranı mısınız? ;-) "Bağlantıyı eşzamansız hale getirir" - klasik GhostDoc WTFness.
KeithS

1
:) evet ve bazen neyin üretildiğini bile okuyamıyor.
pikap

Socket.Close'dan daha iyi socket.EndConnect?
Kiquenet

3
socket.EndConnectDeğil bir zaman süresinden sonra ancak zaman aralığı + endConnect süre sonra işlevini verir, böylece kapatmak için ~ 10 saniye sürer
Royi Namir

8

C # 'da programlamıyorum ama C de, aynı sorunu soketi engellemesiz hale getirerek ve ardından fd'yi bağlantı için beklemek istediğimiz süreye eşit bir zaman aşımı değerine sahip bir seçme / yoklama döngüsüne koyarak çözüyoruz. başarılı olmak için.

Bulduğum bu Visual C ++ ve ayrıca daha önce açıklandığı seçme / anket mekanizmasına doğru orada eğilir açıklama için.

Deneyimlerime göre, soket başına bağlantı zaman aşımı değerlerini değiştiremezsiniz. Herkes için değiştirirsiniz (OS parametrelerini ayarlayarak).


7

çok geç olabilir, ancak Task.WaitAny (c # 5 +) 'e dayalı temiz bir çözüm var:

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

Herhangi bir "ConnectAsync" aşırı yüklemesi ana bilgisayarı ve bağlantı noktasını kabul ediyor mu?
marsh-wiggle

@ marsh-wiggle, "ConnectAsync" yönteminde 4 aşırı yükleme var docs.microsoft.com/en-us/dotnet/api/… Lütfen Uzantı Yöntemleri bölümüne bakın
Oleg Bondarenko

1
@OlegBondarenko ok, .net için uygun değil 4.5.1. Kendim sarmalıyım. Teşekkürler!
marsh-wiggle

5

Sorunu Socket.Connect Method yerine Socket.ConnectAsync Method kullanarak çözdüm. Socket.ConnectAsync (SocketAsyncEventArgs) 'ı çağırdıktan sonra, bir zamanlayıcı (timer_connection) başlatın, zaman dolduysa, soket bağlantısının bağlı olup olmadığını kontrol edin (eğer (m_clientSocket.Connected)), değilse, zaman aşımı hatası açılır.

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

2
Zamanlayıcı durduğunda, bir hata mesajı veriyorsunuz değil mi? Bu, TCP yığınınızın gerçekten bağlanmasını nasıl durdurur? Uzak bir ana bilgisayarın 2 saniyeden fazla uzakta olduğu bir senaryo hayal edin, yani rto> 2. Zamanlayıcınız duracak ve hata mesajını yazdıracaksınız. Ancak, TCP zamanlayıcınız tarafından kontrol edilmez. Yine de bağlanmayı deneyecek ve 2 saniye sonra başarıyla bağlanabilir. C #, "bağlantı" isteklerini veya soketteki kapatmayı iptal etmek için bir olanak sağlar. Zamanlayıcı çözümünüz, bağlantının başarılı olup olmadığını 2 saniye sonra kontrol etmeye eşittir.
Aditya Sehgal

Bunu buldum: splinter.com.au/blog/?p=28 Görünüşe göre yol bu. Sizinkine benzer ama yukarıda anlattığım şeyi yaptığını düşünüyorum.
Aditya Sehgal

Zaman aşımına uğradığında, m_clientSocket.Close ();
Vincent McNabb

Güncelleme, aditya tarafından referans verilen blog bağlantım değişti: splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t
Chris

"Timer_connection.Dispose ();" çağrısıyla ilgili mantığı yeniden yazardım. Timer_connection nesne referansı muhtemelen nesne yerleştirildikten sonra kullanılır.
BoiseBaked

2

Bunu MSDN'de kontrol edin . Bunu Socket sınıfındaki uygulanan özelliklerle yapabileceğiniz görünmüyor.

MSDN'deki poster aslında sorununu iş parçacığı kullanarak çözdü . Birkaç saniye boyunca bağlantı kodunu çalıştıran ve ardından soketin Connected özelliğini kontrol eden diğer iş parçacıklarına çağrılan bir ana iş parçacığı vardı:

Aslında soketi bağlayan başka bir yöntem yarattım ... ana iş parçacığını 2 saniye boyunca uyuttum ve ardından bağlantı yöntemini kontrol ettim (ayrı bir iş parçacığında çalıştırılır) soket iyi bağlanmışsa aksi halde "Zaman aşımına uğradı" istisnası at ve hepsi bu. Çözümler için tekrar teşekkürler.

Ne yapmaya çalışıyorsun ve zaman aşımına uğramadan önce neden 15-30 saniye bekleyemiyor?


2

Bir Sokete bağlanırken aynı sorunu yaşadım ve aşağıdaki çözümü buldum, Benim için iyi çalışıyor. '

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

           }
       } 

1

Unity ile çalıştım ve BeginConnect ve soketten gelen diğer eşzamansız yöntemlerle bazı sorunlar yaşadım.

Anlamadığım bir şey var ama önceki kod örnekleri benim için çalışmıyor.

Bu yüzden çalışmasını sağlamak için bu kod parçasını yazdım. Bunu android ve pc ile adhoc bir ağda, ayrıca bilgisayarımda yerel olarak test ediyorum. Umarım yardımcı olabilir.

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


}

ve C # üzerinde çalışmasını sağlamak için çok basit bir bekçi köpeği var:

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

1

Bu, FlappySock'un cevabı gibidir, ancak buna bir geri arama ekledim çünkü düzeni ve Boolean'ın nasıl geri döndüğünü beğenmedim. Nick Miller'ın bu cevabının yorumlarında:

Tecrübelerime göre, uç noktaya ulaşılabiliyorsa, ancak uç noktada bağlantıyı alabilecek bir sunucu yoksa, AsyncWaitHandle.WaitOne sinyal verilecek, ancak soket bağlı kalmayacak

Bu yüzden bana göre, iade edilenlere güvenmek tehlikeli olabilir - kullanmayı tercih ederim socket.Connected. Boş değer atanabilir bir Boole ayarladım ve geri arama işlevinde güncelledim. Ayrıca, ana işleve dönmeden önce sonucu raporlamayı her zaman bitirmediğini fark ettim - bununla da ilgileniyorum ve zaman aşımını kullanarak sonucu bekletiyorum:

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

İlgili: Bağlı olup olmadığımı nasıl kontrol edebilirim?


-8

Socket sınıfında bir ReceiveTimeout özelliği olmalıdır.

Socket.ReceiveTimeout Özelliği


1
Denedim. Sadece çalışmıyor. M_clientSocket.ReceiveTimeout = 1000 ekledim; m_clientSocket.Connect (ipEnd) çağrılmadan önce. Bununla birlikte, istisna mesajını açmadan önce yaklaşık 15-20 saniye bekler.
ninikin

2
Bu, bağlantı yapıldıktan sonra soketin veri aldığı zaman için zaman aşımını ayarlar.
eric.christensen

1
Kullanılamaz ReceiveTimeout- bu kesinlikle BeginReceiveve ile alırken içindir EndReceive. Sadece bağlı olup olmadığınızı gördüğünüzde eşdeğeri yoktur.
vapcguy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.