使用netty简单实现web服务器

文章介绍了HTTP请求和响应报文的结构,包括请求行、请求头和请求体以及响应行、响应头和响应体。接着展示了如何使用Netty框架来实现一个简单的Web服务和HTTP客户端。在服务端,通过NettyServerHandler解析HttpRequest并构建HttpResponse。在客户端,通过HttpClient发送请求并接收响应。
摘要由CSDN通过智能技术生成

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());
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值