简体   繁体   English

ByteBuffer - 编码字符串和 put 与 CharSet 编码之间的区别?

[英]ByteBuffer - difference between encoding string and put vs CharSet encode?

I have two different ways to create ByteBuffer object from String:我有两种不同的方法可以从 String 创建ByteBuffer对象:

  1. Get byte[] from String an use ByteBuffer.put(byte[]) method:从 String 中获取byte[]并使用ByteBuffer.put(byte[])方法:
private ByteBuffer respWithPut() {
    ByteBuffer respBuf = ByteBuffer.allocate(1024);
    respBuf.put(httpResponse().getBytes(StandardCharsets.US_ASCII));
    return respBuf;
}
  1. Use Charset.encode(String) method:使用Charset.encode(String)方法:
private ByteBuffer respFromChar() {
    return StandardCharsets.US_ASCII.encode(httpResponse());
}

I am trying to send a simple HTTP response using this (complete code at the end of the question).我正在尝试使用它发送一个简单的 HTTP 响应(问题末尾的完整代码)。 With respWithPut() I get corrupted response on client, while respFromChar() works fine.使用respWithPut()我在客户端得到损坏的响应,而respFromChar()工作正常。

What am I doing wrong in respWithPut() ?我在respWithPut()做错了什么?

Complete Sample Code:完整的示例代码:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Arrays;
import java.util.concurrent.Future;

public class AsyncServer {
    final String HTTP_DELIM = "\r\n";

    private String httpResponse() {
        String body = "HELLO";
        String prologue = "HTTP/1.1 200 OK";
        String header = String.join(HTTP_DELIM,
                Arrays.asList(
                        "Date: " + Instant.now().toString(),
                        "Content-Type: text/plain",
                        String.format("Content-Length: %d", body.length()),
                        HTTP_DELIM
                )
        );
        return prologue + HTTP_DELIM + header +body;
    }

    private ByteBuffer respWithPut() {
        ByteBuffer respBuf = ByteBuffer.allocate(1024);
        respBuf.put(httpResponse().getBytes(StandardCharsets.US_ASCII));
        return respBuf;
    }

    private ByteBuffer respFromChar() {
        return StandardCharsets.US_ASCII.encode(httpResponse());
    }

    public void startHttpServer() throws Exception {
        AsynchronousServerSocketChannel listener
                = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));
        while (true) {
            Future<AsynchronousSocketChannel> asyncCh = listener.accept();
            AsynchronousSocketChannel aSock = asyncCh.get();
            aSock.write(respWithPut());
            aSock.close();
        }
    }

    public static void main(String[] args) throws Exception {
        AsyncServer asyncServer = new AsyncServer();
        asyncServer.startHttpServer();
    }
}

To make a sample request, use: curl -v "http://localhost:8080/" .要发出示例请求,请使用: curl -v "http://localhost:8080/"

A ByteBuffer has a position indicating where the next byte should be read from. ByteBuffer 有一个位置,指示应该从哪里读取下一个字节。 Your respWithPut method needs to call respBuf.flip() to make sure the buffer's position is pointing to the data you just put in it.您的 respWithPut 方法需要调用respBuf.flip()以确保缓冲区的位置指向您刚刚放入的数据。

After ByteBuffer.allocate :ByteBuffer.allocate之后:

position                              limit
↓                                     ↓
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| … |_|
0 1 2 3 4 5 5 6 7 8 9 1 1 1 1 1 1     ↑
                      0 1 2 3 4 5     buffer size

After calling ByteBuffer.put with, for example, a byte array of length eight:例如,在使用长度为 8 的字节数组调用ByteBuffer.put之后:

                  position            limit
                  ↓                   ↓
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| … |_|
0 1 2 3 4 5 5 6 7 8 9 1 1 1 1 1 1     ↑
                      0 1 2 3 4 5     buffer size

The next ByteBuffer.get call will read the byte at index 8, which is still zero since you haven't used put to add any data there.下一个ByteBuffer.get调用将读取索引 8 处的字节,该字节仍然为零,因为您还没有使用put在那里添加任何数据。

After calling ByteBuffer.flip , the limit will be the old position, and the new position will be zero, making any existing data ready for reading:调用ByteBuffer.flip ,限制将是旧位置,新位置将为零,使任何现有数据准备好读取:

next get operation will read from here
↓
position          limit
↓                 ↓
|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_|_| … |_|
0 1 2 3 4 5 5 6 7 8 9 1 1 1 1 1 1     ↑
                      0 1 2 3 4 5     buffer size

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM