为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
对象可序列化),请准确解释为什么会出现这种情况并提供使用SSLSocket
和multiprocessing
模块的解决方案。
解决方法
我最终使用os.fork
分配了新进程。 ( os.fork
例子可以在这里找到)。 它仅支持linux。 所以,我在cygwin上安装了python然后使用它。