Передача сокетов между процессами
Авторы: Warren Young и Frank Schmied
Для того, чтобы передать сокет от одного
процесса другому, можно воспользоваться
функцией WSADuplicateSocket() из Winsock 2. Изначально в
часто задаваемых вопросах (FAQ) эту проблему
решали следующим способом:
Спецификация данного метода подробно
описывается в секции 2.10 MSDN-а, где подробно по
шагам комментируется данная функция. Так же
можно почитать статью Q150523 в
Microsoft Knowledge Base, в которой описываются различия
наследования сокета в разных версиях Windows.
Другая забавная особенность Win32 API заключается
в том, что он позволяет присваивать новому
процессу (во время его создания) различные "стандартные
обработчики" ("standard handles") (stdin, stdout и stderr).
Статья Q190351
в MSKB описывает данный момент. Обратите внимание,
что данная возможность доступна только для
дочерних процессов; т.е. Вы не сможете
перенаправить стандартный обработчик I/O на сокет.
Естевственно, что данная возможность не
предоставляет нам таких преимуществ, как,
например, Unix-функция dup2() .
Так же в FAQ сказано, что мы не можем использовать
такую возможность в Winsock 1.1. Однако Frank Schmied
показал, что можно слегка обмануть стек Microsoft Winsock
1.1 в Win32, и в конечном результате добиться своей
цели. Вот его комментарии:
Можно заставить Winsock 1.1 передавать сокет от
одного процесса другому используя Win32 функцию DuplicateHandle() .
Обработка данного вызова может быть весьма
сложной. Фактически, генерация реальных
дескрипторов процесса не так проста, как может
показаться на первый взгляд. Windows использует два
типа дескрипторов окна: псевдо-дескрипторы и
реальные дескрипторы. Обычно дескрипторы в Windows -
это адреса в памяти, и экземпляр дескриптора
является ни чем иным как смещением указателя на
код, расположенный в текущем адресном
пространстве. Итак, дескриптор процесса HINSTANCE (псевдо
или локальный) обычно равен 0x4000000. Передача
данного дескриптора от одного процесса к другому
не работает. Чтобы получить реальный дескриптор
текущего процесса, можно использовать OpenProcess() :
OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
Создание дубликата дескриптора выглядит
примерно так:
SOCKET ConvertProcessSocket(SOCKET oldsocket, DWORD source_pid)
{
HANDLE source_handle = OpenProcess(PROCESS_ALL_ACCESS,
FALSE, source_pid);
HANDLE newhandle;
DuplicateHandle(source_handle, (HANDLE)oldsocket,
GetCurrentProcess(), &newhandle, 0, FALSE,
DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
CloseHandle(source_handle);
return (SOCKET)newhandle;
}
Данный пример отлично работает на
многопроцессорном вебсервере. Данная функция
передаёт сокет в новый процесс и закрывает
дескриптор старого процесса. Дескриптор имеет те
же свойства что и старый, но не может быть
унаследован дочерним процессом. Чтобы исправить
это, достаточно в DuplicateHandle() изменить FALSE на
TRUE. Как мы видим, дескриптор основного процесса
может быть псевдо-дескриптором, но дескриптор
второго процесса обязан быть реальным.
Алгоритм таков: исходный процесс конвертирует
локальный дескриптор SOCKET в реальный дескриптор
при помощи OpenProcess() , затем передаёт это
значение и ID процесса другому процессу. Второй
процесс вызывает функцию ConvertProcessSocket() ,
чтобы преобразовать реальный дескриптор в
локальный, который уже можно будет использовать
в Winsock. Обратите внимание, что вызов DuplicateHandle()
закрывает дескриптор первого процесса, а затем
функция CloseHandle() закрывает реальный
дескриптор, который вы передаёте второму
процессу.
Недостатки: данная методика скорее всего
работает только со стеком Microsoft. Однозначно не
будет работать в Win16, и, возможно в WinCE. Не извесно,
будет или нет работать в присутствие Layered Service
Providers, исключая Windows NT 4.0 SP 4+, в которой пропатчен
уровень Installable FileSystems (IFS). Возможно найдутся ещё
причины, по которым данный метод может не
сработать :)
|