System.Net.Sockets.Socket.Disconnect底层逻辑与挂钩检测方案咨询
问题解答
System.Net.Sockets.Socket.Disconnect 底层调用逻辑
你之前挂钩的WinSock函数没有捕获到调用,核心原因是你伪造的Socket从未被标记为已连接状态,.NET Runtime的托管层校验会直接抛出你看到的SocketException,根本不会走到下层非托管WinSock接口调用的步骤。
正常情况下,Windows平台下面向连接的Socket(如TCP)执行Disconnect(Boolean reuseSocket)的完整流程为:
- 第一步托管层校验Socket状态:通过调用WinSock的
getpeername、或者读取SO_CONNECT_STATE套接字选项判断Socket是否处于已连接状态,如果未连接直接抛出异常,终止后续流程 - 第二步如果校验通过,首先调用
shutdown(SHUT_SEND)关闭Socket的发送通道 - 第三步调用
WSASendDisconnect发送断开连接通知,等待对端响应 - 第四步如果
reuseSocket参数为true,调用setsockopt设置SO_REUSEADDR选项 - 第五步如果是同步Disconnect调用,会等待所有未完成的IO操作结束后返回
卡顿原因
你实现的recv钩子采用无限循环等待数据,C#程序触发Disconnect抛出异常后,异常处理逻辑会等待当前未完成的IO操作(也就是你阻塞的recv调用)结束,无限循环没有退出信号自然会导致程序卡顿。
解决方案&需要额外挂钩的函数
- 首先新增挂钩
getpeername和getsockopt:对你自己拦截伪造的Socket,在这两个函数的钩子里直接返回成功,伪造已连接的状态和假的对端地址,让Disconnect可以通过托管层校验,继续执行后续的WinSock调用 - 已经挂钩的
shutdown函数不需要修改逻辑,但是要补充:在shutdown的处理逻辑中添加对对应Socket的recv循环的终止信号,不用等到closesocket被调用才终止循环,Disconnect流程会优先调用shutdown,这一步就能结束阻塞的recv逻辑 - 如果是.NET Core/.NET 5+的程序,还可以额外挂钩
WSADisconnectEvent这个相对冷门的WinSock扩展函数,部分版本的.NET Runtime会调用这个函数处理断开逻辑
额外说明
如果不想修改状态伪造逻辑,也可以在你的recv循环里增加超时检测,或者定期检查当前Socket的状态标记,一旦发现上层程序已经触发了断开相关的操作就主动退出循环,避免永久阻塞。
内容的提问来源于stack exchange,提问作者user13095983
相关产品推荐
相关产品推荐

