代码之家  ›  专栏  ›  技术社区  ›  Mahesha999

为sslsocket上的每个请求创建新进程会给出“TypeError:Cannot serializesocket object”,但对普通/非sslsocket执行同样的操作是可行的

  •  0
  • Mahesha999  · 技术社区  · 7 年前

    keytool 在python服务器和java客户端。我已经创建了;密钥库,导出证书,将证书添加到信任库,将密钥库转换为标准pkcs格式,然后从pkcs中提取密钥和证书以在python服务器中使用(开始的前三步 here 最后三步 here this question 有关生成密钥和证书的详细步骤,请参阅 that question .

    我的SSL服务器看起来像这样

    服务器.py

    import socket
    import multiprocessing as mup
    import ssl
    
    def worker_ssl(data_socket, client_address):
        print("Inside worker")
        #some processing
    
    def start_server_ssl():
    
        socketObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_address = ("127.0.0.1", 6000)
        socketObj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        socketObj.bind(server_address)
        socketObj.listen(10)
    
        ssl_socket = ssl.wrap_socket(socketObj,
                                server_side = True,
                                certfile="cert.pem",
                                keyfile="key.pem")        
    
        while True:
            print("Waiting For Connections .........\n")
            try:
                data_socket, client_address = ssl_socket.accept()
    
                process = mup.Process(target=worker_ssl, args=(data_socket, client_address))            
                process.daemon = True
                process.start()
    
            except socket.error as msg:
                print (" [ERROR] : %s " % msg)
                continue
    
        socketObj.close()
        ssl_socket.shutdown(socket.SHUT_RDWR)
        ssl_socket.close()
    
    if __name__ == '__main__':
        start_server_ssl()
    

    SSL客户端如下所示:

    客户端4py.java

    import java.io.BufferedWriter;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.nio.charset.StandardCharsets;
    import java.security.KeyStore;
    
    import javax.net.ssl.KeyManagerFactory;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSocket;
    import javax.net.ssl.SSLSocketFactory;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.TrustManagerFactory;
    
    public class Client4Py {
        static KeyStore ks;
        static KeyManagerFactory kmf;
        static TrustManagerFactory tmf;
        static SSLContext sc;
        static TrustManager[] trustManagers;
    
        static {
            try {
                ks = KeyStore.getInstance("JKS");
                ks.load(new FileInputStream("D:\\javasslstores\\truststore.jks"), "passwd123".toCharArray());
    
                kmf = KeyManagerFactory.getInstance("SunX509");
                kmf.init(ks, "passwd123".toCharArray());
    
                tmf = TrustManagerFactory.getInstance("SunX509"); 
                tmf.init(ks);
    
                sc = SSLContext.getInstance("TLS"); 
    
                sc.init(null, tmf.getTrustManagers(), null);
            } catch (Exception e) {
                System.out.println(e.getMessage());
                System.out.println(e.getStackTrace());
            }
        }
    
        public static void main(String[] args) throws IOException {
            SSLSocketFactory ssf = sc.getSocketFactory();
            SSLSocket socket = (SSLSocket) ssf.createSocket("127.0.0.1", 6000);
            socket.startHandshake();
    
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),StandardCharsets.UTF_8)));
            //PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
    
            out.println("<<startMessage>>");
            out.println("Message from Client4Py");
            out.println("<<endMessage>>");
            out.flush();
    
            if (out.checkError())
                System.out.println(
                    "SSLSocketClient:  java.io.PrintWriter error");
    
            out.close();
            socket.close();
        }
    }
    

    首先运行服务器,然后运行客户端之后,服务器控制台上的输出如下:

    1    Waiting For Connections .........
    2    
    3    Traceback (most recent call last):
    4      File "D:\workspaces\workspace6\PythonServer\server.py", line 40, in <module>
    5        start_server_ssl()
    6      File "D:\workspaces\workspace6\PythonServer\server.py", line 29, in start_server_ssl
    7        process.start()
    8      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\process.py", line 105, in start
    9        self._popen = self._Popen(self)
    10      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\context.py", line 223, in _Popen
    11        return _default_context.get_context().Process._Popen(process_obj)
    12      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\context.py", line 322, in _Popen
    13        return Popen(process_obj)
    14      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    15        reduction.dump(process_obj, to_child)
    16      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\reduction.py", line 60, in dump
    17        ForkingPickler(file, protocol).dump(obj)
    18      File "D:\Programs\python\python-3.6.6-amd64\lib\socket.py", line 185, in __getstate__
    19        raise TypeError("Cannot serialize socket object")
    20    TypeError: Cannot serialize socket object
    21    Traceback (most recent call last):
    22      File "<string>", line 1, in <module>
    23      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\spawn.py", line 99, in spawn_main
    24        new_handle = reduction.steal_handle(parent_pid, pipe_handle)
    25      File "D:\Programs\python\python-3.6.6-amd64\lib\multiprocessing\reduction.py", line 87, in steal_handle
    26        _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
    27    PermissionError: [WinError 5] Access is denied
    

    你可以在20号线上看到,有 TypeError: Cannot serialize socket object

    代码在删除所有SSL内容后开始工作

    当我给你打电话 wrap_socket() ,替换 ssl_socket.accept() socketObject.accept() 和评论 ssl_socket.shutdown() close() worker() Inside worker 在控制台上。以下是修改后的非SSL服务器:

    import socket
    import multiprocessing as mup
    # import ssl
    
    def worker_ssl(data_socket, client_address):
        print("Inside worker")
        #some processing
    
    def start_server_ssl():
    
        socketObj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_address = ("127.0.0.1", 6000)
        socketObj.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        socketObj.bind(server_address)
        socketObj.listen(10)
    
    #     ssl_socket = ssl.wrap_socket(socketObj,
    #                             server_side = True,
    #                             certfile="cert.pem",
    #                             keyfile="key.pem")        
    
        while True:
            print("Waiting For Connections .........\n")
            try:
                data_socket, client_address = socketObj.accept()
    
                process = mup.Process(target=worker_ssl, args=(data_socket, client_address))            
                process.daemon = True
                process.start()
    
            except socket.error as msg:
                print (" [ERROR] : %s " % msg)
                continue
    
        socketObj.close()
    #     ssl_socket.shutdown(socket.SHUT_RDWR)
    #     ssl_socket.close()
    
    if __name__ == '__main__':
        start_server_ssl()
    
    1 回复  |  直到 7 年前
        1
  •  1
  •   Vic Levy    5 年前

    我通过在子进程中用SSLContext将套接字包装起来来解决这个问题。创建SSLContext多处理所需的一切都没有序列化问题。

    更新:下面是一个服务器sudo代码,说明了上述说明:

    def client_connection_handler(client_socket, client_address, ssl_settings):
        context = ssl.SSLContext()
        context.load_cert_chain(certfile=ssl_settings['certfile'], keyfile=ssl_settings['keyfile'],)
        context.verify_mode = ssl_settings["verify_mode"]
        context.load_verify_locations(cafile=ssl_settings["cafile"])
        client_secured_socket = context.wrap_socket(client_socket, server_side=True)
        #
        # send and receive data
        #
        client_secured_socket.close()
    
    
    def server():
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.bind((hostname, port))
        server_socket.listen(5)
        while True:
            # Waiting for client connection
            client_socket, client_address = server_socket.accept()
            concurrent_connections = len(multiprocessing.active_children())
            if max_concurrent_clients > concurrent_connections:
                p = multiprocessing.Process(target=client_connection_handler, args=(client_socket, client_address, ssl_settings))
                p.daemon = True
                p.start()
                continue
            # Max client connection has been reached
            client_socket.close()
    
        2
  •  0
  •   Mahesha999    7 年前

    17        ForkingPickler(file, protocol).dump(obj)
    18      File "D:\Programs\python\python-3.6.6-amd64\lib\socket.py", line 185, in __getstate__
    19        raise TypeError("Cannot serialize socket object")
    20    TypeError: Cannot serialize socket object
    

    看来 multiprocessing Process.start() 方法序列化传递给它的参数,以便将它们传递给新进程。进一步看来 SSLSocket 无法序列化对象。然而,似乎 Socket This issue 插座 SSL插座 不可序列化,但 插座 插座 SSL插座 . 还要注意,错误发生在 socket.py, line 185, in __getstate__ ,在 插座 SSL插座 班级。

    SSL插座 对象不可序列化并且 插座 对象是可序列化的),解释了为什么会出现这种情况,并提供了使用的解决方案 SSL插座 模块。

    变通办法

    最后我用 os.fork . (示例 叉子 可以找到 here ). 它仅在linux上受支持。所以,我 installed python on cygwin 然后用它。

    推荐文章