使用 Netty 实现 HTTP 和 TCP
- Http Client
- HttpClient 客户端
- HttpClientHandler 处理类
- Http Server
- NettyHttpServer 服务端
- HttpServerInitializer 初始化类
- HttpServerRequestHandler 处理类
- Tcp Client
- RpcRequest 实体类
- RpcResponse 实体类
- RpcEncoder 编码
- RpcDecoder 解码
- TcpClient 客户端
- TcpClientInitializer 初始化类
- TcpClientHandler 处理类
- Tcp Server
- NettyTcpServer 服务端
- TcpServerInitializer 初始化类
- TcpServerRequestHandler 处理类
- ThreadPoolConfig 线程池配置类
- springboot 启动类
- 结果
Http Client
HttpClient 客户端
package com.example.nettydemo.eyue.httpClient;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponseDecoder;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
public class HttpClient {
public void connect(String host, int port) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) {
ch.pipeline().addLast(new HttpResponseDecoder());//HTTP编码
ch.pipeline().addLast(new HttpRequestEncoder());//HTTP解码
ch.pipeline().addLast(new HttpClientHandler());//业务处理器
}
});
//建立长连接
ChannelFuture f = b.connect(host, port).sync();
System.out.println("netty http client connected on host(" + host + ") port(" + port + ")");
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new HttpClient().connect("127.0.0.1", 8801);
}
}
HttpClientHandler 处理类
package com.example.nettydemo.eyue.httpClient;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import java.net.URI;
import java.nio.charset.StandardCharsets;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
public class HttpClientHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
//发送http请求
URI uri = new URI("http://127.0.0.1:8800:/test?data=100");
String content = "hello netty http Server!";
DefaultFullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
uri.toASCIIString(), Unpooled.wrappedBuffer(content.getBytes(StandardCharsets.UTF_8)));
// request.headers().set(HttpHeaderNames.HOST, host);
request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
ctx.channel().writeAndFlush(request);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpContent) {
HttpContent content = (HttpContent) msg;
ByteBuf buf = content.content();
System.out.println("收到服务端的消息:" + buf.toString(CharsetUtil.UTF_8));
}
}
}
Http Server
NettyHttpServer 服务端
package com.example.nettydemo.eyue.httpserver;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
@Slf4j
public class NettyHttpServer implements Runnable{
int port ;
public NettyHttpServer(int port){
this.port = port;
}
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
@Override
public void run() {
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss,work)
.handler(new LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel.class)
.childHandler(new HttpServerInitializer());
ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
// System.out.println(" server start up on port : " + port);
if (f.isSuccess()) {
System.out.println("HTTP服务端启动成功");
} else {
System.out.println("HTTP服务端启动失败");
f.cause().printStackTrace();
}
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
// public static void main(String[] args) throws Exception{
// NettyHttpServer server = new NettyHttpServer(8801);// 8081为启动端口
// server.start();
// }
}
HttpServerInitializer 初始化类
package com.example.nettydemo.eyue.httpserver;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
System.out.println("初始化通道: Http server initChannel..");
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new HttpServerCodec());// http 编解码
pipeline.addLast("httpAggregator",new HttpObjectAggregator(512*1024)); // http 消息聚合器 512*1024为接收的最大contentlength
pipeline.addLast(new HttpServerRequestHandler());// 请求处理器
}
}
HttpServerRequestHandler 处理类
// An highlighted block
var foo = 'bar';package com.example.nettydemo.eyue.httpserver;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import java.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
@Slf4j
public class HttpServerRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
//100 Continue
if (HttpUtil.is100ContinueExpected(req)) {
ctx.write(new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.CONTINUE));
}
//返回此通道绑定到的本地地址(IP 地址 和 端口号)
SocketAddress socketAddress = ctx.channel().localAddress();
String str = socketAddress.toString();
System.out.println("ip"+str);
//截取端口号
String str1=str.substring(0, str.indexOf(":"));
String str2=str.substring(str1.length()+1, str.length());
System.out.println("port: "+str2);
// 获取请求的uri
String uri = req.uri();
System.out.println("收到客户端发来的消息"+uri);
Map<String,String> resMap = new HashMap<>();
resMap.put("method",req.method().name());
resMap.put("uri",uri);
String msg = "你请求uri为:" + uri+"";
// 创建http响应
FullHttpResponse response = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.OK,
Unpooled.copiedBuffer(msg, CharsetUtil.UTF_8));
// 设置头信息
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
//response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
// 将html write到客户端
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
//通知处理器最后的channelRead()是当前批处理中的最后一条消息时调用
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务端接收数据完毕..");
ctx.flush();
}
//读操作时捕获到异常时调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
//客户端去和服务端连接成功时触发
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush("hello client");
}
}
Tcp Client
RpcRequest 实体类
package com.example.nettydemo.eyue.entiy;
import lombok.Data;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
@Data
public class RpcRequest {
private String id;
private Object data;
@Override
public String toString() {
return "RpcRequest{" + "id='" + id + '\'' + ", data=" + data + '}';
}
}
RpcResponse 实体类
package com.example.nettydemo.eyue.entiy;
import lombok.Data;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
@Data
public class RpcResponse {
private String id;
private Object data;
private int status;
@Override
public String toString() {
return "RpcResponse{" + "id='" + id + '\'' + ", data=" + data + ", status=" + status + '}';
}
}
RpcEncoder 编码
package com.example.nettydemo.eyue;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
public class RpcEncoder extends MessageToByteEncoder {
//目标对象类型进行编码
private Class<?> target;
public RpcEncoder(Class target) {
this.target = target;
}
@Override
protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
if (target.isInstance(msg)) {
byte[] data = JSON.toJSONBytes(msg); //使用fastJson将对象转换为byte
out.writeInt(data.length); //先将消息长度写入,也就是消息头
out.writeBytes(data); //消息体中包含我们要发送的数据
}
}
}
RpcDecoder 解码
package com.example.nettydemo.eyue;
import com.alibaba.fastjson.JSON;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import java.util.List;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
public class RpcDecoder extends ByteToMessageDecoder {
//目标对象类型进行解码
private Class<?> target;
public RpcDecoder(Class target) {
this.target = target;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (in.readableBytes() < 4) { //不够长度丢弃
return;
}
in.markReaderIndex(); //标记一下当前的readIndex的位置
int dataLength = in.readInt(); // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4
if (in.readableBytes() < dataLength) { //读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
in.resetReaderIndex();
return;
}
byte[] data = new byte[dataLength];
in.readBytes(data);
Object obj = JSON.parseObject(data, target); //将byte数据转化为我们需要的对象
out.add(obj);
}
}
TcpClient 客户端
package com.example.nettydemo.eyue.tcpClient;
import com.example.nettydemo.eyue.entiy.RpcRequest;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.SneakyThrows;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
public class TcpClient {
// 要请求的服务器的ip地址
private String ip;
// 服务器的端口
private int port;
public TcpClient(String ip, int port){
this.ip = ip;
this.port = port;
this.connect(ip,port);
}
@SneakyThrows
public void connect(String host, int port){
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new TcpClientInitializer()); // 初始化
//开启客户端
ChannelFuture f = b.connect(host, port).sync();
System.out.println("向 ip: " + host + " , port:" + port + " 的服务器 发送请求 ");
if(f.isSuccess()){
System.out.println("客户端启动成功");
} else {
System.out.println("客户端启动失败");
f.cause().printStackTrace();
}
//要发送的数据
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setId("100");
rpcRequest.setData("我是 tcp 客户端。。。。。。。。。。。。");
//输出
f.channel().writeAndFlush(rpcRequest);
//等待直到连接中断
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args){
new TcpClient("127.0.0.1", 8800);
}
}
TcpClientInitializer 初始化类
package com.example.nettydemo.eyue.tcpClient;
import com.example.nettydemo.eyue.RpcDecoder;
import com.example.nettydemo.eyue.RpcEncoder;
import com.example.nettydemo.eyue.entiy.RpcRequest;
import com.example.nettydemo.eyue.entiy.RpcResponse;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
public class TcpClientInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel){
System.out.println("初始化通道: Tcp client initChannel..");
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new RpcEncoder(RpcRequest.class));//TCP编码
pipeline.addLast(new RpcDecoder(RpcResponse.class));//TCP解码
pipeline.addLast(new TcpClientHandler());//业务处理器
}
}
TcpClientHandler 处理类
package com.example.nettydemo.eyue.tcpClient;
import com.example.nettydemo.eyue.entiy.RpcResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
public class TcpClientHandler extends SimpleChannelInboundHandler<RpcResponse> {
/**
* 读取 响应的结果
* @param channelHandlerContext
* @param response
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcResponse response) throws Exception {
System.out.println("接受到server响应数据: " + response.toString());
}
// 数据读取完毕的处理
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.err.println("客户端读取数据完毕");
}
// 出现异常的处理
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.err.println("client 读取数据出现异常");
ctx.close();
}
}
Tcp Server
NettyTcpServer 服务端
package com.example.nettydemo.eyue.tcpserver;
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
/**
* @author dong
* @date 2022/3/31
* @AapiNote
*/
@Slf4j
public class NettyTcpServer implements Runnable{
int port ;
public NettyTcpServer(int port){
this.port = port;
}
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup work = new NioEventLoopGroup();
@Override
public void run() {
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss,work)
.handler(new LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel.class)
.childHandler(new TcpServerInitializer());
// .childHandler(new TcpServerInitializer())
ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
// System.out.println(" server start up on port : " + port);
if (f.isSuccess()) {
System.out.println("Tcp服务端启动成功");
} else {
System.out.println("Tcp服务端启动失败");
f.cause().printStackTrace();
}
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
TcpServerInitializer 初始化类
package com.example.nettydemo.eyue.tcpserver;
import com.example.nettydemo.eyue.RpcDecoder;
import com.example.nettydemo.eyue.RpcEncoder;
import com.example.nettydemo.eyue.entiy.RpcRequest;
import com.example.nettydemo.eyue.entiy.RpcResponse;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
public class TcpServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
System.out.println("初始化通道: Tcp server initChannel..");
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new RpcDecoder(RpcRequest.class));
pipeline.addLast(new RpcEncoder(RpcResponse.class));
pipeline.addLast(new TcpServerRequestHandler());
}
}
TcpServerRequestHandler 处理类
package com.example.nettydemo.eyue.tcpserver;
import com.example.nettydemo.eyue.entiy.RpcRequest;
import com.example.nettydemo.eyue.entiy.RpcResponse;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.net.SocketAddress;
import java.util.UUID;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
public class TcpServerRequestHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//返回此通道绑定到的本地地址(IP 地址 和 端口号)
SocketAddress socketAddress = ctx.channel().localAddress();
String str = socketAddress.toString();
System.out.println("ip"+str);
//截取端口号
String str1=str.substring(0, str.indexOf(":"));
String str2=str.substring(str1.length()+1, str.length());
System.out.println("port: "+str2);
/* 接收到 客户端发送来的数据 */
RpcRequest request = (RpcRequest) msg;
System.out.println("接收到客户端信息:" + request.toString());
/* 返回 响应给 客户端 */
RpcResponse response = new RpcResponse();
response.setId(UUID.randomUUID().toString());
response.setData("server响应结果");
response.setStatus(1);
//输出 数据 到 客户端
ctx.writeAndFlush(response);
}
//通知处理器最后的channelRead()是当前批处理中的最后一条消息时调用
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("服务端接收数据完毕..");
ctx.flush();
}
//读操作时捕获到异常时调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
//客户端去和服务端连接成功时触发
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush("hello client");
}
}
ThreadPoolConfig 线程池配置类
package com.example.nettydemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author dong
* @date 2022/4/2
* @AapiNote
*/
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
//设置线程池参数信息
taskExecutor.setCorePoolSize(10);
taskExecutor.setMaxPoolSize(50);
taskExecutor.setQueueCapacity(200);
taskExecutor.setKeepAliveSeconds(60);
taskExecutor.setThreadNamePrefix("myExecutor--");
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
taskExecutor.setAwaitTerminationSeconds(60);
//修改拒绝策略为使用当前线程执行
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//初始化线程池
taskExecutor.initialize();
return taskExecutor;
}
}
springboot 启动类
package com.example.nettydemo;
import com.example.nettydemo.config.ThreadPoolConfig;
import com.example.nettydemo.eyue.httpserver.NettyHttpServer;
import com.example.nettydemo.eyue.tcpserver.NettyTcpServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;
@EnableAsync //开启异步线程
@SpringBootApplication
public class NettyDemoApplication {
public static void main(String[] args) {
SpringApplication.run(NettyDemoApplication.class, args);
ThreadPoolConfig threadPoolConfig = new ThreadPoolConfig();
Executor executor = threadPoolConfig.taskExecutor();
executor.execute(new NettyTcpServer(8800));
executor.execute(new NettyHttpServer(8801));
}
}
结果
HTTP服务端启动成功
Tcp服务端启动成功
初始化通道: Tcp server initChannel..
ip/127.0.0.1:8800
port: 8800
接收到客户端信息:RpcRequest{id='100', data=我是 tcp 客户端。。。。。。。。。。。。}
服务端接收数据完毕..
初始化通道: Http server initChannel..
ip/127.0.0.1:8801
port: 8801
收到客户端发来的消息http://127.0.0.1:8800:/test?data=100