如何使用 Java 客户端和 Python 服务器通过套接字创建 IPC?

Posted

技术标签:

【中文标题】如何使用 Java 客户端和 Python 服务器通过套接字创建 IPC?【英文标题】:How to create IPC through sockets with a Java client and a Python server? 【发布时间】:2019-05-10 17:10:11 【问题描述】:

我试图让两个进程通过本地套接字进行通信:一个 Python 服务器 和一个 Java 客户端。我想在两者之间传递的数据由 Protobuf 对象的 bytes 组成,大小可变。我希望连接保持打开并一直使用到程序结束,因为我传递了很多需要处理的对象。

由于 Protobuf 对象的大小可变,我在发送包含该对象的真实消息/响应之前发送消息/响应的大小。

目前,我在 Python 端使用来自 socketserver 库的 TCPServer。我实现了以下处理程序:

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def recv_all(self, n):
        # Helper function to recv n bytes or return None if EOF is hit
        data = b''
        while len(data) < n:
            packet = self.request.recv(n - len(data))
            if not packet:
                return None
            data += packet
        return data

    def handle(self):
        logger.debug("Beginning of handle cycle for client: .".format(self.client_address))

        while True:
            if True: # please disregard this if condition
                # Receive 4 bytes (1 int) denoting the size of the message
                data_length_bytes: bytes = self.recv_all(4)
                logger.debug('Received data_length: '.format(data_length_bytes))

                # If recv read an empty request b'', then client has closed the connection
                if not data_length_bytes:
                    break

                data_length: int = int.from_bytes(data_length_bytes.strip(), byteorder='big')
                data: bytes = self.recv_all(data_length).strip()

                response: bytes = data.upper()

                # Send length of response first
                self.request.sendall(len(response).to_bytes(4, byteorder='big'))
                # Send response
                self.request.sendall(response)

                logger.debug(
                    'Sent response to: . Size of response:  bytes. Response: .'.format(self.client_address,
                                                                                             len(response),
                                                                                             response))


        logger.debug("End of handle cycle for client: .".format(self.client_address))

还有以下客户:

class SocketClient

    private static Socket socket;
    private int port;
    private DataOutputStream out;
    private DataInputStream in;

    SocketClient(int port)
    
        this.port = port;
        this.createSocket();
    

    private void createSocket() 
        InetAddress address;
        try 
            address = InetAddress.getByName("localhost");
            socket = new Socket(address, port);
            this.out = new DataOutputStream(socket.getOutputStream());
            this.in = new DataInputStream(socket.getInputStream());

         catch (IOException e) 
            e.printStackTrace();
        
    

    byte[] sendMessageAndReceiveResponse(byte[] messageToSend)
        try 
            if(true)   // again, please disregard this condition
                //Send the size of the message to the server
                this.out.writeInt(messageToSend.length);
                out.flush();

                this.out.write(messageToSend);
                out.flush();

                //Get the response message from the server
                int length = in.readInt();                    // read length of incoming message

                byte[] buffer = null;
                if(length>=0) 
                    buffer = new byte[length];
                    in.readFully(buffer, 0, buffer.length); // read the message
                

                return buffer;
            
        
        catch (ConnectException exception) 
            System.out.println("ATTENTION! Could not connect to socket. Nothing was retrieved from the Python module.");
            exception.printStackTrace();
            return null;
        
        catch (Exception exception)
        
            exception.printStackTrace();
            return null;
        
    

    void close()
        //Closing the socket
        try
        
            in.close();
            out.close();
            socket.close();
        
        catch(Exception e)
        
            e.printStackTrace();
        
    

我在启动 Python 服务器后运行以下实验:

        SocketClient socketClient = new SocketClient(5000);

        byte[] response;

        // Case 1
        response = socketClient.sendMessageAndReceiveResponse("12345678".getBytes());
        System.out.println(new String(response));

        // Case 2
        response = socketClient.sendMessageAndReceiveResponse("123456781".getBytes());
        System.out.println(new String(response));

        // Case 3
        response = socketClient.sendMessageAndReceiveResponse("12345678123456781".getBytes());
        System.out.println(new String(response));


        socketClient.close();

案例 1 和案例 3 运行良好。但是,当我在 Python 服务器端运行 case 2 时,会得到以下日志:

DEBUG -- [handle()] Received data_length: b'\x00\x00\x00\t' # The '\t' shouldn't be here. A '\x09' should.

然后服务器抛出异常并退出连接。 8

【问题讨论】:

【参考方案1】:

我知道为什么我在处理 8

当长度等于 9 时,我得到了 \t 字符。我注意到如果我将长度更改为 10,它将变为 \n。到 13 日,\r。我意识到没有任何\t 神奇地出现。 Python 出于某种原因将\x09 转换为\t,因为水平制表符\t 的ASCII 码等于9!

当我在这一行应用strip() 函数时:

data_length: int = int.from_bytes(data_length_bytes.strip(), byteorder='big')

,Python 删除了我的\t,实际上是我的\x09。我的问题是在 剥离它之前记录值,所以我花了很长时间才弄清楚我的错误。

因此,解决方案就是不使用strip()。我把我当前的工作代码(至少对于我的测试)留在这里,供某人使用:

Python 服务器处理程序:

class MyTCPHandler(socketserver.BaseRequestHandler):
    """
    The request handler class for our server.

    It is instantiated once per connection to the server, and must
    override the handle() method to implement communication to the
    client.
    """

    def recv_all(self, n):
        # Helper function to recv n bytes or return None if EOF is hit
        data = b''
        while len(data) < n:
            packet = self.request.recv(n - len(data))
            if not packet:
                return None
            data += packet
        return data

    def handle(self):
        while True:
            data_length_bytes: bytes = self.recv_all(4)

            # If recv read an empty request b'', then client has closed the connection
            if not data_length_bytes:
                break

            # DON'T DO strip() ON THE DATA_LENGTH PACKET. It might delete what Python thinks is whitespace but
            # it actually is a byte that makes part of the integer.
            data_length: int = int.from_bytes(data_length_bytes, byteorder='big')

            # Don't do strip() on data either (be sure to check if there is some error if you do use)
            data: bytes = self.recv_all(data_length)

            response: bytes = data.upper()

            self.request.sendall(len(response).to_bytes(4, byteorder='big'))
            self.request.sendall(response)

Java 客户端保持不变,但没有我出于调试原因使用的 if(true) 条件。

【讨论】:

以上是关于如何使用 Java 客户端和 Python 服务器通过套接字创建 IPC?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 jpeg 帧从 java 客户端流式传输到 python 服务器

基于thrift的java和python分别作为客户端和服务端的调用实现

套接字阻止在 Java 服务器和 Python 客户端之间发送消息

如何在网页中调用python脚本?

python thrift 服务端与客户端使用

python客户端无法连接到java服务器