繁体   English   中英

从 Python 2 迁移到 3 时的文件描述符/套接字处理差异

[英]File descriptor / socket handling differences when migrating from Python 2 to 3

我正在将一个应用程序从 Python 2 迁移到 3。该应用程序涉及一个 Python 脚本,该脚本编排了一个 C 应用程序的几个实例。 python 脚本为每个应用程序打开一个套接字,然后将相应的文件描述符传递给 C 程序。 这适用于 Python 2.7 中的原始版本,但与 Python 3.6 或 3.9 不同。

我能够找到一个变化:默认情况下,除了stdinstdoutstderr之外的文件描述符不被子进程继承(更多信息在这里

我要做的是:

import socket                                
import os                                    
import subprocess                            

sock = socket.socket()                       
sock.bind(('10.80.100.32',0))                
sock                                         
# Out[6]: <socket.socket fd=11, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('10.80.100.32', 36737)>

env  = os.environ.copy()                     
env["LD_LIBRARY_PATH"] = env["LD_LIBRARY_PATH"] + ":%s" % os.getcwd()                             
p = subprocess.Popen(["./app", "--sockfd", "11"], close_fds = False, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)                                               
p.pid                                       
# Out[10]: 393727

然后我检查相应的进程:在 Python 2 的情况下它存在并且有一个服务器在等待连接,或者在 Python 3 的情况下该进程已死。

我试图将文件描述符设置为可继承:

os.get_inheritable(11)                      
# Out[15]: False
os.set_inheritable(11, True)

然而这并没有帮助,应用程序仍然崩溃。

我还尝试将pass_fds = [11]显式传递给Popen ,但这也没有帮助。

如果我运行应用程序并让它自己创建套接字,那么它可以正常工作,包括从 Python 脚本启动时。 所以在这一点上,我相当肯定这个问题与从 Python 2 到 Python 3 的一些变化有关。

是否有任何其他变化可能对观察到的行为产生影响? 我还能尝试什么让它发挥作用?

这里的问题似乎是你从来没有在你的套接字上调用listen() 如果我将您的代码修改为(a)设置inheritable标志和(b)调用listen ,它看起来像这样:

import socket
import os
import subprocess

sock = socket.socket()
sock.set_inheritable(True)
sock.bind(("0.0.0.0", 0))
sock.listen(5)
print("listening on", sock.getsockname()[1])

env = os.environ.copy()
p = subprocess.Popen(
    ["./socklisten", "{}".format(sock.fileno())],
    close_fds=False,
    env=env,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
)
p.wait()

其中socklisten是以下简单程序,它在给定文件描述符上打印出一个字符串:

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>

int main(int argc, char **argv) {
    int sock;

    if (argc <= 1) {
        fprintf(stderr, "missing socket\n");
        exit(1);
    }

    sock = atoi(argv[1]);
    if (sock == 0) {
        fprintf(stderr, "invalid socket number\n");
        exit(1);
    }

    while (1) {
        int client;
        char msg[100];
        struct sockaddr_in clientaddr;
        socklen_t clientlen = sizeof(struct sockaddr_in);
        if (-1 == (client = accept(sock, (struct sockaddr *)&clientaddr, &clientlen))) {
            perror("accept");
            exit(1);
        }

        sprintf(msg, "This is a test.\r\n");
        write(client, msg, strlen(msg)+1);
        close(client);
    }

}

这一切都按预期工作。 如果我运行 Python 代码,它会打开套接字并等待子进程退出:

$ python server.py
listening on 51163

如果我连接到该端口,我会看到预期的响应:

$ nc localhost 51163
This is a test.

如果我删除对sock.set_inheritable的调用或对sock.listen的调用,则代码将按照您在问题中的描述失败。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM