前言

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

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

主要角色

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

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

项目构建

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

build.gradle

复制内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//插件管理
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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一样,使用简单,原理复杂是一个样的,但是凡事都是先从会用开始学习。