ByteBuffer - 编码字符串和 put 与 CharSet 编码之间的区别?
ByteBuffer - difference between encoding string and put vs CharSet encode?
我有两种不同的方法从字符串创建 ByteBuffer
对象:
- 从字符串中获取
byte[]
并使用 ByteBuffer.put(byte[])
方法:
private ByteBuffer respWithPut() {
ByteBuffer respBuf = ByteBuffer.allocate(1024);
respBuf.put(httpResponse().getBytes(StandardCharsets.US_ASCII));
return respBuf;
}
- 使用
Charset.encode(String)
方法:
private ByteBuffer respFromChar() {
return StandardCharsets.US_ASCII.encode(httpResponse());
}
我正在尝试使用它发送一个简单的 HTTP 响应(问题末尾的完整代码)。使用 respWithPut()
我在客户端收到损坏的响应,而 respFromChar()
工作正常。
我在 respWithPut()
中做错了什么?
完整示例代码:
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();
}
}
要提出样品请求,请使用:curl -v "http://localhost:8080/"
。
一个 ByteBuffer 有一个 position indicating where the next byte should be read from. Your respWithPut method needs to call respBuf.flip() 来确保缓冲区的位置指向你刚放入其中的数据。
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
调用 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
下一个 ByteBuffer.get
调用将读取索引 8 处的字节,该字节仍为零,因为您还没有使用 put
在此处添加任何数据。
调用ByteBuffer.flip
后,limit将是旧位置,新位置将为零,使任何现有数据都可以读取:
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
我有两种不同的方法从字符串创建 ByteBuffer
对象:
- 从字符串中获取
byte[]
并使用ByteBuffer.put(byte[])
方法:
private ByteBuffer respWithPut() {
ByteBuffer respBuf = ByteBuffer.allocate(1024);
respBuf.put(httpResponse().getBytes(StandardCharsets.US_ASCII));
return respBuf;
}
- 使用
Charset.encode(String)
方法:
private ByteBuffer respFromChar() {
return StandardCharsets.US_ASCII.encode(httpResponse());
}
我正在尝试使用它发送一个简单的 HTTP 响应(问题末尾的完整代码)。使用 respWithPut()
我在客户端收到损坏的响应,而 respFromChar()
工作正常。
我在 respWithPut()
中做错了什么?
完整示例代码:
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();
}
}
要提出样品请求,请使用:curl -v "http://localhost:8080/"
。
一个 ByteBuffer 有一个 position indicating where the next byte should be read from. Your respWithPut method needs to call respBuf.flip() 来确保缓冲区的位置指向你刚放入其中的数据。
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
调用 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
下一个 ByteBuffer.get
调用将读取索引 8 处的字节,该字节仍为零,因为您还没有使用 put
在此处添加任何数据。
调用ByteBuffer.flip
后,limit将是旧位置,新位置将为零,使任何现有数据都可以读取:
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