目前正在写的一个叫做 jsoncat 的轻量级 HTTP 框架内置的 HTTP 服务器是我自己基于 Netty 写的,所有的核心代码加起来不过就几十行。这得益于 Netty 提供的各种开箱即用的组件,为我们节省了太多事情。

这篇文章我会手把手带着小伙伴们实现一个简易的 HTTP Server。

如果文章有任何需要改善和完善的地方,欢迎在评论区指出,共同进步!

开始之前为了避免有小伙伴不了解 Netty ,还是先来简单介绍它!

什么是 Netty?
简单用 3 点来概括一下 Netty 吧!

Netty 是一个基于 NIO 的 client-server(客户端服务器)框架,使用它可以快速简单地开发网络应用程序。
Netty 极大地简化并优化了 TCP 和 UDP 套接字服务器等网络编程,并且性能以及安全性等很多方面都要更好。
Netty 支持多种协议 如 FTP,SMTP,HTTP 以及各种二进制和基于文本的传统协议。本文所要写的 HTTP Server 就得益于 Netty 对 HTTP 协议(超文本传输协议)的支持。
Netty 应用场景有哪些?
凭借自己的了解,简单说一下吧!理论上来说,NIO 可以做的事情 ,使用 Netty 都可以做并且更好。

不过,我们还是首先要明确的是 Netty 主要用来做网络通信 。

实现框架的网络通信模块 : Netty 几乎满足任何场景的网络通信需求,因此,框架的网络通信模块可以基于 Netty 来做。拿 RPC 框架来说! 我们在分布式系统中,不同服务节点之间经常需要相互调用,这个时候就需要 RPC 框架了。不同服务指点的通信是如何做的呢?那就可以使用 Netty 来做了!比如我调用另外一个节点的方法的话,至少是要让对方知道我调用的是哪个类中的哪个方法以及相关参数吧!
实现一个自己的 HTTP 服务器 :通过 Netty ,我们可以很方便地使用少量代码实现一个简单的 HTTP 服务器。Netty 自带了编解码器和消息聚合器,为我们开发节省了很多事!
实现一个即时通讯系统 : 使用 Netty 我们可以实现一个可以聊天类似微信的即时通讯系统,这方面的开源项目还蛮多的,可以自行去 Github 找一找。
实现消息推送系统 :市面上有很多消息推送系统都是基于 Netty 来做的。

那些开源项目用到了 Netty?
我们平常经常接触的 Dubbo、RocketMQ、Elasticsearch、gRPC 、Spring Cloud Gateway 等等都用到了 Netty。

可以说大量的开源项目都用到了 Netty,所以掌握 Netty 有助于你更好的使用这些开源项目并且让你有能力对其进行二次开发。

实际上还有很多很多优秀的项目用到了 Netty,Netty 官方也做了统计,统计结果在这里:https://netty.io/wiki/related-projects.html 。

实现 HTTP Server 必知的前置知识
既然,我们要实现 HTTP Server 那必然先要回顾一下 HTTP 协议相关的基础知识。

HTTP 协议
超文本传输协议(HTTP,HyperText Transfer Protocol)主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。

当我们使用浏览器浏览网页的时候,我们网页就是通过 HTTP 请求进行加载的,整个过程如下图所示。

HTTP请求过程

https://www.seobility.net/en/wiki/HTTP_headers

HTTP 协议是基于 TCP 协议的,因此,发送 HTTP 请求之前首先要建立 TCP 连接也就是要经历 3 次握手。目前使用的 HTTP 协议大部分都是 1.1。在 1.1 的协议里面,默认是开启了 Keep-Alive 的,这样的话建立的连接就可以在多次请求中被复用了。

了解了 HTTP 协议之后,我们再来看一下 HTTP 报文的内容,这部分内容很重要!(参考图片来自:https://iamgopikrishna.wordpress.com/2014/06/13/4/)

HTTP 请求报文:

HTTP 请求报文

HTTP 响应报文:

HTTP 响应报文

我们的 HTTP 服务器会在后台解析 HTTP 请求报文内容,然后根据报文内容进行处理之后返回 HTTP 响应报文给客户端。

Netty 编解码器
如果我们要通过 Netty 处理 HTTP 请求,需要先进行编解码。所谓编解码说白了就是在 Netty 传输数据所用的 ByteBuf 和 Netty 中针对 HTTP 请求和响应所提供的对象比如 HttpRequest 和 HttpContent之间互相转换。

Netty 自带了 4 个常用的编解码器:

HttpRequestEncoder (HTTP 请求编码器):将 HttpRequest 和 HttpContent 编码为 ByteBuf 。
HttpRequestDecoder (HTTP 请求解码器):将 ByteBuf 解码为 HttpRequest 和 HttpContent
HttpResponsetEncoder (HTTP 响应编码器):将 HttpResponse 和 HttpContent 编码为 ByteBuf 。
HttpResponseDecoder(HTTP 响应解码器):将 ByteBuf 解码为 HttpResponst 和 HttpContent
网络通信最终都是通过字节流进行传输的。 ByteBuf 是 Netty 提供的一个字节容器,其内部是一个字节数组。 当我们通过 Netty 传输数据的时候,就是通过 ByteBuf 进行的。

HTTP Server 端用于接收 HTTP Request,然后发送 HTTP Response。因此我们只需要 HttpRequestDecoder 和 HttpResponseEncoder 即可。

我手绘了一张图,这样看着应该更容易理解了。

Netty 对 HTTP 消息的抽象
为了能够表示 HTTP 中的各种消息,Netty 设计了抽象了一套完整的 HTTP 消息结构图,核心继承关系如下图所示。

HttpObject : 整个 HTTP 消息体系结构的最上层接口。HttpObject 接口下又有 HttpMessage 和HttpContent两大核心接口。
HttpMessage: 定义 HTTP 消息,为HttpRequest和HttpResponse提供通用属性
HttpRequest : HttpRequest对应 HTTP request。通过 HttpRequest 我们可以访问查询参数(Query Parameters)和 Cookie。和 Servlet API 不同的是,查询参数是通过QueryStringEncoder和QueryStringDecoder来构造和解析查询查询参数。
HttpResponse : HttpResponse 对应 HTTP response。和HttpMessage相比,HttpResponse 增加了 status(相应状态码) 属性及其对应的方法。
HttpContent : 分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制(HTTP/1.1 才有),允许 HTTP 由应用服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多“块”(数据量比较大的情况)。我们可以把 HttpContent 看作是这一块一块的数据。
LastHttpContent : 标识 HTTP 请求结束,同时包含 HttpHeaders 对象。
FullHttpRequest 和 FullHttpResponse : HttpMessage 和 HttpContent 聚合后得到的对象。

HTTP 消息聚合器
HttpObjectAggregator 是 Netty 提供的 HTTP 消息聚合器,通过它可以把 HttpMessage 和 HttpContent 聚合成一个 FullHttpRequest 或者 FullHttpResponse(取决于是处理请求还是响应),方便我们使用。

另外,消息体比较大的话,可能还会分成好几个消息体来处理,HttpObjectAggregator 可以将这些消息聚合成一个完整的,方便我们处理。

使用方法:将 HttpObjectAggregator 添加到 ChannelPipeline 中,如果是用于处理 HTTP Request 就将其放在 HttpResponseEncoder 之后,反之,如果用于处理 HTTP Response 就将其放在 HttpResponseDecoder 之后。

因为,HTTP Server 端用于接收 HTTP Request,对应的使用方式如下。

ChannelPipeline p = …;
p.addLast(“decoder”, new HttpRequestDecoder())
.addLast(“encoder”, new HttpResponseEncoder())
.addLast(“aggregator”, new HttpObjectAggregator(512 * 1024))
.addLast(“handler”, new HttpServerHandler());
基于 Netty 实现一个 HTTP Server
通过 Netty,我们可以很方便地使用少量代码构建一个可以正确处理 GET 请求和 POST 请求的轻量级 HTTP Server。

源代码地址:https://github.com/Snailclimb/netty-practical-tutorial/tree/master/example/http-server 。

添加所需依赖到 pom.xml
第一步,我们需要将实现 HTTP Server 所必需的第三方依赖的坐标添加到 pom.xml中。

io.netty netty-all 4.1.42.Final org.slf4j slf4j-api 1.7.25 org.slf4j slf4j-simple 1.7.25 org.projectlombok lombok 1.18.8 provided commons-codec commons-codec 1.14 创建服务端 @Slf4j public class HttpServer {

private static final int PORT = 8080;public void start() {EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap();b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)// TCP默认开启了 Nagle 算法,该算法的作用是尽可能的发送大数据快,减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。.childOption(ChannelOption.TCP_NODELAY, true)// 是否开启 TCP 底层心跳机制.childOption(ChannelOption.SO_KEEPALIVE, true)//表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁,服务器处理创建新连接较慢,可以适当调大这个参数.option(ChannelOption.SO_BACKLOG, 128).handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) {ch.pipeline().addLast("decoder", new HttpRequestDecoder()).addLast("encoder", new HttpResponseEncoder()).addLast("aggregator", new HttpObjectAggregator(512 * 1024)).addLast("handler", new HttpServerHandler());}});Channel ch = b.bind(PORT).sync().channel();log.info("Netty Http Server started on port {}.", PORT);ch.closeFuture().sync();} catch (InterruptedException e) {log.error("occur exception when start server:", e);} finally {log.error("shutdown bossGroup and workerGroup");bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}
}

}
简单解析一下服务端的创建过程具体是怎样的!

1.创建了两个 NioEventLoopGroup 对象实例:bossGroup 和 workerGroup。

bossGroup : 用于处理客户端的 TCP 连接请求。
workerGroup : 负责每一条连接的具体读写数据的处理逻辑,真正负责 I/O 读写操作,交由对应的 Handler 处理。
举个例子:我们把公司的老板当做 bossGroup,员工当做 workerGroup,bossGroup 在外面接完活之后,扔给 workerGroup 去处理。一般情况下我们会指定 bossGroup 的 线程数为 1(并发连接量不大的时候) ,workGroup 的线程数量为 CPU 核心数 *2 。另外,根据源码来看,使用 NioEventLoopGroup 类的无参构造函数设置线程数量的默认值就是 CPU 核心数 *2 。

2.创建一个服务端启动引导/辅助类: ServerBootstrap,这个类将引导我们进行服务端的启动工作。

3.通过 .group() 方法给引导类 ServerBootstrap 配置两大线程组,确定了线程模型。

4.通过channel()方法给引导类 ServerBootstrap指定了 IO 模型为NIO

NioServerSocketChannel :指定服务端的 IO 模型为 NIO,与 BIO 编程模型中的ServerSocket对应
NioSocketChannel : 指定客户端的 IO 模型为 NIO, 与 BIO 编程模型中的Socket对应
5.通过 .childHandler()给引导类创建一个ChannelInitializer ,然后指定了服务端消息的业务处理逻辑也就是自定义的ChannelHandler 对象

6.调用 ServerBootstrap 类的 bind()方法绑定端口 。

//bind()是异步的,但是,你可以通过 sync()方法将其变为同步。
ChannelFuture f = b.bind(port).sync();
自定义服务端 ChannelHandler 处理 HTTP 请求
我们继承SimpleChannelInboundHandler ,并重写下面 3 个方法:

channelRead() :服务端接收并处理客户端发送的 HTTP 请求调用的方法。
exceptionCaught() :处理客户端发送的 HTTP 请求发生异常的时候被调用。
channelReadComplete() : 服务端消费完客户端发送的 HTTP 请求之后调用的方法。
另外,客户端 HTTP 请求参数类型为 FullHttpRequest。我们可以把 FullHttpRequest对象看作是 HTTP 请求报文的 Java 对象的表现形式。

@Slf4j
public class HttpServerHandler extends SimpleChannelInboundHandler {
private static final String FAVICON_ICO = “/favicon.ico”;
private static final AsciiString CONNECTION = AsciiString.cached(“Connection”);
private static final AsciiString KEEP_ALIVE = AsciiString.cached(“keep-alive”);
private static final AsciiString CONTENT_TYPE = AsciiString.cached(“Content-Type”);
private static final AsciiString CONTENT_LENGTH = AsciiString.cached(“Content-Length”);

@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) {log.info("Handle http request:{}", fullHttpRequest);String uri = fullHttpRequest.uri();if (uri.equals(FAVICON_ICO)) {return;}RequestHandler requestHandler = RequestHandlerFactory.create(fullHttpRequest.method());Object result;FullHttpResponse response;try {result = requestHandler.handle(fullHttpRequest);String responseHtml = "<html><body>" + result + "</body></html>";byte[] responseBytes = responseHtml.getBytes(StandardCharsets.UTF_8);response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(responseBytes));response.headers().set(CONTENT_TYPE, "text/html; charset=utf-8");response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());} catch (IllegalArgumentException e) {e.printStackTrace();String responseHtml = "<html><body>" + e.toString() + "</body></html>";byte[] responseBytes = responseHtml.getBytes(StandardCharsets.UTF_8);response = new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer(responseBytes));response.headers().set(CONTENT_TYPE, "text/html; charset=utf-8");}boolean keepAlive = HttpUtil.isKeepAlive(fullHttpRequest);if (!keepAlive) {ctx.write(response).addListener(ChannelFutureListener.CLOSE);} else {response.headers().set(CONNECTION, KEEP_ALIVE);ctx.write(response);}
}@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();
}@Override
public void channelReadComplete(ChannelHandlerContext ctx) {ctx.flush();
}

}
我们返回给客户端的消息体是 FullHttpResponse 对象。通过 FullHttpResponse 对象,我们可以设置 HTTP 响应报文的 HTTP 协议版本、响应的具体内容 等内容。

我们可以把 FullHttpResponse 对象看作是 HTTP 响应报文的 Java 对象的表现形式。

FullHttpResponse response;

String responseHtml = “” + result + “”;
byte[] responseBytes = responseHtml.getBytes(StandardCharsets.UTF_8);
// 初始化 FullHttpResponse ,并设置 HTTP 协议 、响应状态码、响应的具体内容
response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(responseBytes));
我们通过 FullHttpResponse的headers()方法获取到 HttpHeaders,这里的 HttpHeaders 对应于 HTTP 响应报文的头部。通过 HttpHeaders对象,我们就可以对 HTTP 响应报文的头部的内容比如 Content-Typ 进行设置。

response.headers().set(CONTENT_TYPE, “text/html; charset=utf-8”);
response.headers().setInt(CONTENT_LENGTH, response.content().readableBytes());
本案例中,为了掩饰我们设置的 Content-Type 为 text/html ,也就是返回 html 格式的数据给客户端。

常见的 Content-Type

Content-Type 解释
text/html html 格式
text/plain 纯文本格式
text/css css 格式
text/javascript js 格式
application/json json 格式(前后端分离项目常用)
image/gif gif 图片格式
image/jpeg jpg 图片格式
image/png png 图片格式
请求的具体处理逻辑实现
因为有这里有 POST 请求和 GET 请求。因此我们需要首先定义一个处理 HTTP Request 的接口。

public interface RequestHandler {
Object handle(FullHttpRequest fullHttpRequest);
}
HTTP Method 不只是有 GET 和 POST,其他常见的还有 PUT、DELETE、PATCH。只是本案例中实现的 HTTP Server 只考虑了 GET 和 POST。

GET :请求从服务器获取特定资源。举个例子:GET /classes(获取所有班级)
POST :在服务器上创建一个新的资源。举个例子:POST /classes(创建班级)
PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /classes/12(更新编号为 12 的班级)
DELETE :从服务器删除特定的资源。举个例子:DELETE /classes/12(删除编号为 12 的班级)
PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。
GET 请求的处理
@Slf4j
public class GetRequestHandler implements RequestHandler {
@Override
public Object handle(FullHttpRequest fullHttpRequest) {
String requestUri = fullHttpRequest.uri();
Map<String, String> queryParameterMappings = this.getQueryParams(requestUri);
return queryParameterMappings.toString();
}

private Map<String, String> getQueryParams(String uri) {QueryStringDecoder queryDecoder = new QueryStringDecoder(uri, Charsets.toCharset(CharEncoding.UTF_8));Map<String, List<String>> parameters = queryDecoder.parameters();Map<String, String> queryParams = new HashMap<>();for (Map.Entry<String, List<String>> attr : parameters.entrySet()) {for (String attrVal : attr.getValue()) {queryParams.put(attr.getKey(), attrVal);}}return queryParams;
}

}
我这里只是简单得把 URI 的查询参数的对应关系直接返回给客户端了。

实际上,获得了 URI 的查询参数的对应关系,再结合反射和注解相关的知识,我们很容易实现类似于 Spring Boot 的 @RequestParam 注解了。

建议想要学习的小伙伴,可以自己独立实现一下。不知道如何实现的话,你可以参考我开源的轻量级 HTTP 框架jsoncat (仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架)。

POST 请求的处理
@Slf4j
public class PostRequestHandler implements RequestHandler {

@Override
public Object handle(FullHttpRequest fullHttpRequest) {String requestUri = fullHttpRequest.uri();log.info("request uri :[{}]", requestUri);String contentType = this.getContentType(fullHttpRequest.headers());if (contentType.equals("application/json")) {return fullHttpRequest.content().toString(Charsets.toCharset(CharEncoding.UTF_8));} else {throw new IllegalArgumentException("only receive application/json type data");}}private String getContentType(HttpHeaders headers) {String typeStr = headers.get("Content-Type");String[] list = typeStr.split(";");return list[0];
}

}

对于 POST 请求的处理,我们这里只接受处理 Content-Type 为 application/json 的数据,如果 POST 请求传过来的不是 application/json 类型的数据,我们就直接抛出异常。

实际上,我们获得了客户端传来的 json 格式的数据之后,再结合反射和注解相关的知识,我们很容易实现类似于 Spring Boot 的 @RequestBody 注解了。

建议想要学习的小伙伴,可以自己独立实现一下。不知道如何实现的话,你可以参考我开源的轻量级 HTTP 框架jsoncat (仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架)。

请求处理工厂类
public class RequestHandlerFactory {
public static final Map<HttpMethod, RequestHandler> REQUEST_HANDLERS = new HashMap<>();

static {REQUEST_HANDLERS.put(HttpMethod.GET, new GetRequestHandler());REQUEST_HANDLERS.put(HttpMethod.POST, new PostRequestHandler());
}public static RequestHandler create(HttpMethod httpMethod) {return REQUEST_HANDLERS.get(httpMethod);
}

}
我这里用到了工厂模式,当我们额外处理新的 HTTP Method 方法的时候,直接实现 RequestHandler 接口,然后将实现类添加到 RequestHandlerFactory 即可。

启动类
public class HttpServerApplication {
public static void main(String[] args) {
HttpServer httpServer = new HttpServer();
httpServer.start();
}
}
效果
运行 HttpServerApplication 的main()方法,控制台打印出:
https://www.qq620.com/a-43811-1-1.html
https://www.qq620.com/a-43812-1-1.html
https://www.qq620.com/a-43813-1-1.html
https://www.qq620.com/a-43814-1-1.html
https://www.qq620.com/a-43815-1-1.html
https://www.qq620.com/a-43816-1-1.html
https://www.qq620.com/a-43817-1-1.html
https://www.qq620.com/a-43818-1-1.html
https://www.qq620.com/a-43819-1-1.html
https://www.qq620.com/a-43820-1-1.html
https://www.qq620.com/a-43821-1-1.html
https://www.qq620.com/a-43822-1-1.html
https://www.qq620.com/a-43823-1-1.html
https://www.qq620.com/a-43824-1-1.html
https://www.qq620.com/a-43825-1-1.html
https://www.qq620.com/a-43826-1-1.html
https://www.qq620.com/a-43827-1-1.html
https://www.qq620.com/a-43828-1-1.html
https://www.qq620.com/a-43829-1-1.html
https://www.qq620.com/a-43830-1-1.html
https://www.qq620.com/a-43831-1-1.html
https://www.qq620.com/a-43832-1-1.html
https://www.qq620.com/a-43833-1-1.html
https://www.qq620.com/a-43834-1-1.html
https://www.qq620.com/a-43835-1-1.html
https://www.qq620.com/a-43836-1-1.html
https://www.qq620.com/a-43837-1-1.html
https://www.qq620.com/a-43838-1-1.html
https://www.qq620.com/a-43839-1-1.html
https://www.qq620.com/a-43840-1-1.html
https://www.qq620.com/a-43841-1-1.html
https://www.qq620.com/a-43842-1-1.html
https://www.qq620.com/a-43843-1-1.html
https://www.qq620.com/a-43844-1-1.html
https://www.qq620.com/a-43845-1-1.html
https://www.qq620.com/a-43846-1-1.html
https://www.qq620.com/a-43847-1-1.html
https://www.qq620.com/a-43848-1-1.html
https://www.qq620.com/a-43849-1-1.html
https://www.qq620.com/a-43850-1-1.html
https://www.qq620.com/a-43851-1-1.html
https://www.qq620.com/a-43852-1-1.html
https://www.qq620.com/a-43853-1-1.html
https://www.qq620.com/a-43854-1-1.html
https://www.qq620.com/a-43855-1-1.html
https://www.qq620.com/a-43856-1-1.html
https://www.qq620.com/a-43857-1-1.html
https://www.qq620.com/a-43858-1-1.html
https://www.qq620.com/a-43859-1-1.html
https://www.qq620.com/a-43860-1-1.html
https://www.qq620.com/a-43861-1-1.html
https://www.qq620.com/a-43862-1-1.html
https://www.qq620.com/a-43863-1-1.html
https://www.qq620.com/a-43864-1-1.html
https://www.qq620.com/a-43865-1-1.html
https://www.qq620.com/a-43866-1-1.html
https://www.qq620.com/a-43867-1-1.html
https://www.qq620.com/a-43868-1-1.html
https://www.qq620.com/a-43869-1-1.html
https://www.qq620.com/a-43870-1-1.html
https://www.qq620.com/a-43871-1-1.html
https://www.qq620.com/a-43872-1-1.html
https://www.qq620.com/a-43873-1-1.html
https://www.qq620.com/a-43874-1-1.html
https://www.qq620.com/a-43875-1-1.html
https://www.qq620.com/a-43876-1-1.html
https://www.qq620.com/a-43877-1-1.html
https://www.qq620.com/a-43878-1-1.html
https://www.qq620.com/a-43879-1-1.html
https://www.qq620.com/a-43880-1-1.html
https://www.qq620.com/a-43881-1-1.html
https://www.qq620.com/a-43882-1-1.html
https://www.qq620.com/a-43883-1-1.html
https://www.qq620.com/a-43884-1-1.html
https://www.qq620.com/a-43885-1-1.html
https://www.qq620.com/a-43886-1-1.html
https://www.qq620.com/a-43887-1-1.html
https://www.qq620.com/a-43888-1-1.html
https://www.qq620.com/a-43889-1-1.html
https://www.qq620.com/a-43890-1-1.html
https://www.qq620.com/a-43891-1-1.html
https://www.qq620.com/a-43892-1-1.html
https://www.qq620.com/a-43893-1-1.html
https://www.qq620.com/a-43894-1-1.html
https://www.qq620.com/a-43895-1-1.html
https://www.qq620.com/a-43896-1-1.html
https://www.qq620.com/a-43897-1-1.html
https://www.qq620.com/a-43898-1-1.html
https://www.qq620.com/a-43899-1-1.html
https://www.qq620.com/a-43900-1-1.html
https://www.qq620.com/a-43901-1-1.html
https://www.qq620.com/a-43902-1-1.html
https://www.qq620.com/a-43903-1-1.html
https://www.qq620.com/a-43904-1-1.html
https://www.qq620.com/a-43905-1-1.html
https://www.qq620.com/a-43906-1-1.html
https://www.qq620.com/a-43907-1-1.html
https://www.qq620.com/a-43908-1-1.html
https://www.qq620.com/a-43909-1-1.html
https://www.qq620.com/a-43910-1-1.html
https://www.qq620.com/a-43911-1-1.html
https://www.qq620.com/a-43912-1-1.html
https://www.qq620.com/a-43913-1-1.html
https://www.qq620.com/a-43914-1-1.html
https://www.qq620.com/a-43915-1-1.html
https://www.qq620.com/a-43916-1-1.html
https://www.qq620.com/a-43917-1-1.html
https://www.qq620.com/a-43918-1-1.html
https://www.qq620.com/a-43919-1-1.html
https://www.qq620.com/a-43920-1-1.html
https://www.qq620.com/a-43921-1-1.html
https://www.qq620.com/a-43922-1-1.html
https://www.qq620.com/a-43923-1-1.html
https://www.qq620.com/a-43924-1-1.html
https://www.qq620.com/a-43926-1-1.html
https://www.qq620.com/a-43927-1-1.html
https://www.qq620.com/a-43928-1-1.html
https://www.qq620.com/a-43929-1-1.html
https://www.qq620.com/a-43930-1-1.html
https://www.qq620.com/a-43931-1-1.html
https://www.qq620.com/a-43932-1-1.html
https://www.qq620.com/a-43933-1-1.html
https://www.qq620.com/a-43934-1-1.html
https://www.qq620.com/a-43935-1-1.html
https://www.qq620.com/a-43936-1-1.html
https://www.qq620.com/a-43937-1-1.html
https://www.qq620.com/a-43938-1-1.html
https://www.qq620.com/a-43939-1-1.html
https://www.qq620.com/a-43940-1-1.html
https://www.qq620.com/a-43941-1-1.html
https://www.qq620.com/a-43942-1-1.html
https://www.qq620.com/a-43943-1-1.html
https://www.qq620.com/a-43944-1-1.html
https://www.qq620.com/a-43945-1-1.html
https://www.qq620.com/a-43946-1-1.html
https://www.qq620.com/a-43947-1-1.html
https://www.qq620.com/a-43948-1-1.html
https://www.qq620.com/a-43949-1-1.html
https://www.qq620.com/a-43950-1-1.html
https://www.qq620.com/a-43951-1-1.html
https://www.qq620.com/a-43952-1-1.html
https://www.qq620.com/a-43953-1-1.html
https://www.qq620.com/a-43954-1-1.html
https://www.qq620.com/a-43955-1-1.html
https://www.qq620.com/a-43956-1-1.html
https://www.qq620.com/a-43957-1-1.html
https://www.qq620.com/a-43958-1-1.html
https://www.qq620.com/a-43959-1-1.html
https://www.qq620.com/a-43960-1-1.html
https://www.qq620.com/a-43961-1-1.html
https://www.qq620.com/a-43962-1-1.html
https://www.qq620.com/a-43963-1-1.html
https://www.qq620.com/a-43964-1-1.html
https://www.qq620.com/a-43965-1-1.html
https://www.qq620.com/a-43966-1-1.html
https://www.qq620.com/a-43967-1-1.html
https://www.qq620.com/a-43968-1-1.html
https://www.qq620.com/a-43969-1-1.html
https://www.qq620.com/a-43970-1-1.html
https://www.qq620.com/a-43971-1-1.html
https://www.qq620.com/a-43972-1-1.html
https://www.qq620.com/a-43973-1-1.html
https://www.qq620.com/a-43974-1-1.html
https://www.qq620.com/a-43975-1-1.html
https://www.qq620.com/a-43976-1-1.html
https://www.qq620.com/a-43977-1-1.html
https://www.qq620.com/a-43978-1-1.html
https://www.qq620.com/a-43979-1-1.html
https://www.qq620.com/a-43980-1-1.html
https://www.qq620.com/a-43981-1-1.html
https://www.qq620.com/a-43982-1-1.html
https://www.qq620.com/a-43983-1-1.html
https://www.qq620.com/a-43984-1-1.html
https://www.qq620.com/a-43985-1-1.html
https://www.qq620.com/a-43986-1-1.html
https://www.qq620.com/a-43987-1-1.html
https://www.qq620.com/a-43988-1-1.html
https://www.qq620.com/a-43989-1-1.html
https://www.qq620.com/a-43990-1-1.html
https://www.qq620.com/a-43991-1-1.html
https://www.qq620.com/a-43992-1-1.html
https://www.qq620.com/a-43993-1-1.html
https://www.qq620.com/a-43994-1-1.html
https://www.qq620.com/a-43995-1-1.html
https://www.qq620.com/a-43996-1-1.html
https://www.qq620.com/a-43997-1-1.html
https://www.qq620.com/a-43998-1-1.html
https://www.qq620.com/a-43999-1-1.html
https://www.qq620.com/a-44000-1-1.html
https://www.qq620.com/a-44001-1-1.html
https://www.qq620.com/a-44002-1-1.html
https://www.qq620.com/a-44003-1-1.html
https://www.qq620.com/a-44004-1-1.html
https://www.qq620.com/a-44005-1-1.html
https://www.qq620.com/a-44006-1-1.html
https://www.qq620.com/a-44007-1-1.html
https://www.qq620.com/a-44008-1-1.html
https://www.qq620.com/a-44009-1-1.html
https://www.qq620.com/a-44010-1-1.html
https://www.qq620.com/a-44011-1-1.html
https://www.qq620.com/a-44012-1-1.html
https://www.qq620.com/a-44013-1-1.html
https://www.qq620.com/a-44014-1-1.html
https://www.qq620.com/a-44015-1-1.html
https://www.qq620.com/a-44016-1-1.html
https://www.qq620.com/a-44017-1-1.html
https://www.qq620.com/a-44018-1-1.html
https://www.qq620.com/a-44019-1-1.html
https://www.qq620.com/a-44020-1-1.html
https://www.qq620.com/a-44021-1-1.html
https://www.qq620.com/a-44022-1-1.html
https://www.qq620.com/a-44023-1-1.html
https://www.qq620.com/a-44024-1-1.html
https://www.qq620.com/a-44025-1-1.html
https://www.qq620.com/a-44026-1-1.html
https://www.qq620.com/a-44027-1-1.html
https://www.qq620.com/a-44028-1-1.html
https://www.qq620.com/a-44029-1-1.html
https://www.qq620.com/a-44030-1-1.html
https://www.qq620.com/a-44031-1-1.html
https://www.qq620.com/a-44032-1-1.html
https://www.qq620.com/a-44033-1-1.html
https://www.qq620.com/a-44034-1-1.html
https://www.qq620.com/a-44035-1-1.html
https://www.qq620.com/a-44036-1-1.html
https://www.qq620.com/a-44037-1-1.html
https://www.qq620.com/a-44038-1-1.html
https://www.qq620.com/a-44039-1-1.html
https://www.qq620.com/a-44040-1-1.html
https://www.qq620.com/a-44041-1-1.html
https://www.qq620.com/a-44042-1-1.html
https://www.qq620.com/a-44043-1-1.html
https://www.qq620.com/a-44044-1-1.html
https://www.qq620.com/a-44045-1-1.html
https://www.qq620.com/a-44046-1-1.html
https://www.qq620.com/a-44047-1-1.html
https://www.qq620.com/a-44048-1-1.html
https://www.qq620.com/a-44049-1-1.html
https://www.qq620.com/a-44050-1-1.html
https://www.qq620.com/a-44051-1-1.html
https://www.qq620.com/a-44052-1-1.html
https://www.qq620.com/a-44053-1-1.html
https://www.qq620.com/a-44054-1-1.html
https://www.qq620.com/a-44055-1-1.html
https://www.qq620.com/a-44056-1-1.html
https://www.qq620.com/a-44057-1-1.html
https://www.qq620.com/a-44058-1-1.html
https://www.qq620.com/a-44059-1-1.html
https://www.qq620.com/a-44060-1-1.html
https://www.qq620.com/a-44061-1-1.html
https://www.qq620.com/a-44062-1-1.html
https://www.qq620.com/a-44063-1-1.html
https://www.qq620.com/a-44064-1-1.html
https://www.qq620.com/a-44065-1-1.html
https://www.qq620.com/a-44066-1-1.html
https://www.qq620.com/a-44067-1-1.html
https://www.qq620.com/a-44068-1-1.html
https://www.qq620.com/a-44069-1-1.html
https://www.qq620.com/a-44070-1-1.html
https://www.qq620.com/a-44071-1-1.html
https://www.qq620.com/a-44072-1-1.html
https://www.qq620.com/a-44073-1-1.html
https://www.qq620.com/a-44074-1-1.html
https://www.qq620.com/a-44075-1-1.html
https://www.qq620.com/a-44076-1-1.html
https://www.qq620.com/a-44077-1-1.html
https://www.qq620.com/a-44078-1-1.html
https://www.qq620.com/a-44079-1-1.html
https://www.qq620.com/a-44080-1-1.html
https://www.qq620.com/a-44081-1-1.html
https://www.qq620.com/a-44082-1-1.html
https://www.qq620.com/a-44083-1-1.html
https://www.qq620.com/a-44084-1-1.html
https://www.qq620.com/a-44086-1-1.html
https://www.qq620.com/a-44087-1-1.html
https://www.qq620.com/a-44088-1-1.html
https://www.qq620.com/a-44089-1-1.html
https://www.qq620.com/a-44090-1-1.html
https://www.qq620.com/a-44091-1-1.html
https://www.qq620.com/a-44092-1-1.html
https://www.qq620.com/a-44093-1-1.html
https://www.qq620.com/a-44094-1-1.html
https://www.qq620.com/a-44095-1-1.html
https://www.qq620.com/a-44096-1-1.html
https://www.qq620.com/a-44097-1-1.html
https://www.qq620.com/a-44098-1-1.html
https://www.qq620.com/a-44099-1-1.html
https://www.qq620.com/a-44100-1-1.html
https://www.qq620.com/a-44101-1-1.html
https://www.qq620.com/a-44102-1-1.html
https://www.qq620.com/a-44103-1-1.html
https://www.qq620.com/a-44104-1-1.html
https://www.qq620.com/a-44105-1-1.html
https://www.qq620.com/a-44106-1-1.html
https://www.qq620.com/a-44107-1-1.html
https://www.qq620.com/a-44108-1-1.html
https://www.qq620.com/a-44109-1-1.html
https://www.qq620.com/a-44110-1-1.html
https://www.qq620.com/a-44111-1-1.html
https://www.qq620.com/a-44112-1-1.html
https://www.qq620.com/a-44113-1-1.html
https://www.qq620.com/a-44114-1-1.html
https://www.qq620.com/a-44115-1-1.html
https://www.qq620.com/a-44116-1-1.html
https://www.qq620.com/a-44117-1-1.html
https://www.qq620.com/a-44118-1-1.html
https://www.qq620.com/a-44119-1-1.html
https://www.qq620.com/a-44120-1-1.html
https://www.qq620.com/a-44121-1-1.html
https://www.qq620.com/a-44122-1-1.html
https://www.qq620.com/a-44123-1-1.html
https://www.qq620.com/a-44124-1-1.html
https://www.qq620.com/a-44125-1-1.html
https://www.qq620.com/a-44126-1-1.html
https://www.qq620.com/a-44127-1-1.html
https://www.qq620.com/a-44128-1-1.html
https://www.qq620.com/a-44129-1-1.html
https://www.qq620.com/a-44130-1-1.html
https://www.qq620.com/a-44131-1-1.html
https://www.qq620.com/a-44132-1-1.html
https://www.qq620.com/a-44133-1-1.html
https://www.qq620.com/a-44134-1-1.html
https://www.qq620.com/a-44135-1-1.html
https://www.qq620.com/a-44136-1-1.html
https://www.qq620.com/a-44137-1-1.html
https://www.qq620.com/a-44138-1-1.html
https://www.qq620.com/a-44139-1-1.html
https://www.qq620.com/a-44140-1-1.html
https://www.qq620.com/a-44141-1-1.html
https://www.qq620.com/a-44142-1-1.html
https://www.qq620.com/a-44143-1-1.html
https://www.qq620.com/a-44144-1-1.html
https://www.qq620.com/a-44145-1-1.html
https://www.qq620.com/a-44146-1-1.html
https://www.qq620.com/a-44147-1-1.html
https://www.qq620.com/a-44148-1-1.html
https://www.qq620.com/a-44149-1-1.html
https://www.qq620.com/a-44150-1-1.html
https://www.qq620.com/a-44151-1-1.html
https://www.qq620.com/a-44152-1-1.html
https://www.qq620.com/a-44153-1-1.html
https://www.qq620.com/a-44154-1-1.html
https://www.qq620.com/a-44155-1-1.html
https://www.qq620.com/a-44156-1-1.html
https://www.qq620.com/a-44157-1-1.html
https://www.qq620.com/a-44158-1-1.html
https://www.qq620.com/a-44159-1-1.html
https://www.qq620.com/a-44160-1-1.html
https://www.qq620.com/a-44161-1-1.html
https://www.qq620.com/a-44162-1-1.html
https://www.qq620.com/a-44163-1-1.html
https://www.qq620.com/a-44164-1-1.html
https://www.qq620.com/a-44165-1-1.html
https://www.qq620.com/a-44166-1-1.html
https://www.qq620.com/a-44167-1-1.html
https://www.qq620.com/a-44168-1-1.html
https://www.qq620.com/a-44169-1-1.html
https://www.qq620.com/a-44170-1-1.html
https://www.qq620.com/a-44171-1-1.html
https://www.qq620.com/a-44172-1-1.html
https://www.qq620.com/a-44173-1-1.html
https://www.qq620.com/a-44174-1-1.html
https://www.qq620.com/a-44175-1-1.html
https://www.qq620.com/a-44176-1-1.html
https://www.qq620.com/a-44177-1-1.html
https://www.qq620.com/a-44178-1-1.html
https://www.qq620.com/a-44179-1-1.html
https://www.qq620.com/a-44180-1-1.html
https://www.qq620.com/a-44181-1-1.html
https://www.qq620.com/a-44182-1-1.html
https://www.qq620.com/a-44183-1-1.html
https://www.qq620.com/a-44184-1-1.html
https://www.qq620.com/a-44185-1-1.html
https://www.qq620.com/a-44186-1-1.html
https://www.qq620.com/a-44187-1-1.html
https://www.qq620.com/a-44188-1-1.html
https://www.qq620.com/a-44189-1-1.html
https://www.qq620.com/a-44190-1-1.html
https://www.qq620.com/a-44191-1-1.html
https://www.qq620.com/a-44192-1-1.html
https://www.qq620.com/a-44193-1-1.html
https://www.qq620.com/a-44194-1-1.html
https://www.qq620.com/a-44195-1-1.html
https://www.qq620.com/a-44196-1-1.html
https://www.qq620.com/a-44197-1-1.html
https://www.qq620.com/a-44198-1-1.html
https://www.qq620.com/a-44199-1-1.html
https://www.qq620.com/a-44200-1-1.html
https://www.qq620.com/a-44201-1-1.html
file:///C:/Users/dell/Desktop/%E8%B0%B7%E6%AD%8C%E6%B5%8F%E8%A7%88%E5%99%A8%E7%89%88YYPOST4.823B/YYPOST/loading.html
https://www.qq620.com/a-44202-1-1.html
https://www.qq620.com/a-44203-1-1.html
https://www.qq620.com/a-44204-1-1.html
https://www.qq620.com/a-44205-1-1.html
https://www.qq620.com/a-44206-1-1.html
https://www.qq620.com/a-44207-1-1.html
https://www.qq620.com/a-44208-1-1.html
https://www.qq620.com/a-44209-1-1.html
https://www.qq620.com/a-44210-1-1.html
https://www.qq620.com/a-44211-1-1.html
https://www.qq620.com/a-44212-1-1.html
https://www.qq620.com/a-44213-1-1.html
https://www.qq620.com/a-44214-1-1.html
https://www.qq620.com/a-44215-1-1.html
https://www.qq620.com/a-44216-1-1.html
https://www.qq620.com/a-44217-1-1.html
https://www.qq620.com/a-44218-1-1.html
https://www.qq620.com/a-44219-1-1.html
https://www.qq620.com/a-44220-1-1.html
https://www.qq620.com/a-44221-1-1.html
https://www.qq620.com/a-44222-1-1.html
https://www.qq620.com/a-44223-1-1.html
https://www.qq620.com/a-44224-1-1.html
https://www.qq620.com/a-44225-1-1.html
https://www.qq620.com/a-44226-1-1.html
https://www.qq620.com/a-44227-1-1.html
https://www.qq620.com/a-44228-1-1.html
https://www.qq620.com/a-44229-1-1.html
https://www.qq620.com/a-44230-1-1.html
https://www.qq620.com/a-44231-1-1.html
https://www.qq620.com/a-44232-1-1.html
https://www.qq620.com/a-44233-1-1.html
https://www.qq620.com/a-44234-1-1.html
https://www.qq620.com/a-44235-1-1.html
https://www.qq620.com/a-44236-1-1.html
https://www.qq620.com/a-44237-1-1.html
https://www.qq620.com/a-44238-1-1.html
https://www.qq620.com/a-44239-1-1.html
https://www.qq620.com/a-44240-1-1.html
https://www.qq620.com/a-44241-1-1.html
https://www.qq620.com/a-44242-1-1.html
https://www.qq620.com/a-44243-1-1.html
https://www.qq620.com/a-44244-1-1.html
https://www.qq620.com/a-44245-1-1.html
https://www.qq620.com/a-44246-1-1.html
https://www.qq620.com/a-44247-1-1.html
https://www.qq620.com/a-44248-1-1.html

[nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x9bb1012a] REGISTERED
[nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x9bb1012a] BIND: 0.0.0.0/0.0.0.0:8080
[nioEventLoopGroup-2-1] INFO io.netty.handler.logging.LoggingHandler - [id: 0x9bb1012a, L:/0:0:0:0:0:0:0:0:8080] ACTIVE
[main] INFO server.HttpServer - Netty Http Server started on port 8080.

几十行代码基于Netty搭建一个 HTTP Server相关推荐

  1. java netty聊天室_netty实现消息中心(二)基于netty搭建一个聊天室

    前言 上篇博文(netty实现消息中心(一)思路整理 )大概说了下netty websocket消息中心的设计思路,这篇文章主要说说简化版的netty聊天室代码实现,支持群聊和点对点聊天. 此demo ...

  2. 基于netty搭建websocket,实现消息的主动推送

    基于netty搭建websocket,实现消息的主动推送 rpf_siwash https://www.jianshu.com/p/56216d1052d7 netty是由jboss提供的一款开源框架 ...

  3. netty案例,netty4.1中级拓展篇五《基于Netty搭建WebSocket,模仿微信聊天页面》

    前言介绍 本章节我们模仿微信聊天页面,开发一个基于Netty搭建WebSocket通信案例.Netty的应用方面非常广:聊天.MQ.RPC.数据等等,在5G到来的时候更加需要大量数据传输,Netty的 ...

  4. 我的Serverless实战—基于Serverless搭建一个简单的WordPress个人博客图文详解-JJZ

    文正在参与 "100%有奖 | 我的Serverless 实战"征稿活动 活动链接:https://marketing.csdn.net/p/15940c87f66c68188cf ...

  5. 【NLP实战】如何基于Tensorflow搭建一个聊天机器人

    实战是学习一门技术最好的方式,也是深入了解一门技术唯一的方式.因此,NLP专栏计划推出一个实战专栏,让有兴趣的同学在看文章之余也可以自动动手试一试. 本篇介绍如何基于tensorflow快速搭建一个基 ...

  6. 不会代码,如何搭建一个个人博客?

    本科的时候不是计算机专业,学的自动化,也不太上进,尽顾着玩游戏了.那会一直觉得那些写博客的人超级牛逼,能够搭建自己个人博客的人更是牛逼哄哄的啊~ 前段时间,研究生课题没那么忙的时候,折腾着搭建了一个自 ...

  7. 基于netty实现一个简单的支持http和webSocket协议的的服务器(含xxl-job通信模块源码分析)

    文章目录 背景 依赖 包结构 实现 WebSocketServer 业务handler WebSocketServerHandler 测试 xxl-job 源码中基于netty实现的http 总结 参 ...

  8. python知识图谱问答系统代码_如何搭建一个基于知识图谱的问答系统(以医疗行业为例)...

    原标题:如何搭建一个基于知识图谱的问答系统(以医疗行业为例) 问答系统(QA)已经成为人类访问十亿级知识图谱的流行方式.与网络搜索不同,在自然语言问题能够被精确地理解和映射到知识图谱上的结构化查询的前 ...

  9. 从入门到进阶|如何基于WebRTC搭建一个视频会议

    文|网易智慧企业流媒体服务器天团 导读:疫情期间,视频会议等远程办公产品备受青睐,众多互联网玩家切入视频会议市场,加剧市场竞争.但是,产品虽多,能够带来稳定可靠体验的产品却凤毛麟角,它的难点在哪里?视 ...

最新文章

  1. 检测、分割用什么轻量级网络呢?
  2. java thread参数传递_将参数传递给Java Thread
  3. 多协议注入工具t50
  4. PHP 手册 参考文档
  5. CVE-2014-7911 Android本地提权漏洞分析与利用
  6. 【DevExpress v17.2新功能预告】增强ASP.NET TreeList
  7. mac下安装brew下载非常慢解决方法
  8. PyG图神经网络框架学习--示例介绍
  9. 【OpenCV】OpenCV实战从入门到精通之 -- 显示创建Mat对象的七种方法
  10. 《战舰世界》携手汉堡王开启“战舰堡胃战”主题活动
  11. mysql passwor authen_mysql5.7 的 user表的密码字段从 password 变成了 authentication_string
  12. 三分钟快速理顺HTMLJSP中单、双引号用法、含义
  13. RTC月度小报5月 |教育aPaaS灵动课堂升级、抢先体验VUE版 Agora Web SDK、声网Agora与HTC达成合作
  14. 什么专业可以留学计算机动画,美国留学计算机动画专业怎么样?
  15. Kubeadm创建集群
  16. dive into openstack ovn (by quqi99)
  17. RAID管理工具 -- MegaCli常用命令
  18. 各类申报:限价申报与市价申报
  19. 企业应用程序集成 无缝集成云和本地应用程序以消除数据孤岛
  20. 使用c 语言的游戏设计软件,游戏制作软件,制作游戏的软件

热门文章

  1. 百度地图API后台持续定位、指南针改变位置问题
  2. 四川星利恒电商:拼多多保证金如何退
  3. Geek Game 1st,叶子的新歌
  4. 关于offsetParent
  5. 返利网java面试_腾讯系统测试面试经验
  6. Istio二之流量劫持过程
  7. 齐次坐标(Homogeneous Coordinate)介绍
  8. mysql如何drop数据库_MySQL如何删除数据库
  9. 图灵程序设计丛书 python_Python深度学习(图灵程序设计丛书)
  10. 森华易腾IDC机房参观有感