目录
创建HttpRequest类
package com.lhstack.bio.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.nio.charset.Charset;
import java.util.Map;
/**
* @author lhstack
*/
public class HttpRequest {
private String version;
private String uri;
private String method;
private final Map<String,String> headers;
private ByteBuf content;
public HttpRequest(String version, String uri, String method, Map<String, String> headers) {
this.version = version;
this.uri = uri;
this.method = method;
this.headers = headers;
this.content = Unpooled.EMPTY_BUFFER;
}
public String version() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String uri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
public String method() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Map<String, String> headers() {
return headers;
}
public ByteBuf getContent() {
return content;
}
public void setContent(ByteBuf content) {
this.content = content;
}
/**
* clone一份
* @return
*/
public HttpRequest copy(){
HttpRequest httpRequest = new HttpRequest(this.version, this.uri, this.method, this.headers);
httpRequest.setContent(this.content.copy());
return httpRequest;
}
@Override
public String toString() {
return "HttpRequest{" +
"version='" + version + '\'' +
", uri='" + uri + '\'' +
", method='" + method + '\'' +
", headers=" + headers +
", content=" + this.content.toString(Charset.defaultCharset()) +
'}';
}
}
创建HttpResponse类
package com.lhstack.bio.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* @author lhstack
*/
public class HttpResponse {
private String version;
private int status;
private ByteBuf content;
private Map<String,String> headers;
public HttpResponse(String version, int status) {
this(version,status,Unpooled.EMPTY_BUFFER);
}
public HttpResponse(String version, int status, ByteBuf content) {
this(version,status,content,new HashMap<>());
}
public HttpResponse(String version, int status, ByteBuf content, Map<String, String> headers) {
this.version = version;
this.status = status;
this.content = content;
this.headers = headers;
this.init();
}
private void init() {
this.headers.put("Server","lhstack");
if(this.content.readableBytes() > 0){
this.headers.put("Content-Length",String.valueOf(this.content.readableBytes()));
}
}
public String version() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public int status() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public ByteBuf content() {
return content;
}
public void setContent(ByteBuf content) {
this.content = content;
}
public Map<String, String> headers() {
return headers;
}
@Override
public String toString() {
return "HttpResponse{" +
"version='" + version + '\'' +
", status=" + status +
", content=" + content.toString(StandardCharsets.UTF_8) +
", headers=" + headers +
'}';
}
}
创建HttpMessageCodec
实现将http请求封装成HttpRequest对象,将服务端响应的数据封装成HttpResponse
package com.lhstack.bio.codec.http;
import com.lhstack.bio.codec.MessageCodec;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
/**
* @author lhstack
* http解编码器
*/
public class HttpMessageCodec implements MessageCodec<HttpResponse> {
/**
* 解析状态
*/
enum State{
//http读取行状态
READ_LINE,
//http请求体状态
READ_BODY,
//http读取完成状态
READ_FINISH
}
/**
* 保存上一次读取的字节
*/
private byte oldByte = ' ';
private static final String LR_LF = "\r\n";
private static final String SPACE = " ";
private State state = State.READ_LINE;
private HttpRequest httpRequest;
private static final String CONTENT_LENGTH = "Content-Length";
/**
* 读取的http请求信息
*/
private ByteBuf httpBody = Unpooled.buffer(128);
@Override
public ByteBuf messageEncoder(HttpResponse msg) {
ByteBuf result = Unpooled.buffer(128);
result.writeBytes(String.format("%s %s %s\r\n",msg.version(),msg.status(),"OK").getBytes(StandardCharsets.UTF_8));
msg.headers().forEach((k,v) ->{
result.writeBytes(String.format("%s: %s\r\n",k,v).getBytes(StandardCharsets.UTF_8));
});
if(msg.headers().containsKey(CONTENT_LENGTH)){
result.writeBytes(new byte[]{
'\r','\n'})
.writeBytes(msg.content());
}
result.writeBytes(new byte[]{
'\r','\n'});
return result;
}
@Override
public Object messageDecoder(ByteBuf buf) {
if(state == State.READ_LINE){
//循环判断是否可读
while(buf.isReadable()){
buf.markReaderIndex();
//获取一个字节
byte b = buf.readByte();
if(this.oldByte == '\n' && b == '\r'){
//如果不可读,则返回到上一个状态
if(!buf.isReadable()){
buf.resetReaderIndex();
return null;
}
//读取完最后一个\n
buf.readByte();
//开始解析http协议
httpParse();
break;
}
this.oldByte = b;
this.httpBody.writeByte(b);
}
}
if(this.state == State.READ_BODY){
int length = Integer.parseInt(this.httpRequest.headers().get(CONTENT_LENGTH));
//如果长度不够,等待下一次数据到达
if(buf.readableBytes() < length){
return null;
}
ByteBuf requestBody = Unpooled.buffer(length);
buf.readBytes(requestBody);
this.httpRequest.setContent(requestBody);
this.state = State.READ_FINISH;
}
if(this.state == State.READ_FINISH){
//初始化
this.state = State.READ_LINE;
//回收httpBody
this.httpBody.release();
this.httpBody = Unpooled.buffer(128);
this.oldByte = ' ';
HttpRequest copy = this.httpRequest.copy();
//回收httpRequest
this.httpRequest.getContent().release();
this.httpRequest = null;
return copy;
}
return null;
}
private void httpParse() {
byte[] bytes = new byte[this.httpBody.readableBytes()];
//读取这一次的请求
this.httpBody.readBytes(bytes);
//存储解析出来的请求头
Map<String,String> headers = new HashMap<>();
//获取每一行数据
String[] lines = new String(bytes, StandardCharsets.UTF_8).split(LR_LF);
//GET /index.html HTTP/1.1
String[] line0 = lines[0].split(SPACE);
String method = line0[0].strip();
String uri = line0[1].strip();
String version = line0[2].strip();
for (int i = 1; i < lines.length; i++) {
String[] kv = lines[i].split(":",2);
//获取key
String key = kv[0].strip();
if(!headers.containsKey(key) && kv.length == 1){
headers.put(key,"");
}else if(!headers.containsKey(key) && kv.length == 2){
headers.put(key,kv[1].strip());
}else {
headers.put(key,headers.get(key) + "," + kv[1].strip());
}
}
httpRequest = new HttpRequest(version,uri,method,headers);
if(headers.containsKey(CONTENT_LENGTH)){
this.state = State.READ_BODY;
}else{
this.state = State.READ_FINISH;
}
}
}
启动服务端
public static void main(String[] args) throws Exception {
Server server = new Server(8080, 200, HttpMessageCodec::new, () -> (MessageHandler<HttpRequest>) (msg, socket) -> {
System.out.println(msg);
HttpResponse httpResponse = new HttpResponse("HTTP/1.1", 200, Unpooled.wrappedBuffer("hello world".getBytes(StandardCharsets.UTF_8)));
httpResponse.headers().put("Content-Type","text/plain;charset=utf-8");
return httpResponse;
});
server.start();
}
使用浏览器访问,查看服务端控制台,可以看到,解析到了浏览器的请求数据
再查看浏览器,浏览器也将服务端响应的hello world打印到了页面
我们再使用PostMan实现带文件的请求,看服务端是否接受到了文件
可以看到,postman的请求到了服务端,并接收到了服务端响应的hello world
这个时候,我们查看服务端,同时服务端也接收到了客户端发送过来的文件,说明我们这次针对http协议的解析,是成功的
下一期,我将实现一个群聊服务器,实现多个客户端互相聊天
转载:https://blog.csdn.net/qq_42413011/article/details/114646159
查看评论