用Netty手写Http/Https服务器

Netty是一个以事件驱动的异步通信网络框架,可以帮助我们实现多种协议的客户端和服务端通信,话不多说,上代码,需要引入下方依赖

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>

        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>0.6.12</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.4</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.8</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.49</version>
            <type>jar</type>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.49</version>
            <type>jar</type>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>

1.Server

package http;

import constant.Constant;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;

import java.security.cert.CertificateException;

public class HttpServer {
    // 通过nio方式来接收连接和处理连接
    private static EventLoopGroup group = new NioEventLoopGroup();
    
    // 服务端引导类
    private static ServerBootstrap b = new ServerBootstrap();
    
    // 是否开启SSL模式
    public static final boolean SSL = false;

    // Netty创建全部都是实现自AbstractBootstrap,客户端的是Bootstrap,服务端的则是ServerBootstrap
    public static void main(String[] args) throws Exception {
        final SslContext sslContext;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslContext = SslContextBuilder.forServer(ssc.certificate(),
                    ssc.privateKey()).build();
            
        } else {
            sslContext = null;
        }
        
        try {
            b.group(group)
                    .channel(NioServerSocketChannel.class)
                    // 设置过滤器
                    .childHandler(new ServerHandlerInit(sslContext));
            // 异步进行绑定
            ChannelFuture f = b.bind(Constant.DEFAULT_PORT);
            // 给ChannelFuture 增加监听器
            f.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("绑定端口已成功....");
                }
            });
            System.out.println("服务端启动成功,端口是:" + Constant.DEFAULT_PORT);
            System.out.println("服务器启动模式: " + (SSL ? "SSL安全模式" : "普通模式"));
            // 监听服务器关闭监听
            ChannelFuture closeFuture = f.channel().closeFuture().sync();
            closeFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("服务器已经关闭....");
                }
            });
        } finally {
            // 关闭EventLoopGroup,释放掉所有资源,包括创建的线程
            group.shutdownGracefully();
        }
    }
}

package http;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ssl.SslContext;

public class ServerHandlerInit extends ChannelInitializer<SocketChannel> {
    
    private final SslContext sslContext;
    
    public ServerHandlerInit(SslContext sslContext) {
        this.sslContext = sslContext;
    }
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        if (sslContext != null) {
            pipeline.addLast(sslContext.newHandler(ch.alloc()));
        }
        
        // pipeline中的handler可以自定义名称方便排查问题
        // 把应答报文 编码
        pipeline.addLast("encoder", new HttpResponseEncoder());
        // 把请求报文 解码
        pipeline.addLast("decoder", new HttpRequestDecoder());
        
        // 聚合http为一个完整的报文
        pipeline.addLast("aggregator",
                new HttpObjectAggregator(10*1024*1024));
        // 把应答报文压缩
        pipeline.addLast("compressor", new HttpContentCompressor());
        pipeline.addLast(new BusinessHandler());
    }
}

2.业务处理类

package http;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

public class BusinessHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String result = "";
        FullHttpRequest httpRequest = (FullHttpRequest)msg;
        System.out.println(httpRequest.headers());
        try {
            // 获取路径
            String path = httpRequest.uri();
            // 获取body
            String body = httpRequest.content().toString(CharsetUtil.UTF_8);
            // 获取请求方法
            HttpMethod method = httpRequest.method();
            System.out.println("接收到 " + method + "请求");
            // 如果不是这个路径,就直接返回错误
            if (!"/test".equalsIgnoreCase(path)) {
                result = "非法请求!" + path;
                send(ctx,result, HttpResponseStatus.BAD_REQUEST);
                return;
            }
            
            // 如果是GET请求
            if (HttpMethod.GET.equals(method)) {
                // 接收到的消息,做业务处理...
                System.out.println("body :" + body);
                result = "GET请求,应答:" + RespConstant.getNews();
                send(ctx, result, HttpResponseStatus.OK);
                return ;
            }
            
            // 如果是其他类型请求,如post
            if (HttpMethod.POST.equals(method)) {
                // 接收到的消息,做业务逻辑处理
               // ....
                // return;
            }
        } catch (Exception e) {
            System.out.println("处理请求失败!");
            e.printStackTrace();
        } finally {
            // 释放请求
            httpRequest.release();
        }
    }

    private void send(ChannelHandlerContext ctx, String context,
                      HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                status, Unpooled.copiedBuffer(context, CharsetUtil.UTF_8));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=UTF-8");
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    }
    
    // 建立连接时,返回消息
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("连接的客户端地址 :" + ctx.channel().remoteAddress());
//        super.channelActive(ctx);
    }
}

3.Client

package http;

import constant.Constant;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
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.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;

public class HttpClient {

    public static final String HOST  = "127.0.0.1";
    public static void main(String[] args) throws InterruptedException {
        if (HttpServer.SSL) {
            System.out.println("服务器处于SSL模式,客户端不支持,推出");
            return ;
        }
        
        HttpClient client = new HttpClient();
        client.connect(Constant.DEFAULT_SERVER_IP, Constant.DEFAULT_PORT);
    }
    
    public void connect(String host, int port) throws InterruptedException {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            
                            ch.pipeline().addLast(new HttpClientCodec());
                            // 聚合Http为一个完整的报文
                            ch.pipeline().addLast("aggregator",
                                    new HttpObjectAggregator(10 * 1024 * 1024));
                            // 解压缩
                            ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
                            
                            ch.pipeline().addLast(new HttpClientInboundHandler());
                                    
                        }
                    });
            
            // start 
            ChannelFuture f = b.connect(host, port).sync();
            f.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("连接成功....");
                }
            });

            ChannelFuture closeFuture = f.channel().closeFuture().sync();
            closeFuture.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    System.out.println("关闭成功...");
                }
            });
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
    
    
    
}

package http;

import constant.Constant;
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;

public class HttpClientInboundHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        FullHttpResponse httpResponse = (FullHttpResponse) msg;
        System.out.println(httpResponse.status());
        System.out.println(httpResponse.headers());
        ByteBuf buf = httpResponse.content();
        System.out.println(buf.toString(CharsetUtil.UTF_8));
        httpResponse.release();
    }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive.......");
        URI uri = new URI("/test");
        String msg = "Hello";
        DefaultFullHttpRequest request =
                new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
                        HttpMethod.GET,
                        uri.toASCIIString(),
                        Unpooled.wrappedBuffer(msg.getBytes("UTF-8")));
        // 构建http请求
        request.headers().set(HttpHeaderNames.HOST, Constant.DEFAULT_SERVER_IP);
        request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        request.headers().set(HttpHeaderNames.CONTENT_LENGTH, 
                request.content().readableBytes());
        // 发送http请求
        ctx.writeAndFlush(request);
                
//        super.channelActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
//        super.exceptionCaught(ctx, cause);
    }
}

package http;

import java.util.Random;

public class RespConstant {
    private static final String[] NEWS = {
            "她那时候还太年轻,不知道所有命运赠送的礼物,早已在暗中标好了价格。——斯蒂芬·茨威格《断头皇后》",
            "这是一个最好的时代,也是一个最坏的时代;这是一个智慧的年代,这是一个愚蠢的年代;\n" +
                    "这是一个信任的时期,这是一个怀疑的时期;这是一个光明的季节,这是一个黑暗的季节;\n" +
                    "这是希望之春,这是失望之冬;人们面前应有尽有,人们面前一无所有;\n" +
                    "人们正踏上天堂之路,人们正走向地狱之门。 —— 狄更斯《双城记》",
    };
    
    private static final Random R = new Random();
    
    public static String getNews() {
        return NEWS[R.nextInt(NEWS.length)];
    }
}

package constant;

import java.util.Date;

/**
 * 常量
 */
public class Constant {
    
    public static final Integer DEFAULT_PORT = 7777;
    
    public static final String DEFAULT_SERVER_IP= "127.0.0.1";
    
    // 根据输入信息拼接出一个应答信息
    public static String response(String msg) {
        return "Hello, " + msg + ", Now is" + new Date(System.currentTimeMillis()).toString(); 
    }
}

4.总结分析

如果你想实现http请求,需要把HttpServer中的SSL置为false,结果如下
在这里插入图片描述
在这里插入图片描述
如果你想实现Https的请求,则将SSL的变量置为true,目前的代码中是没有支持客户端的SSL请求的,我们可以在postman或者chrome浏览器中查看
https://localhost:7777/test
在这里插入图片描述
由于我们的证书是自己设置的,所以chrome浏览器认为这个证书不是有效的,需要我们手动点击
在这里插入图片描述

IDEA中会出现红色证书错误,暂时可以不用管,你还可以在postman的GET请求中添加body
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
到此http的简易服务器就搭建好了,如果你的程序出现了以下错误,请检查开头的pom配置是否下载成功,这是由于证书有问题才报的错
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/343733.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

BUU BRUTE 1

靶场教程 1.开局页面&#xff0c;是个登录界面。2.尝试万能密码&#xff0c;发现并不可行&#xff0c;提示【用户名错误】。用户名输入admin&#xff0c;发现提示密码错误&#xff0c;为四位数字。3.那么&#xff0c;抓包爆破吧。通过burp进行抓包。4.发送包到 Intruder 进行爆…

网络安全--防御保护---组网实验

实验拓扑图搭建如下&#xff1a; 实验要求&#xff1a; 1.防火墙线下使用子接口分别对应两个内部区域 2.所有分区设备可以ping通网关 一般组网步骤&#xff1a; 1.先配ip&#xff0c;接口&#xff0c;区域&#xff0c;安全策略 2.内网配置回包路由 3.配置dmz区域的服务器…

【每日一题】最长交替子数组

文章目录 Tag题目来源解题思路方法一&#xff1a;双层循环方法二&#xff1a;单层循环 写在最后 Tag 【双层循环】【单层循环】【数组】【2024-01-23】 题目来源 2765. 最长交替子数组 解题思路 两个方法&#xff0c;一个是双层循环&#xff0c;一个是单层循环。 方法一&am…

windows命令行切换目录(cd命令格式)

cd命令格式&#xff1a;cd [/d] 路径名解释&#xff1a;1&#xff0c;在上述格式中&#xff0c;中括号里面的部分表示切换盘符&#xff0c;当需要在不同盘之间进行切换&#xff0c;就需要加上中括号里面的内容。当在同一个盘内进行路径切换&#xff0c;就可以不加中括号内的部分…

Canvas-Editor 实现类似 Word 协同编辑

前言 对于word的协同编辑&#xff0c;已经构思很久了&#xff0c;但是没有找到合适的插件。今天推荐基于canvas/svg 的富文本编辑器 canvas-editor&#xff0c;能实现类似word的基础功能&#xff0c;如果后续有更好的&#xff0c;也会及时更新。 Canvas-Editor 效果图 官方文…

数据结构<1>——树状数组

树状数组&#xff0c;也叫Fenwick Tree和BIT(Binary Indexed Tree)&#xff0c;是一种支持单点修改和区间查询的&#xff0c;代码量小的数据结构。 那神马是单点修改和区间查询&#xff1f;我们来看一道题。 洛谷P3374(模板): 在本题中&#xff0c;单点修改就是将某一个数加上…

(C++)简单计算器

文章目录 一、实验目的、内容二、实验程序设计及结构1.需求分析变量函数 2.设计结构或流程图 三、设计过程四、测试分析第一组第二组实验中出现的bug及解决方案 五、设计的特点和结果 一、实验目的、内容 输入是一个带有括号的四则运算表达式&#xff0c;输出是计算得出的正确…

canvas绘制美国国旗(USA Flag)

查看专栏目录 canvas实例应用100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

kubeadm部署k8s1.27.2版本高可用集群(外部etcd集群带TLS认证)

文章目录 环境软件版本服务器系统初始化etcd 证书生成etcd集群部署负载均衡器部署部署k8s集群部署网络组件FAQ 环境 控制平面节点主机的配置最少是2C2G,否则kubeadm init的时候会报错 主机名IP组件系统os128192.168.177.128etcd、kube-apiserver、kube-controller-manager、k…

Kubernetes/k8s之HPA,命名空间资源限制

Horizontal Pod Autoscaling:po的水平自动伸缩 这是k8s自带的模块 pod占用cpu比例达到一定的阀值&#xff0c;会触发伸缩机制。 根据cpu的阀值触发伸缩机制 replication controller 副本控制器 控制pod的副本数 deployment controller 节点控制器 部署pod hpa控制副本的数…

玩客云Armbian 23.8.1 Bullseye安装PrometheusGrafana

Welcome to Armbian 23.8.1 Bullseye with bleeding edge Linux 6.4.13-edge-meson prometheus 参考Monitoring – How to install Prometheus/Grafana on arm – Raspberry PI/Rock64 | Blogs (mytinydc.com) cd /usr/local/srcwget https://github.com/prometheus/prometh…

Studio One 6 mac 6.5.2 激活版 数字音乐编曲创作

PreSonus Studio One是PreSonus出品的一款功能强大的音乐创作软件。主要为用户提供音乐创作、录音、编辑、制作等功能。它可以让你创造音乐&#xff0c;无限的轨道&#xff0c;无限的MIDI和乐器轨道&#xff0c;虚拟乐器和效果通道&#xff0c;这些都是强大和完美的。 软件下载…

不合格机器人工程讲师再读《悉达多》-2024-

一次又一次失败的经历&#xff0c;让我对经典书籍的认同感越来越多&#xff0c;越来越觉得原来的自己是多么多么的无知和愚昧。 ----zhangrelay 唯物也好&#xff0c;唯心也罢&#xff0c;我们都要先热爱这个世界&#xff0c;然后才能在其中找到自己所热爱的事业。 ----zh…

神经网络的学习(Neural Networks: Learning)

1.代价函数 案例&#xff1a;假设神经网络的训练样本有&#x1d45a;个&#xff0c;每个包含一组输入&#x1d465;和一组输出信号&#x1d466;&#xff0c;&#x1d43f;表示神经网络层数&#xff0c;&#x1d446;&#x1d43c;表示每层的 neuron 个数(&#x1d446;&#…

web安全思维导图(白帽子)

web安全思维导图(白帽子) 客户端脚本安全 服务端应用安全 白帽子讲web安全 安全运营体系建设

‘cnpm‘ 不是内部或外部命令,也不是可运行的程序

一、问题 昨天用npm 安装环境&#xff0c;实在太慢了&#xff0c;就想用cnpm&#xff0c;然后发现提示‘cnpm 不是内部或外部命令,也不是可运行的程序。 看了很多方法&#xff0c;选择了下面这个&#xff0c;运气好到爆棚&#xff0c;就直接可以用了。其他的方法暂未去了解。先…

C++模板与STL【STL概述】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;C从基础到进阶 &#x1f30f;1 STL概述&#x1f349;1.1 STL的诞生&#x1f349;1.2 STL基本概念&#x1f349;1.3 STL六大组件&#x1f349;1.4 STL中容器、算法、迭代器&#x1f349;1.5…

Typecho后台无法登录显示503 service unavailable问题及处理

一、Typecho 我的博客地址&#xff1a;https://www.aomanhao.top 使用老薛主机动态Typecho博客框架handsome主题的搭配&#xff0c;文章内容可以异地网页更新&#xff0c;可以听后台背景音乐&#xff0c;很好的满足我的痛点需求&#xff0c;博客部署在云端服务器访问响应较快…

微信小程序(十二)在线图标与字体的获取与引入

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.从IconFont获取图标与文字的样式链接 2.将在线图标配置进页面中&#xff08;源码&#xff09; 3.将字体配置进页面文字中&#xff08;源码&#xff09; 4.css样式的多文件导入 获取链接 1.获取图标链接 登入…

百度大脑 使用

百度大脑&#xff1a; 官方网址&#xff1a;https://ai.baidu.com/ 文档中心&#xff1a;https://ai.baidu.com/ai-doc 体验中心&#xff1a;https://ai.baidu.com/experience 百度大脑则是百度AI核心技术引擎&#xff0c;它包括基础层、感知层、认知层和安全&#xff0c;是百…