netty 构建一个简易服务

2023-10-20 12:49:14 浏览数 (2)

前言

构建一个只有Server服务端的netty服务,不需要额外的编写client去访问,也就是说使用现在的软件去访问。 最简单的就是使用浏览器去访问,浏览是HTTP协议,所以服务必须是一个支持HTTP协议的应用。

实现使用: IDEA netty gradle 构建一个简单的HTTP服务,用来学习netty。

主要角色

netty 构建一个服务,需要三个角色参与

  1. Server 运行服务
  2. Initializer 组装组件
  3. Handle 业秋处理

项目构建

可以创建一个gradle的空项目,这下面的代码复制进到项目中。

build.gradle

复制内容

代码语言:javascript复制
//插件管理
plugins {
    id 'java'
}

//座标
group 'com.liukai.netty'
version '1.0-SNAPSHOT'

//源和目标编译版本
sourceCompatibility = 1.8
targetCompatibility = 1.8

//仓库,这里使用 maven 远程仓库,也就是说本没有,就去远程获取
repositories {
    mavenCentral()
}

//依赖管理
dependencies {
    //测试用名,不用打到生产包中
    //group是 maven 的 group, name 是 artifactId, version 相同
    testCompile group: 'junit', name: 'junit', version: '4.12'
    //等价写法,推荐 group:artifactId:version
    testCompile(
            "junit:junit:4.12"
            //多行逗号隔开
    )
    //编译的包
    compile(
            "io.netty:netty-all:4.1.10.Final"
            //多行逗号隔开
    )
}

服务端 NettyServer

代码语言:javascript复制
package com.liukai.netty.test01;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * 创建 http 服务器
 *
 * @author liu kai
 * @since 2019-12-29 22:03
 */
public class NettyServer {

    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                .childHandler(new NettyChannelInitializer());
        try {
            ChannelFuture channelFuture = bootstrap.bind(8889).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
   }

}

组装组件 NettyChannelInitializer

代码语言:javascript复制
package com.liukai.netty.test01;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;

/**
 * 渠道构建
 *
 * @author liu kai
 * @since 2019-12-29 22:03
 */
public class NettyChannelInitializer extends ChannelInitializer<SocketChannel> {

    //这是一个回调的方法,在channel被注册时被调用
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //这里看一下源码
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //增加一个自己定义的处理器handel
        pipeline.addLast("testHttpServerHandler", new NettyServerHandle());
    }
}

Handle 业务处理器 NettyServerHandle

代码语言:javascript复制
package com.liukai.netty.test01;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import java.net.URI;

/**
 * 业务处理
 *
 * @author liu kai
 * @since 2019-12-29 22:02
 */
public class NettyServerHandle extends SimpleChannelInboundHandler<HttpObject> {
    /**
     * 继承InboundHandler类,代表处理进入的请求,还有OutboundHandler,处理出去请求
     * 其中里面的泛型表示msg的类型,如果指定了HttpObject,表明这是个HTTP连接的对象
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        //channelRead0读取客户端请求,并返回响应的方法
        //如果不加这个判断使用curl 测试会报错,使用curl测试命令curl "http://localhost:8899"
        //判断这个是不是httpRequest请求
        if (msg instanceof HttpRequest) {
            System.out.println(msg.getClass());
            System.out.println(ctx.channel().remoteAddress());
            HttpRequest httpRequest = (HttpRequest) msg;
            URI uri = new URI(httpRequest.getUri());
            ctx.channel().closeFuture();

            if ("/favicon.ico".equals(uri.getPath())) {
                System.out.println("chrome 请求 favicon");
                return;
            }
            System.out.println("请求方法名: "   httpRequest.getMethod().name());
            //ByteBuf 是netty中的重要概念,代表响应返回的数据
            ByteBuf content = Unpooled.copiedBuffer("Hello World", CharsetUtil.UTF_8);
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
            //如果只是调用write方法,他仅仅是存在缓冲区里,并不会返回客户端
            //调用writeAndFlush可以
            ctx.writeAndFlush(response);
        }
    }
}

验证

控制台输出

总结

netty是一个不错的网络通信框架,本身使用起来比较简单,但是越是使用简单的框架底层实现就会比较复杂。 就跟String一样,使用简单,原理复杂是一个样的,但是凡事都是先从会用开始学习。

0 人点赞