C# 如何配置套接字连接超时

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1062035/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:18:13  来源:igfitidea点击:

How to configure socket connect timeout

c#socketstimeout

提问by ninikin

When the Client tries to connect to a disconnected IP address, there is a long timeout over 15 seconds... How can we reduce this timeout? What is the method to configure it?

当客户端尝试连接到断开连接的 IP 地址时,会出现超过 15 秒的长时间超时……我们如何减少此超时?它的配置方法是什么?

The code I'm using to set up a socket connection is as following:

我用来设置套接字连接的代码如下:

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

采纳答案by ninikin

I solved the problem by using Socket.ConnectAsync Method instead of Socket.Connect Method. After invoking the Socket.ConnectAsync(SocketAsyncEventArgs), start a timer (timer_connection), if time is up, check whether socket connection is connected (if(m_clientSocket.Connected)), if not, pop up timeout error.

我通过使用 Socket.ConnectAsync 方法而不是 Socket.Connect 方法解决了这个问题。调用Socket.ConnectAsync(SocketAsyncEventArgs)后,启动一个定时器(timer_connection),如果时间到,检查socket连接是否连接(if(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();
        }
    }

回答by Colin

There should be a ReceiveTimeout property in the Socket class.

Socket 类中应该有一个 ReceiveTimeout 属性。

Socket.ReceiveTimeout Property

Socket.ReceiveTimeout 属性

回答by eric.christensen

Check this out on MSDN. It does not appear that you can do this with the implemented properties in the Socket class.

The poster on MSDN actually solved his problemusing threading. He had a main thread which called out to other threads which run the connection code for a couple seconds and then check the Connected property of the socket:

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.

我创建了另一个实际连接套接字的方法......让主线程休眠 2 秒钟,然后检查连接方法(在单独的线程中运行)如果套接字连接良好,否则抛出异常“超时”就这样。再次感谢您的回复。

What are you trying to do, and why can't it wait for 15-30 seconds before timing out?

你想做什么,为什么不能在超时前等待 15-30 秒?

回答by Aditya Sehgal

I dont program in C# but in C, we solve the same problem by making the socket non-blocking and then putting the fd in a select/poll loop with a timeout value equal to the amount of time we are willing to wait for the connect to succeed.

我不在 C# 中编程,但在 C 中,我们通过使套接字非阻塞,然后将 fd 放入 select/poll 循环中,超时值等于我们愿意等待连接的时间来解决同样的问题成功。

I found thisfor Visual C++ and the explanation there also bends towards the select/poll mechanism I explained before.

我为 Visual C++找到了这个,那里的解释也倾向于我之前解释过的选择/轮询机制。

In my experience, you cannot change connect timeout values per socket. You change it for all (by tuning OS parameters).

根据我的经验,您无法更改每个套接字的连接超时值。您可以对其进行更改(通过调整操作系统参数)。

回答by picrap

I just wrote an extension class in order to allow timeouts in connections. Use it exactly as you would use the standard Connect()methods, with an extra parameter named timeout.

我刚刚编写了一个扩展类以允许连接超时。完全像使用标准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)
            { }
        }
    }

回答by FlappySocks

I found this. Simpler than the accepted answer, and works with .NET v2

我找到了这个。比接受的答案更简单,并且适用于 .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.");
}

//... 

回答by bevacqua

My take:

我的看法:

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

回答by Tyronne Thomas

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

连接到 Socket 时我遇到了同样的问题,我想出了以下解决方案,它对我来说很好用。`

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

           }
       } 

回答by Hugo Zevetel

I worked with Unity and had some problem with the BeginConnect and other async methods from socket.

我与 Unity 一起工作,但在使用来自套接字的 BeginConnect 和其他异步方法时遇到了一些问题。

There is something than I don't understand but the code samples before doesn't work for me.

有些东西我不明白,但之前的代码示例对我不起作用。

So I wrote this piece of code to make it work. I test it on an adhoc network with android and pc, also in local on my computer. Hope it can help.

所以我写了这段代码来让它工作。我在带有 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;
    }


}

and there a very simple watchdog on C# to make it work:

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

回答by vapcguy

This is like FlappySock's answer, but I added a callback to it because I didn't like the layout and how the Boolean was getting returned. In the comments of that answer from Nick Miller:

这就像 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
根据我的经验,如果可以到达端点,但端点上没有服务器能够接收连接,那么 AsyncWaitHandle.WaitOne 将发出信号,但套接字将保持未连接状态

So to me, it seems relying on what is returned can be dangerous - I prefer to use socket.Connected. I set a nullable Boolean and update it in the callback function. I also found it doesn't always finish reporting the result before returning to the main function - I handle for that, too, and make it wait for the result using the timeout:

所以对我来说,依赖返回的内容似乎很危险 - 我更喜欢使用socket.Connected. 我设置了一个可为空的布尔值并在回调函数中更新它。我还发现它在返回主函数之前并不总是完成报告结果 - 我也处理它,并使用超时让它等待结果:

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:How to check if I'm connected?

相关:如何检查我是否已连接?