Cách cấu hình thời gian chờ kết nối ổ cắm


104

Khi Khách hàng cố gắng kết nối với một địa chỉ IP đã ngắt kết nối, sẽ có thời gian chờ lâu hơn 15 giây ... Làm cách nào để giảm thời gian chờ này? Phương pháp để cấu hình nó là gì?

Mã tôi đang sử dụng để thiết lập kết nối ổ cắm như sau:

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

Câu trả lời:


146

Tôi đã tìm thấy cái này. Đơn giản hơn câu trả lời được chấp nhận và hoạt động với .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.");
}

//... 

20
OK, ít đầu vào cho cái này-- Tôi thích cái này và mã của nó ít hơn rất nhiều .. tuy nhiên! Thành công không phải là điều kiện chính xác. Thay vào đó, hãy thêm if (! _Socket.Connected) và nó hoạt động tốt hơn nhiều. Tôi sẽ cho nó một +1 cho khía cạnh ít hơn là nhiều hơn.
TravisWhiised

2
Phụ thuộc vào hai điểm cuối của bạn. Nếu cả hai đều nằm trong một trung tâm dữ liệu, thì 1 giây là nhiều, 3 là tốt, 10 là tốt. Nếu một đầu nằm trên thiết bị di động, chẳng hạn như điện thoại thông minh, thì bạn có thể nhìn vào 30 giây.
FlappySocks

3
Một điều quá tìm cho ra ... Nếu thay vì đặt nulltrong cho callbackvà bạn có kế hoạch EndConnect(), nếu ổ cắm đã được closedthì đây sẽ cung cấp cho bạn một ngoại lệ. Vì vậy, hãy chắc chắn rằng bạn kiểm tra ...
poy

9
Điều gì sẽ xảy ra nếu tôi muốn TĂNG thời gian chờ hơn là GIẢM thời gian chờ? Tôi nghĩ cách tiếp cận không đồng bộ chỉ cho phép bạn làm cho mã không đợi trong 20 giây (thời gian chờ nội bộ được đặt trong kết nối ổ cắm). Nhưng trong trường hợp nếu kết nối mất nhiều thời gian hơn, BeginConnect sẽ dừng lại. Hoặc, BeginConnect nội bộ đợi mãi mãi? Tôi có kết nối rất chậm khi đôi khi cần tới 30 - 40 giây để kết nối và thời gian chờ ở giây thứ 21 rất thường xuyên xảy ra.
Alex

3
@TravisWhiised Có thể khẳng định, điều này rất quan trọng! Theo kinh nghiệm của tôi, nếu điểm cuối có thể đạt được, nhưng không có máy chủ nào trên điểm cuối có thể nhận kết nối, thì AsyncWaitHandle.WaitOnesẽ được báo hiệu, nhưng ổ cắm vẫn không được kết nối.
Nicholas Miller

29

Của tôi:

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

Tôi đã có quyền tự do để xử lý một điều kiện. Mong bạn không phiền lòng.
Hemant

Mỗi nhận xét về câu trả lời được xếp hạng cao nhất, mà đây có vẻ là một bản sao ngoại trừ việc nó được tạo thành một SocketExtension, bạn vẫn chưa sử dụng .Connectedđể xem có phải là bạn không và bạn không sử dụng socket.Connected = true;để xác định success.
vapcguy

22

Tôi vừa viết một lớp mở rộng để cho phép hết thời gian chờ kết nối. Sử dụng nó chính xác như bạn sẽ sử dụng các Connect()phương thức tiêu chuẩn , với một tham số bổ sung được đặt tê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
Fan của GhostDoc phải không? ;-) "Không đồng bộ hóa kết nối" - GhostDoc WTFness cổ điển.
KeithS

1
:) có, và đôi khi thậm chí không có người đọc những gì đã được tạo ra.
picrap

Socket.EndConnect tốt hơn socket.Close?
Kiquenet

3
Các socket.EndConnectmất ~ 10 giây để đóng để trở về chức năng chứ không phải sau khoảng thời gian nhưng sau khoảng thời gian + endConnect thời gian
Royi Namir

8

Tôi không lập trình bằng C # nhưng trong C, chúng tôi giải quyết vấn đề tương tự bằng cách làm cho ổ cắm không bị chặn và sau đó đặt fd vào một vòng lặp chọn / thăm dò với giá trị thời gian chờ bằng khoảng thời gian chúng tôi sẵn sàng chờ kết nối. để thành công.

Tôi đã tìm thấy điều này cho Visual C ++ và lời giải thích cũng có phần nghiêng về cơ chế chọn / thăm dò mà tôi đã giải thích trước đây.

Theo kinh nghiệm của tôi, bạn không thể thay đổi giá trị thời gian chờ kết nối trên mỗi ổ cắm. Bạn thay đổi nó cho tất cả (bằng cách điều chỉnh các thông số hệ điều hành).


7

có thể đã quá muộn nhưng có một giải pháp gọn gàng dựa trên 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;
        }

Có bất kỳ quá tải "ConnectAsync" nào chấp nhận máy chủ và cổng không?
marsh-wiggle

@ marsh-wiggle, phương thức "ConnectAsync" có 4 lần quá tải docs.microsoft.com/en-us/dotnet/api/… Vui lòng xem phần Phương pháp mở rộng
Oleg Bondarenko

1
@OlegBondarenko ok, không có sẵn cho .net 4.5.1. Tôi phải tự mình quấn lại. Cảm ơn!
marsh-wiggle

5

Tôi đã giải quyết vấn đề bằng cách sử dụng Phương pháp Socket.ConnectAsync thay vì Phương pháp Socket.Connect. Sau khi gọi Socket.ConnectAsync (SocketAsyncEventArgs), hãy bắt đầu hẹn giờ (timer_connection), nếu hết thời gian, hãy kiểm tra xem kết nối socket đã được kết nối chưa (if (m_clientSocket.Connected)), nếu không, sẽ xuất hiện lỗi hết thời gian.

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
Khi bộ đếm thời gian dừng lại, bạn hiện thông báo lỗi đúng không? Làm thế nào điều này ngăn ngăn xếp TCP của bạn thực sự kết nối. Hãy tưởng tượng một tình huống, trong đó một máy chủ từ xa cách xa hơn 2 giây tức là rto> 2. Bộ đếm thời gian của bạn sẽ dừng và bạn sẽ in thông báo lỗi. Tuy nhiên, TCP không được kiểm soát bởi bộ đếm thời gian của bạn. Nó vẫn sẽ cố gắng kết nối và có thể kết nối thành công sau 2 giây. C # có cung cấp cơ sở để hủy yêu cầu "kết nối" hoặc đóng trên ổ cắm không. Giải pháp hẹn giờ của bạn là kiểm tra xem sau 2 giây kết nối có thành công hay không.
Aditya Sehgal

Tôi tìm thấy cái này: splinter.com.au/blog/?p=28 Có vẻ như đây là cách. Nó tương tự như của bạn nhưng tôi nghĩ rằng nó làm những gì tôi đã giải thích ở trên.
Aditya Sehgal

Khi hết thời gian, bạn nên gọi m_clientSocket.Close ();
Vincent McNabb

Cập nhật, liên kết blog của tôi được tham chiếu bởi aditya đã thay đổi: splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t
Chris

Tôi muốn viết lại logic liên quan đến cuộc gọi "timer_connection.Dispose ();". Tham chiếu đối tượng timer_connection có thể được sử dụng sau khi đối tượng được xử lý.
BoiseBaked

2

Kiểm tra điều này trên MSDN . Có vẻ như bạn có thể thực hiện việc này với các thuộc tính được triển khai trong lớp Socket.

Người đăng trên MSDN thực sự đã giải quyết được vấn đề của anh ấy bằng cách sử dụng phân luồng. Anh ta có một luồng chính gọi ra các luồng khác chạy mã kết nối trong vài giây và sau đó kiểm tra thuộc tính Connected của socket:

Tôi đã tạo một phương pháp khác mà thực sự kết nối với ổ cắm ... có luồng chính ở chế độ ngủ trong 2 giây và sau đó kiểm tra phương thức kết nối (phương thức này được chạy trong một luồng riêng biệt) nếu ổ cắm được kết nối tốt nếu không hãy ném một ngoại lệ "Đã hết thời gian" và đó là tất cả. Cảm ơn một lần nữa vì đã bổ sung.

Bạn đang cố gắng làm gì và tại sao nó không thể đợi trong 15-30 giây trước khi hết thời gian?


2

Tôi đã gặp vấn đề Tương tự khi kết nối với Ổ cắm và tôi đã đưa ra giải pháp bên dưới, Nó hoạt động tốt đối với tôi. `

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

Tôi đã làm việc với Unity và gặp một số vấn đề với BeginConnect và các phương thức không đồng bộ khác từ socket.

Có điều gì đó mà tôi không hiểu nhưng các mẫu mã trước đây không phù hợp với tôi.

Vì vậy, tôi đã viết đoạn mã này để làm cho nó hoạt động. Tôi kiểm tra nó trên một mạng adhoc với android và pc, cũng là cục bộ trên máy tính của tôi. Hy vọng nó có thể giúp ích.

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


}

và có một cơ quan giám sát rất đơn giản trên C # để làm cho nó hoạt động:

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

Đây giống như câu trả lời của FlappySock, nhưng tôi đã thêm một lệnh gọi lại vào nó vì tôi không thích bố cục và cách Boolean được trả về. Trong phần bình luận của câu trả lời đó từ Nick Miller:

Theo kinh nghiệm của tôi, nếu có thể đạt được điểm cuối nhưng không có máy chủ nào trên điểm cuối có thể nhận kết nối, thì AsyncWaitHandle.WaitOne sẽ được báo hiệu, nhưng ổ cắm sẽ vẫn không được kết nối

Vì vậy, đối với tôi, có vẻ như dựa vào những gì được trả lại có thể nguy hiểm - tôi thích sử dụng hơn socket.Connected. Tôi đặt một Boolean có thể vô hiệu hóa và cập nhật nó trong hàm gọi lại. Tôi cũng thấy rằng nó không phải lúc nào cũng hoàn thành việc báo cáo kết quả trước khi quay trở lại chức năng chính - tôi cũng xử lý điều đó và bắt nó đợi kết quả bằng cách sử dụng thời gian chờ:

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

Liên quan: Làm thế nào để kiểm tra xem tôi đã kết nối chưa?


-8

Cần có một thuộc tính ReceiveTimeout trong lớp Socket.

Thuộc tính Socket.ReceiveTimeout


1
Tôi đã thử. Nó chỉ không hoạt động. Tôi đã thêm m_clientSocket.ReceiveTimeout = 1000; trước khi gọi m_clientSocket.Connect (ipEnd). Tuy nhiên, nó vẫn đợi khoảng 15-20 giây trước khi bật lên thông báo ngoại lệ.
ninikin

2
Điều này đặt thời gian chờ khi ổ cắm đang nhận dữ liệu sau khi kết nối được thực hiện.
eric.christensen

1
Không thể sử dụng ReceiveTimeout- điều này hoàn toàn dành cho khi nhận với BeginReceiveEndReceive. Không có gì tương đương khi bạn chỉ xem liệu bạn có được kết nối hay không.
vapcguy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.