C#中的SocketException无法被捕获

8
我在代码中遇到了一个非常奇怪的情况。我正在开发一款使用C#编写的聊天客户端-服务器应用程序。当我关闭服务器时,我希望客户端能够自动关闭。客户端使用StremReader从TcpClient对象读取数据。客户端处于while循环中,它会读取一行(StreamReader.ReadLine()),然后对读取到的行进行一些操作。
当服务器被关闭时,我也会在服务器端关闭tcp连接。因此,我期望客户端会看到由readline引起的SocketException,捕获它并退出。但是异常没有被捕获!
以下是客户端循环代码:
 while (true)
 {
     try
     {
          ricev = read.ReadLine();
     }
     catch(SocketException exc)
     {
         //It never gets in here
     }
     chat.Invoke(showMessage, ricev);
 }

当我关闭服务器时,Visual Studio告诉我在System.dll中引发了一个“System.Net.Sockets.SocketException”的首次异常,但我无法捕获它。为什么会发生这种情况?我还尝试使用任何通用异常进行捕获。
catch
{
}

块,但这也不起作用。

任何帮助将不胜感激!

编辑:经过多次尝试后,我实际上发现SocketException根本没有被触发。这很奇怪,正如我在评论中所说的,在客户端在服务器之前关闭的相反情况下,异常被引发并且我可以捕获它。 我真的不知道发生了什么....


1
您是否在调试器中运行此程序?第一次机会异常是指抛出了异常但可以被捕获的情况。而第二次机会异常则是当异常未被捕获并终止了程序时发生的。您是在VS下运行还是独立运行? - James Michael Hare
应该可以工作。调试+异常,勾选CLR异常的Thrown框。等待调试器中断并开始单步调试以查看为什么它不起作用。 - Hans Passant
尝试添加一个catch(Exception e)子句,看看是否会在那里捕获。 - Evan M
我正在VS调试模式下运行这个程序。我已经尝试过使用catch(Exception e),但那也不起作用。 - l.moretto
StreamReader 的构造函数中出现了问题吗? - user7116
6个回答

1

如果我理解正确,场景是当您调用TcpListener对象中的Stop方法"_server.Stop()"时,在流上调用Read时它不会在客户端抛出SocketException... 我不知道为什么会发生这种情况,但我有一个解决方法,那就是访问TcpListener上的底层Socket并对其调用Shutdown

_server.Stop();
_server.Server.Shutdown(SocketShutdown.Both);//now at your "read.ReadLine();" will throw SocketException

编辑:您在评论中提到:

实际上,我正在关闭由accept方法返回的tcpclient。connClient.Close()

如果您停止了tcpListerner "_server.Stop()",然后关闭从_server.AcceptTcpCleint()方法获取的clients,那么在reader.ReadLine()处将抛出IOException:无法从传输连接读取数据:阻塞操作被WSACancelBlockingCall调用中断 我亲自测试过。


不,实际上我正在关闭由accept方法返回的TcpClient。connClient.Close()。这是关闭连接的正确方式吗?根据Visual Studio中的文档,它说关闭方法释放TcpClient的任何资源并要求关闭底层连接。我错了吗?无论如何,我一定会尝试关闭套接字,看看是否有效,感谢您的建议。 - l.moretto
如果你在accept方法中关闭了listener返回的tcpclient,那么在reader.ReadLine()处会抛出IOException异常。请查看我的回答编辑。 - Jalal Said
好的,感谢帮助。我会尝试做到这一点。无论如何,我几乎通过在读取循环中检查readline是否返回空字符串来解决了这个问题。如果发生这种情况,我就会打破循环。我还添加了一个“finally”块,在出现异常或循环结束时执行,因此在任何情况下,我现在都能够在服务器关闭连接时结束客户端程序。 - l.moretto
ReadLine() 方法是一个阻塞方法,这意味着在获取 null 或字符串之前它将被阻塞;等待某些数据“一行”被添加以便读取它... 也就是说,你不会得到 null 或任何数据,它只会像往常一样阻塞在那里而不抛出异常。 - Jalal Said
是的,但一旦连接关闭,它就会返回null,我已经测试过了。 - l.moretto

0

如果你正在调用Invoke,那么异常很可能会被包装在一个TargetInvocationException中。


0
当您关闭服务器套接字时,另一端会收到一个零字节的消息。这表示服务器已经正确关闭:

如果远程主机使用Shutdown方法关闭Socket连接,并且所有可用数据都已接收,则Receive方法将立即完成并返回零字节。

http://msdn.microsoft.com/en-us/library/8s4y8aff.aspx

只有在异常情况下才会抛出异常,例如当您重新启动服务器计算机时,客户端仍然连接到它 - 而不是正常的程序流程。

ReadLine() 操作也不应该抛出异常,而是在发生这种情况时简单地返回 null

返回值

从输入流中获取的下一行,如果达到输入流的末尾,则返回 null。

http://msdn.microsoft.com/en-us/library/system.io.streamreader.readline.aspx


0

我之前遇到过这个问题,结果发现我已经激活了“当CLR异常抛出时中断”并且没有取消勾选它

所以,请检查您是否没有取消勾选此功能
按Alt + Ctr + E 向下滚动到Common Runtime Language Exception并确保取消勾选“Thrown”复选框。 希望这可以帮助您。


0
Visual Studio告诉我在System.dll中引发了一个“System.Net.Sockets.SocketException”异常,但我无法捕获它。为什么会这样呢?
第一次机会异常被调试器拦截并打断您的程序。只有第二次机会异常才会进入catch块,这就是为什么您在第一次机会时没有进入catch块的原因。请参阅此article以获取更多信息。 从我上面提到的文章中提取 第一次机会异常消息通常不意味着代码存在问题。对于优雅处理异常的应用程序/组件,第一次机会异常消息让开发人员知道遇到了异常情况并进行了处理。
顺便说一下,您始终可以配置调试器以不停止第一次机会异常。

-2

这是因为.Net使用了不安全的Send/Receive方法。您必须处理您的程序上下文中的UnhandledException


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接