zoukankan      html  css  js  c++  java
  • [Win32]防止套接字被继承

    有一个网络应用程序,需要创建子进程,同时要将一个内核对象的句柄传递给子进程使用。句柄默认是不可继承的,为了达到这个目的,要在创建内核对象的时候指定其句柄是可继承的,然后在调用CreateProcess的时候将bInheritHandles参数设置为TRUE,像下面那样(以创建Mutex为例):

    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的时候,父进程中的套接字都被子进程继承了。

     

    如果对这个说法有所怀疑,可以实际证明一下。下面是证明过程,如果不想看可以跳到最后看解决方法。

     

    首先写一个简单的测试程序,创建一个套接字,然后创建子进程:

    #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设置它的可继承性即可。像这样:

    SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    SetHandleInformation((HANDLE)sock, HANDLE_FLAG_INHERIT, 0);


    作者:Zplutor
    出处:http://www.cnblogs.com/zplutor/
    本文版权归作者和博客园共有,欢迎转载。但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    NW开发教程系列六:表头和表体(多表体)
    Java 正则表达式 量词 --- 三种匹配模式【贪婪型、勉强型、占有型】
    信号量与PV操作
    java的(PO,VO,TO,BO,DAO,POJO)解释
    使用Application对象简单完成网站总访问人数的统计
    更新记录后关闭子窗口并刷新父窗口的Javascript
    jquery jqPlot API 中文使用教程
    一款基于bootstrap的datetimepicker
    2013杭州赛区Ants hdu4776
    openGL中的函数调用类成员函数
  • 原文地址:https://www.cnblogs.com/zplutor/p/2408418.html
Copyright © 2011-2022 走看看