[Win32]防止套接字被继承
有一个网络应用程序,需要创建子进程,同时要将一个内核对象的句柄传递给子进程使用。句柄默认是不可继承的,为了达到这个目的,要在创建内核对象的时候指定其句柄是可继承的,然后在调用CreateProcess的时候将bInheritHandles参数设置为TRUE,像下面那样(以创建Mutex为例):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | SECURITY_ATTRIBUTES sa = { 0 }; sa.nLength = sizeof (sa); sa.bInheritHandle = TRUE; HANDLE hMutex = CreateMutex(&sa, TRUE, L "Mutex" ); STARTUPINFO si = { 0 }; //... 设置si的各个字段 PROCESS_INFORMATION pi; CreateProcess( path, NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); |
可是在使用句柄继承之后,这个网络应用程序出现了问题:当父进程结束而子进程没有结束的时候,原本与父进程连接的应用程序并没有收到连接中断的消息,它仍然在等待父进程(已不存在)发送的数据;直到把子进程也结束,连接才会中断。
对于这个问题的初步设想是:子进程把父进程的套接字也继承了,因此即使父进程结束了,套接字仍然是存在的,当子进程也结束的时候,套接字才会被真正地销毁。微软知识库中有一篇文章(http://support.microsoft.com/kb/150523/en-us?fr=1)可以证明这个假设,文中说Windows NT内核的操作系统创建的套接字默认是可继承的,不像其它内核对象那样需要显式进行设置,所以当CreateProcess的bInheritHandles参数为TRUE的时候,父进程中的套接字都被子进程继承了。
如果对这个说法有所怀疑,可以实际证明一下。下面是证明过程,如果不想看可以跳到最后看解决方法。
首先写一个简单的测试程序,创建一个套接字,然后创建子进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <iostream> #include <string> #include <WinSock2.h> int wmain() { WSAData wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); STARTUPINFO si = { 0 }; si.cb = sizeof (si); PROCESS_INFORMATION pi; CreateProcess( L "C:\\Windows\\notepad.exe" , NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi ); CloseHandle(pi.hThread); CloseHandle(pi.hProcess); std::wstring line; while (std::getline(std::wcin, line)) { } WSACleanup(); } |
运行该程序会启动一个记事本程序。接下来使用ProcessExplorer查看父进程中的句柄:
上图中的Win32Console.exe即是父进程。可以看到,在父进程中有一个句柄指向一个类型为File的内核对象,其名称是\Device\Afd,这是套接字在内核中表示,同时也可以看到该内核对象的引用计数是2,说明共有两个句柄指向该对象。
然后再看看子进程的情况:
在子进程中同样有一个指向名为\Device\Afd的内核对象的句柄,记事本程序没有网络功能,所以正常情况下它根本不会有套接字的。留意它的引用计数也是2,还有这个对象的地址与父进程中的是一样的。要是把CreateProcess的bInheritHandles参数改为FALSE,子进程就不会出现这个句柄了。
防止套接字被子进程继承的方法非常简单,只要在创建套接字之后立即使用SetHandleInformation设置它的可继承性即可。像这样:
1 2 | SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SetHandleInformation(( HANDLE )sock, HANDLE_FLAG_INHERIT, 0); |
作者:Zplutor
出处:http://www.cnblogs.com/zplutor/
本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2011-03-20 [Win32]一个调试器的实现(五)调试符号