Байтовый буфер получает повреждение в ответе TCP

У меня есть Tcp Server

public class SomeServer implements Runnable{


    private static final int BUFFER_SIZE = 4096;
    private Selector selector;
    private int port;
    private boolean runserver = true;
    private ServerSocketChannel mySocketChannel;
    private ServerSocket serverSocket;

    public SomeServer(int port) {
        this.port = port;
    }


    @Override
    public void run() {
        startServer();
    }

    private void startServer() {
        try {
            selector = Selector.open();
            InetAddress hostIP = InetAddress.getByName("127.0.0.1");
            mySocketChannel = ServerSocketChannel.open();
            serverSocket = mySocketChannel.socket();
            InetSocketAddress address = new InetSocketAddress(hostIP, port);
            log.info("Setting up SomeServer with on {} with port {} ", hostIP, port);
            serverSocket.bind(address);
            mySocketChannel.configureBlocking(false);
            int ops = mySocketChannel.validOps();
            mySocketChannel.register(selector, ops, null);
            while (runserver) {
                log.info("Server running");
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> i = selectedKeys.iterator();

                while (i.hasNext()) {
                    SelectionKey key = i.next();

                    if (key.isAcceptable()) {
                        processAcceptEvent(mySocketChannel, key);
                    } else if (key.isReadable()) {
                        processReadEvent(key);
                    }
                    i.remove();
                }
            }

        } catch (Exception e) {
            log.error("Something gone wrong in SomeServer {}", e);
        }
    }

    private void processAcceptEvent(ServerSocketChannel mySocket, SelectionKey key) throws IOException {
        log.info("Connection Accepted...");
        // Accept the connection and make it non-blocking
        SocketChannel myClient = mySocket.accept();
        myClient.configureBlocking(false);
        // Register interest in reading this channel
        myClient.register(selector, SelectionKey.OP_READ);
    }

    private void processReadEvent(SelectionKey key) throws Exception {
        log.info("Inside processReadEvent...");
        // create a ServerSocketChannel to read the request
        SocketChannel myClient = (SocketChannel) key.channel();

        ByteBuffer myBuffer = ByteBuffer.allocate(BUFFER_SIZE);

        myBuffer.order(ByteOrder.BIG_ENDIAN);
        int radBytes= myClient.read(myBuffer);
        if (radBytes ==-1)
            return; //means connection is closed

        ByteBuffer responseBuffer = clone(execute(myBuffer));

        if (responseBuffer ==null){
            return;
        }
        int byteswritten = myClient.write(responseBuffer);
        log.info("bytesWritten...: {}", byteswritten);
    }

public ByteBuffer execute(ByteBuffer requestBuffer) throws Exception {
    final CharsetEncoder myEncoder = Charset.forName("ISO-8859-1").newEncoder();
    final byte END_OF_MESSAGE_BYTE = (byte) 0x2b;
    CharsetDecoder myDecoder = Charset.forName("ISO-8859-1").newDecoder();

    //Process Request
    final ByteBuffer processBuffer = clone(requestBuffer);
    // processBuffer.flip();
    int bufferSize = processBuffer.getInt();
    String request = myDecoder.decode(processBuffer).toString().trim();
    int requestLength=request.length();
    if (requestLength<8){
        log.info("Connection closed ");
        return null;
    }
    String firstName = request.substring(0,8);
    int requestBodyLength= requestLength-firstName.length();
    String command = request.substring(8,requestLength-1);

    processBuffer.flip();
    //Build-Response
    processBuffer.clear();
    String response = "some response";
    int responselength = response.length()+firstName.length()+4;
    processBuffer.putInt(responselength);
    processBuffer.put(myEncoder.encode(CharBuffer.wrap(firstName)));
    processBuffer.put(myEncoder.encode(CharBuffer.wrap(response)));
    processBuffer.put(END_OF_MESSAGE_BYTE);
    processBuffer.flip();

    return processBuffer;
}

public static ByteBuffer clone(ByteBuffer original) {
    if (original == null)
        return null;
    ByteBuffer clone = ByteBuffer.allocate(original.capacity());
    original.rewind();//copy from the beginning
    clone.put(original);
    original.rewind();
    clone.flip();
    return clone;
}


}

Отправка какого-то сообщения и кроме ответа на него

Клиентская сторона: чтение ответа

     public class Mycleint {

 priavte SocketCahnnel mySocket;
 priavte String name;
 final byte END_OF_MESSAGE_BYTE = (byte) 0x2b;

public Mycleint (String name , InetSocketAddress inetAddress){
        this.name=name;
        mySocket = SocketChannel.open();
        mySocket.connect(inetAddress);

}

 private String sendRequest(String request) throws IOException {
            final CharsetEncoder myEncoder = Charset.forName("ISO-8859-1").newEncoder();
            int requestLength = 12 + request.length() + 1;
            ByteBuffer buffer = ByteBuffer.allocate(requestLength);
            buffer.order(ByteOrder.BIG_ENDIAN);
            buffer.putInt(requestLength);
            buffer.put(myEncoder.encode(CharBuffer.wrap(name)));
            buffer.put(myEncoder.encode(CharBuffer.wrap(request)));
            buffer.put();
            buffer.flip();
            mysocket.write(buffer);
            return readResponse();
    }

     private String readResponse(SocketChannel mySocket) throws IOException {

    CharsetDecoder myDecoder = Charset.forName("ISO-8859-1").newDecoder();
    ByteBuffer responseHeaderBuf = ByteBuffer.allocate(12);
    responseHeaderBuf.order(ByteOrder.BIG_ENDIAN);
    int bytesRead = 0;
    do {
        bytesRead = mySocket.read(responseHeaderBuf);
    } while (bytesRead != -1 && responseHeaderBuf.position() < 12);

    if (bytesRead == -1) {
        throw new IOException(" : Remote connection closed unexpectedly");
    }

        responseHeaderBuf.flip();
        int lengthField = responseHeaderBuf.getInt();
        int responseLength = lengthField - responseHeaderLength;
        String responseName = myDecoder.decode(responseHeaderBuf).toString();



        ByteBuffer responseBuf = ByteBuffer.allocate(responseLength);
        do {
            bytesRead = mySocket.read(responseBuf);
        } while (bytesRead != -1 && responseBuf.position() < responseLength);

        if (bytesRead == -1) {
            throw new IOException(name + " : Remote connection closed unexpectedly");
        }
        responseBuf.flip();


        if (responseBuf.get(responseBuf.limit() - 1) == END_OF_MESSAGE_BYTE) {
            responseBuf.limit(responseBuf.limit() - 1);
        }
        String response = myDecoder.decode(responseBuf).toString();
        return response;

    }

}

Я оставляю SocketChannel открытым на неопределенное время на стороне клиента.

Когда я декодирую ответ в клиенте responseHeaderBuf.getInt(), он работает правильно в первый раз. но во второй раз данные будут повреждены при декодировании.

Также, если для отправки запроса используется сокет, все работает, как ожидалось, но я хочу повторно использовать созданный мной сокет.

Например, если сервер отправил 28 processBuffer.putInt(28);, клиент говорит, что получил responseHeaderBuf.getInt() -> 721420288,

Может ли кто-нибудь помочь и сказать мне, что я делаю не так.

721420288 = 0x2b000000, что является прямым порядком байтов от 28. Вы не показали код отправки вашего клиента, но я подозреваю, что ваш клиент вызывает order(ByteOrder.LITTLE_ENDIAN) в своем буфере отправки.

VGR 27.08.2018 00:34

@VGR Я добавил полный клиентский код

edwin 27.08.2018 11:04

@VGR вы можете видеть buffer.order(ByteOrder.BIG_ENDIAN); в клиенте во время чтения и записи. также сервер на сервере, у меня были сомнения по поводу метода clone в коде сервера, который я удалил, я пробовал, все еще имеет ту же проблему

edwin 27.08.2018 12:38

Из Документация ByteBuffer: «Первоначальный порядок байтового буфера всегда BIG_ENDIAN». Лучше просто удалить все вызовы метода order.

VGR 27.08.2018 15:38

@VGR, пробовал ваше предложение с order , который не работает. Интересно, что если я открываю новый сокет при каждом вызове sendRequest, все работает, как ожидалось.

edwin 27.08.2018 16:02

@VGR наконец-то обнаружил, что метод clone виновника в изменении порядка буфера на сервере. Я использую тот же буфер для чтения и записи, который работает. Спасибо за ваш совет.

edwin 30.08.2018 00:22

Метод clone(ByteBuffer), показанный в вашем вопросе, не меняет порядок байтов.

VGR 30.08.2018 00:24
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
7
60
0

Другие вопросы по теме