为SSL套接字上的每个请求创建新进程会产生“TypeError:无法序列化套接字对象”,但对正常/非SSL套接字执行相同操作

我正在尝试使用python服务器和java客户端中使用java keytool生成的密钥和证书。 我创建了密钥和密钥库,导出证书,将证书添加到信任库,将密钥库转换为标准pkcs格式,然后从pkcs中提取密钥和证书以在python服务器中使用。 (从这里开始前三步,从这里开始最后三步)。 这个问题在某种程度上跟进了这个问题 ,并且可以在该问题中找到生成密钥和证书的详细步骤。

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

server.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客户端如下所示:

Client4Py.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("<>"); out.println("Message from Client4Py"); out.println("<>"); 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  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 "", line 1, in  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() 

我不知道我是否理解其确切原因,但我在此陈述。

查看以下stacktrace行:

 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对象。 这个问题说明了基于Socket的服务器工作的原因。 但是,我不明白为什么会这样( SSLSocket不可序列化,但Socket对象可以序列化)。 我的意思是有没有任何方法由Socket对象实现,这些方法不是由SSLSocket实现的。 另请注意,错误发生在socket.py, line 185, in __getstate__ ,即在Socket类中,但不在SSLSocket类中。

我想如果有人确认上述原因(即SSLSocket对象不可序列化并且Socket对象可序列化),请准确解释为什么会出现这种情况并提供使用SSLSocketmultiprocessing模块的解决方案。

解决方法

我最终使用os.fork分配了新进程。 ( os.fork例子可以在这里找到)。 它仅支持linux。 所以,我在cygwin上安装了python然后使用它。