Netty服务端简易实现(http协议),Netty网络编程之服务端

简单了解为何学习netty

  • 说到netty就不得不提各种IO模型,在java中常见的主要有三种BIO(同步阻塞的I/O模型)、NIO(同步非阻塞的I/O模型)、AIO(异步非阻塞的IO模型,期性能依赖于操作系统),其中优劣势就不过多评论,可以参考别的博文。netty是对NIO的封装实现,据说相较与传统I/O(BIO)模型性能提升了8倍之多
  • 如果我们想进一步提升自己还是有必要学习一下的,我将会实现Netty的服务端、客户端的简单编码,同时也会实现一个自定义协议,来模拟RPC框架,来加深理解,今天先简单看一下Netty实现服务端的代码。
    学习netty之前,一定要对NIO有一点了解,对比着来看,方便理解

代码一览

  • 服务端启动类
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

/**
 * @ClassName : NettyServer
 * @Description : Netty服务端
 * @Author : 明心
 * @Date: 2021-01-20
 */
public class NettyServer {
     
    //端口号
    private int port;

    //通过构造方法传入端口号
    public NettyServer(int port) {
     
        this.port = port;
    }

    //服务端的启动方法
    public void start(){
     
        //在NIO中有一个Selector,是一个主线程
        //在netty中将主线程划分问:主线程(selector线程数)和工作线程两个
        //由主线程分配work线程,work线程就是具体执行业务逻辑的线程
        //EventLoopGroup:线程池接口,
        // NioEventLoopGroup需要传一个最大线程数,
        // 根据源码得出结论:如果不传或者是0,则默认为cpu核数*2
        EventLoopGroup bossGroup=new NioEventLoopGroup();
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        //相当于 serverScoket  /serverScoketChannel
        ServerBootstrap b=new ServerBootstrap();
        //由ServerBootstrap维护两个线程,work线程是具体执行业务逻辑的
        b.group(bossGroup,workerGroup)
                //使用哪个线程模型,即NioSctpServerChannel类去轮询serverScoket.key
                .channel(NioServerSocketChannel.class)
                //添加配置信息
                //ChannelInitializer :子线程具体执行业务逻辑的
                .childHandler(new ChannelInitializer<SocketChannel>() {
     
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
     
                        ChannelPipeline pipeline = ch.pipeline();
                        //编码器
                        pipeline.addLast(new HttpResponseEncoder())
                                //http解码器
                                .addLast(new HttpRequestDecoder())
                                .addLast(new HttpObjectAggregator(10240))
                                //业务逻辑处理
                                .addLast(new NetDemoHandler());
                    }
                })
                //最大主线程的key的数量
                .option(ChannelOption.SO_BACKLOG, 128)
                //子线程  ChannelOption.SO_KEEPALIVE:长连接,true:一直回收利用
                .childOption(ChannelOption.SO_KEEPALIVE, true);

        try {
     
            // 启动服务器
            ChannelFuture f = b.bind(port).sync();
            System.out.println("netty服务端已启动,监听的端口是:" + port);
            f.channel().closeFuture().sync();
        }catch (Exception e){
     
            e.printStackTrace();
        }

    }
    
    public static void main(String[] args) {
     
        new NettyServer(8080).start();
    }
}
  • 服务端业务逻辑处理类
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName : NetDemoHandler
 * 所有的Handler都有一个共同的父类就是:ChannelInboundHandlerAdapter 
 * 我们可重写里面的两个放法来实现具体逻辑
 * @Description : 业务逻辑处理
 * @Author : 明心
 * @Date: 2021-01-20 
 */
public class NetDemoHandler extends ChannelInboundHandlerAdapter {
     

    //当客户端连接上会调用此方法
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
     

        Map<String, Object> params = getParams((FullHttpRequest) msg);
        //System.out.println(params);

        //将收到的结果写出去
        String result="success params="+params;

        // 设置 http协议及请求头信息,这个地方需要注意一下,一定要符合http请求,才能被写出去,
        FullHttpResponse response = new DefaultFullHttpResponse(
                // 设置http版本为1.1
                HttpVersion.HTTP_1_1,
                // 设置响应状态码
                HttpResponseStatus.OK,
                // 将输出值写出 编码为UTF-8
                Unpooled.wrappedBuffer(result.getBytes("UTF-8")));
        response.headers().set("Content-Type", "text/html;");

        ctx.write(response);
        ctx.flush();
        ctx.close();
    }

    //如果出现异常会调用此方法
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
     
        cause.printStackTrace();
    }

    /**
     * http解析参考了博文:https://blog.csdn.net/neosmith/article/details/50383548
     * 获取客户端传来的参数
     * @param request
     * @return
     * @throws IOException
     */
    private Map<String, Object> getParams(FullHttpRequest request) throws IOException {
     
        Map<String, Object> parameters=new HashMap<>();
        //请求方法类型
        if("GET".equals(request.method().name())){
     
            QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
            if(decoder.parameters().size()>0){
     
                Map<String, List<String>> parametersMap = decoder.parameters();
                parametersMap.forEach((k,v)->{
     
                    parameters.put(k,v.get(0));
                });
            }
        }else {
     
            // 是POST请求
            HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(request);
            decoder.offer(request);
            List<InterfaceHttpData> parmList = decoder.getBodyHttpDatas();

            for (InterfaceHttpData parm : parmList) {
     

                Attribute data = (Attribute) parm;
                parameters.put(data.getName(), data.getValue());
            }
        }
        return parameters;
    }

}
  • 效果

  • 启动
    Netty服务端简易实现(http协议),Netty网络编程之服务端_第1张图片

  • GET请求

Netty服务端简易实现(http协议),Netty网络编程之服务端_第2张图片

  • POST请求
    Netty服务端简易实现(http协议),Netty网络编程之服务端_第3张图片

  • 点此转到客户端代码

你可能感兴趣的:(Netty专项,java,netty,网络)