http请求报文
请求报文由三部分组成:请求行 + 请求头 + 请求体
http响应报文
HTTP 的响应报文也由三部分组成:响应行+响应头+响应体
响应行结构为:协议版本 + 空格 + 响应状态码
响应头和响应体参照请求报文
使用netty实现web服务
代码示例:
httprequest
@Data
public class HttpRequest {
private String method;
private String url;
private String version = "HTTP/1.1";
private Map<String, String> headers = new HashMap<>();
private String body;
public void addHeader(String name, String value) {
this.headers.put(name, value);
}
public void url(String url) {
String replace = url.replace("http://", "");
String host = replace.substring(0, replace.indexOf("/"));
this.url = replace.substring(replace.indexOf("/"));
headers.put("Host", host);
}
public String buildRequest() {
StringBuffer stringBuffer = new StringBuffer();
//起始行
stringBuffer.append(method).append(" ")
.append(url).append(" ")
.append(version).append("\r\n");
//头信息
headers.forEach((key, value) -> {
stringBuffer.append(key).append(": ").append(value).append("\r\n");
});
try {
int length = 0;
if (body != null) {
length = body.getBytes("UTF-8").length;
}
stringBuffer.append("Content-Length: ").append(length).append("\r\n");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
//添加空行结束头信息
stringBuffer.append("\r\n");
stringBuffer.append(body);
return stringBuffer.toString();
}
public static HttpRequest parse(String request) {
HttpRequest httpRequest = new HttpRequest();
String[] requestArray = request.split("\r\n");
//第一行 起始行
String[] first = requestArray[0].split(" ");
httpRequest.setMethod(first[0]);
httpRequest.setUrl(first[1]);
httpRequest.setVersion(first[2]);
//循环获取header
int i = 1;
for (;i< requestArray.length; i++) {
if (requestArray[i].length() == 0) {
//header和body的分割行
break;
}
String[] header = requestArray[i].split(":");
httpRequest.addHeader(header[0], header[1].trim());
}
i++; //从下一行开始读取
StringBuffer stringBuffer = new StringBuffer();
for (;i< requestArray.length; i++) {
stringBuffer.append(requestArray[i]).append("\r\n");
}
int length = stringBuffer.toString().getBytes().length - 2;
httpRequest.setBody(new String(stringBuffer.toString().getBytes(), 0, length));
return httpRequest;
}
}
HttpResponse
@Data
public class HttpResponse {
private String version;
private Integer code;
private Map<String, String> headers = new HashMap<>();
private String body;
private boolean success;
public void addHeader(String name, String value) {
this.headers.put(name, value);
}
public String buildResponse() {
StringBuffer stringBuffer = new StringBuffer();
//起始行
stringBuffer.append(version).append(" ")
.append(code).append("\r\n");
//头信息
headers.forEach((key, value) -> {
stringBuffer.append(key).append(": ").append(value).append("\r\n");
});
try {
int length = 0;
if (body != null) {
length = body.getBytes("UTF-8").length;
}
stringBuffer.append("Content-Length: ").append(length).append("\r\n");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
//添加空行结束头信息
stringBuffer.append("\r\n");
stringBuffer.append(body);
return stringBuffer.toString();
}
public static HttpResponse parse(String response) {
HttpResponse httpResponse = new HttpResponse();
//按行拆分
String[] responseArray = response.split("\r\n");
//第一行 协议版本 + 状态码
String[] first = responseArray[0].split(" ");
httpResponse.setVersion(first[0]);
httpResponse.setCode(Integer.parseInt(first[1]));
httpResponse.setSuccess(Integer.valueOf(200).equals(httpResponse.getCode()));
//循环获取header
int i = 1;
for (;i< responseArray.length; i++) {
if (responseArray[i].length() == 0) {
//header和body的分割行
break;
}
String[] header = responseArray[i].split(":");
httpResponse.addHeader(header[0], header[1].trim());
}
i++; //从下一行开始读取
int length = 0;
boolean flag = false;
if (httpResponse.getHeaders().containsKey("Transfer-Encoding") && "chunked".equals(httpResponse.getHeaders().get("Transfer-Encoding"))) {
flag = true;
i++;
} else {
length = Integer.parseInt(httpResponse.getHeaders().get("Content-Length"));
}
try {
StringBuffer stringBuffer = new StringBuffer();
for (;i< responseArray.length; i++) {
stringBuffer.append(responseArray[i]).append("\r\n");
if (flag && "0".equals(responseArray[i])) {
length = stringBuffer.toString().getBytes("UTF-8").length - 5;
}
}
httpResponse.setBody(new String(stringBuffer.toString().getBytes("UTF-8"),0 , length));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return httpResponse;
}
}
netty服务端代码
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
//连接处理线程
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 业务处理线程
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class) // 使用nio管道
.option(ChannelOption.SO_BACKLOG, 1024) // 设置客户端连接请求等待队列
.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 消息都是ByteBuf , 使用字符串
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new StringEncoder());
ch.pipeline().addLast(new NettyServerHandler());
}
});
//绑定端口
ChannelFuture channelFuture = serverBootstrap.bind(9088).sync();
channelFuture.channel().closeFuture().sync();
}
}
public class NettyServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
HttpRequest request = HttpRequest.parse(s);
System.out.println("接受客户端请求:" + JSON.toJSONString(request));
// 进行业务处理 todo
HttpResponse response = new HttpResponse();
response.setVersion(request.getVersion());
response.setCode(200);
response.setBody("{\"success\":true,\"code\":200,\"message\":null,\"data\":[{\"id\":\"2\",\"userName\":\"mm\"}],\"total\":1}");
channelHandlerContext.channel().writeAndFlush(response.buildResponse());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
使用apifox接口调用
服务器成功响应,目前服务端未实现接口处理逻辑,固定返回结果。
使用netty实现简单httpclient
代码示例:
public class HttpClient {
public static void main(String[] args) throws InterruptedException, IOException {
AtomicReference<Channel> channel = new AtomicReference<>();
Semaphore semaphore = new Semaphore(1);
semaphore.acquire();
new Thread(() -> {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast(new StringDecoder());
channel.pipeline().addLast(new StringEncoder());
channel.pipeline().addLast(new NettyClientHandler());
}
});
try {
//启动客户端去连接服务器端
ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9088).sync();
channel.set(channelFuture.sync().channel());
semaphore.release();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
semaphore.acquire();
HttpRequest request = new HttpRequest();
request.setMethod("POST");
request.url("http://localhost:9088/yh/user/queryList");
request.addHeader("Content-Type", "application/json");
request.addHeader("Accept", "*/*");
request.addHeader("Accept-Encoding", "gzip, deflate, br");
request.setBody("{\"data\":{}}");
channel.get().writeAndFlush(request.buildRequest());
int read = System.in.read();
}
}
public class NettyClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
HttpResponse response = HttpResponse.parse(s);
System.out.println("调用http请求结果:" + (response.isSuccess() ? "成功" : "异常码:" +response.getCode()) + ",响应:");
if (response.getHeaders().containsKey("Content-Type") && "application/json".equals(response.getHeaders().get("Content-Type"))) {
JSONObject jsonObject = JSONObject.parseObject(response.getBody());
System.out.println(jsonObject);
} else {
System.out.println(response.getBody());
}
}
}