随笔 - 37  文章 - 0  评论 - 150  阅读 - 29万

[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查看父进程中的句柄:

clip_image002

 

上图中的Win32Console.exe即是父进程。可以看到,在父进程中有一个句柄指向一个类型为File的内核对象,其名称是\Device\Afd,这是套接字在内核中表示,同时也可以看到该内核对象的引用计数是2,说明共有两个句柄指向该对象。

 

然后再看看子进程的情况:

clip_image004

 

在子进程中同样有一个指向名为\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);
posted on   Zplutor  阅读(2935)  评论(2编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
历史上的今天:
2011-03-20 [Win32]一个调试器的实现(五)调试符号
< 2012年3月 >
26 27 28 29 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
1 2 3 4 5 6 7

点击右上角即可分享
微信分享提示