2010-08-26 31 views
3

使用Accept方法创建的TcpClient实例用于管理客户端连接。当我需要终止服务器线程时会出现问题,因为它在接收呼叫时被阻止。处理阻塞.NET套接字的超时时间

因此,我设置了一个TcpClient ReceiveTimeout以循环每个n毫秒来测试退出条件。结果是Receive操作引发了一个具有错误代码SocketError.TimedOut的异常(SocketException)。好,我在想......

的问题是,Socket.Connected返回false,但作为MSDN文档中规定的属性:

Connected属性的值反映了作为连接的状态最近的操作。如果您需要确定连接的当前状态,请进行非阻塞的零字节发送呼叫。如果调用成功返回或抛出WAEWOULDBLOCK错误代码(10035),那么套接字仍然连接;否则,套接字不再连接。

所以,我做什么规定:

try { 
    // Receive operation on socket stream 
    // Send operation on socket stream 
} catch (SocketException e) { 
    if (e.SocketErrorCode == SocketError.TimedOut) { 
    try { 
     IAsyncResult asyncResult; 
     int sResult; 

     asyncResult = mSocket.Client.BeginSend(new byte[] {}, 0, 0, SocketFlags.None, delegate(IAsyncResult result) { }, null); 
     sResult = mSocket.Client.EndSend(asyncResult); 
     Debug.Assert(asyncResult.IsCompleted == true); 

     if (mSocket.Connected == false) 
      throw new Exception("not more connected"); // Always thrown 
    } catch (Exception e) { 
      // ... 
     } 
} 

但是,即使执行aynch发送操作,财产mSocket.Connected永远是假的,导致外环终止(其它线程调用Disconnect方法来终止服务器线程)。

我错过了什么?

回答

2

的问题是,如果出现超时的TcpClient断开连接。所以你的方法将无法工作。 使用异步读/写功能或使用select。

与异步函数调用的可能最简单的方法是这样的:

byte[] data = new byte[4096]; 
IASyncResult result = stream.BeginRead(data, 0, data.Length, null, null); 
result.AsyncWaitHandle.WaitOne(<timeout value in ms>); 
int bytes = stream.EndRead(result); 

if (!result.IsCompleted) 
    <timed out> 
else 
    <read data> 
... 
+0

恭候您的第一个答案!但这正是我试图避免的。对于使用非阻塞套接字,我会重构服务器逻辑并使每个网络操作异步,而不会产生额外的线程。 “问题是如果发生超时TcpClient断开连接”:它写在哪里?在stdc非阻塞套接字中,在超时(使用select实现)之后,不会断开连接。 – Luca 2010-08-26 13:20:44

+0

我发布的代码使用异步BeginRead/EndRead函数。但它阻止使用WaitOne。所以你不必改变你的服务器逻辑。 BeginRead不会阻止。 WaitOne阻塞,直到出现数据或发生指定的超时。使用!result.IsCompleted来检查它是否超时或有数据可用。 – pitt7 2010-08-26 14:07:25

+0

我不知道“如果发生超时,TcpClient断开连接”在哪里写入。但我有完全相同的问题。如果Read由超时返回,则连接为False。我没有对此做过多的研究,只是认为C#中并没有设置超时值,并定期运行到这个超时。 – pitt7 2010-08-26 14:11:04

0

您应该看看链接到的Socket.Connected MSDN页面上的C#示例。它具有明显不同的方法来确定套接字是否仍然连接。

// .Connect throws an exception if unsuccessful 
client.Connect(anEndPoint); 

// This is how you can determine whether a socket is still connected. 
bool blockingState = client.Blocking; 
try 
{ 
    byte [] tmp = new byte[1]; 

    client.Blocking = false; 
    client.Send(tmp, 0, 0); 
    Console.WriteLine("Connected!"); 
} 
catch (SocketException e) 
{ 
    // 10035 == WSAEWOULDBLOCK 
    if (e.NativeErrorCode.Equals(10035)) 
     Console.WriteLine("Still Connected, but the Send would block"); 
    else 
    { 
     Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode); 
    } 
} 
finally 
{ 
    client.Blocking = blockingState; 
} 

Console.WriteLine("Connected: {0}", client.Connected); 
+0

我也有同样的行为。发送例程不会抛出异常,并且Connected属性保持设置为false。后来的例程调用(发送/接收)的行为与galbarm答案描述的一样。 – Luca 2010-08-26 13:11:28