如何配置套接字连接超时
Posted
技术标签:
【中文标题】如何配置套接字连接超时【英文标题】:How to configure socket connect timeout 【发布时间】:2010-11-06 21:59:46 【问题描述】:当客户端尝试连接断开连接的 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);
【问题讨论】:
【参考方案1】:我找到了这个。比公认的答案更简单,并且适用于 .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.");
//...
【讨论】:
好的,关于这个的输入很少——我喜欢这个并且它的代码少了很多。但是 !success 不是正确的条件。相反,添加 if (!_socket.Connected) 效果会更好。我会给它一个+1,因为少即是多。 取决于你的两个端点。如果他们都在数据中心,那么 1 秒应该足够了,3 秒是好的衡量标准,10 秒是善良的。如果一端在移动设备上,例如智能手机,那么您可能会看到 30 秒。 另一件事情也要注意...你是个例外。所以一定要检查... 如果我想增加超时而不是减少超时怎么办?我认为异步方法只是让您让代码不等待 20 秒(套接字连接中设置的内部超时)。但是如果连接需要更长的时间,BeginConnect 无论如何都会停止。或者,BeginConnect 是否在内部永远等待?我的连接速度非常慢,有时需要长达 30-40 秒才能连接,并且经常在 21 秒时超时。 @TravisWhidden 可以确认,这很重要!根据我的经验,如果可以到达端点,但端点上没有能够接收连接的服务器,则将向AsyncWaitHandle.WaitOne
发出信号,但套接字将保持未连接状态。【参考方案2】:
我的看法:
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.
【讨论】:
我冒昧地处理了一个条件。希望你不要介意。 Per cmets on top-rated answer,这看起来是一个副本,除了它被制成SocketExtension
,您还没有使用.Connected
来查看您是否是,并且你没有使用socket.Connected = true;
来定义success
。【参考方案3】:
我刚刚编写了一个扩展类以允许连接超时。就像使用标准 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)
【讨论】:
GhostDoc 的粉丝,是吗? ;-) “异步连接” - 经典的 GhostDoc WTFness。 :) 是的,有时甚至不是生成的内容的读者。 socket.EndConnect 比 socket.Close 更好?socket.EndConnect
需要大约 10 秒才能关闭,因此函数不会在时间跨度之后而是在时间跨度+endConnect 时间之后返回【参考方案4】:
我没有用 C# 编程,但在 C 中,我们通过使套接字非阻塞然后将 fd 置于选择/轮询循环中来解决相同的问题,超时值等于我们愿意等待的时间量连接成功。
我发现 this 用于 Visual C++,那里的解释也倾向于我之前解释的选择/轮询机制。
根据我的经验,您无法更改每个套接字的连接超时值。您可以全部更改(通过调整操作系统参数)。
【讨论】:
【参考方案5】:可能为时已晚,但有基于 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;
【讨论】:
是否有任何“ConnectAsync”重载接受主机和端口? @marsh-wiggle, "ConnectAsync" 方法有 4 个重载 docs.microsoft.com/en-us/dotnet/api/… 请查看扩展方法部分 @OlegBondarenko 好的,不适用于 .net 4.5.1。我必须自己包起来。谢谢!【参考方案6】:我通过使用 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();
【讨论】:
当计时器停止时,您会显示错误消息对吗?这如何阻止您的 TCP 堆栈实际连接。想象一个场景,远程主机距离超过 2 秒,即 rto > 2。您的计时器将停止,您将打印错误消息。但是,TCP 不受您的计时器控制。它仍然会尝试连接,并且可能会在 2 秒后成功连接。 C# 是否提供了取消“连接”请求或关闭套接字的功能。您的计时器解决方案等于在 2 秒后检查连接是否成功。 我发现了这个:splinter.com.au/blog/?p=28 看起来就是这样。它与您的相似,但我认为它与我上面解释的一样。 超时时,应调用m_clientSocket.Close(); 更新,aditya 引用的我的博客链接已更改:splinter.com.au/opening-a-tcp-connection-in-c-with-a-custom-t 我会重写与调用“timer_connection.Dispose();”相关的逻辑。 timer_connection 对象引用可能在对象被释放后使用。【参考方案7】:在MSDN 上查看。您似乎无法使用 Socket 类中已实现的属性来执行此操作。 MSDN 上的海报实际上solved his problem 使用线程。他有一个主线程调用其他线程,这些线程运行连接代码几秒钟,然后检查套接字的 Connected 属性:
我实际上创建了另一种方法 连接插座...有主 线程休眠 2 秒,然后 检查连接方法(至 在单独的线程中运行)如果 插座连接良好,否则 抛出异常“超时”和 就这样。再次感谢 回复。
你想做什么,为什么不能等待 15-30 秒才超时?
【讨论】:
【参考方案8】:我在连接到 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)
【讨论】:
【参考方案9】:我使用 Unity,但在使用 BeginConnect 和来自套接字的其他异步方法时遇到了一些问题。
有些东西我不明白,但之前的代码示例对我不起作用。
所以我写了这段代码来让它工作。我在带有 android 和 pc 的 adhoc 网络上测试它,也在我的计算机上本地。希望对您有所帮助。
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://***.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);
【讨论】:
【参考方案10】:这就像 FlappySock 的答案,但我添加了一个回调,因为我不喜欢布局以及布尔值是如何返回的。在尼克米勒的回答中:
以我的经验,如果可以到达端点,但端点上没有服务器能够接收连接,则 AsyncWaitHandle.WaitOne 将发出信号,但套接字将保持未连接状态
所以对我来说,依赖返回的内容似乎很危险——我更喜欢使用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
相关: How to check if I'm connected?
【讨论】:
【参考方案11】:Socket 类中应该有一个 ReceiveTimeout 属性。
Socket.ReceiveTimeout Property
【讨论】:
我试过了。它只是行不通。我添加了 m_clientSocket.ReceiveTimeout = 1000;在调用 m_clientSocket.Connect(ipEnd) 之前。但是,它仍然会等待大约 15-20 秒,然后才会弹出异常消息。 设置连接建立后套接字接收数据的超时时间。 不能使用ReceiveTimeout
- 这仅适用于使用BeginReceive
和EndReceive
接收时。当您只是查看您是否已连接时,没有等价物。以上是关于如何配置套接字连接超时的主要内容,如果未能解决你的问题,请参考以下文章