使用 Netty 实现 HTTP 和 TCP

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
  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值