Как создать IPC через сокеты с клиентом Java и сервером Python? - PullRequest
0 голосов
/ 10 мая 2019

Я пытаюсь заставить два процесса взаимодействовать через локальные сокеты: Python сервер и Java клиент . Данные, которые я хочу передать между обоими, состоят из байтов объекта Protobuf с переменным размером. Я хочу, чтобы соединение с оставалось открытым и использовалось до конца программы, потому что я передаю много объектов, которые необходимо обработать.

Поскольку объекты Protobuf имеют переменный размер, я отправляю размер сообщения / ответа перед отправкой истинного сообщения / ответа, содержащего объект.

В настоящее время я использую TCPServer из библиотеки socketserver на стороне 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):
        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 работают хорошо. Однако, когда я запускаю case 2 на стороне сервера Python, я получаю следующий журнал:

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

А затем сервер выдает и исключение и выходит из соединения. Это происходит с каждой строкой с длиной 8 <длина <14. Что я делаю не так, и есть ли более простой способ добиться того, чего я хочу? </p>

1 Ответ

0 голосов
/ 11 мая 2019

Я выяснил, почему у меня проблемы с сообщениями размером 8 <длина <14. </p>

Я получал символ \t, когда длина была равна 9. Я заметил, что если я изменил длину на10, это станет \n.И до 13 \r.Я понял, что не было никакого волшебства.По какой-то причине 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) условие, которое я использовал по причинам отладки.

...