龙空技术网

Java,Netty,实现HTTP/HTTPS服务,WEB静态服务器,代码案例分享

古怪今人 129

前言:

而今你们对“https搭建教程netty”可能比较珍视,咱们都需要了解一些“https搭建教程netty”的相关知识。那么小编在网摘上搜集了一些有关“https搭建教程netty””的相关知识,希望看官们能喜欢,兄弟们一起来学习一下吧!

前言

HTTP编码器、解码器,Netty为HTTP消息提供了ChannelHandler编码器和解码器:

1、HttpRequestEncoder:编码器,用于客户端,向服务器发送请求。

2、HttpResponseEecoder:编码器,用于服务端,向客户端发送响应。

3、HttpResponseDecoder:解码器,用于客户端,接收来自服务端的请求。

4、HttpRequestDecoder:解码器,用于服务端,接收来自客户端的请求。

除独立的编码器、解码器,Netty还提供了其它的编解码器:

1、HttpClientCodec:用于客户端的编解码器,等效于HttpRequestEncoder和HttpResponseDecoder的组合。

2、HttpServerCodec:用于服务端的编解码器,等效于HttpRequsetDecoder和HttpResponseEncoder的组合。

3、HttpServerCodec:实现了ChannelInboundHandler和ChannelOutboundHandler,所以具备服务端编码和解码能力。

HttpObjectAggregator聚合器:

上述解码器会将每个HTTP消息中生成多个消息对象,如:HttpRequest、HttpResponse,HttpContent、LastHttpContent,使用聚合器可以将多个消息体合并为一个FullHttpRequest或者FullHttpResponse消息,便于处理请求和响应,Netty提供了HttpObjectAggregator聚合器完成该工作。

SslHandler

SslHandler的ChannelHandler实现HTTPS报文加密和解密的功能,其中SslHandler在内部使用SSLEngine来完成实际的工作,SSLEngine的实现可以是JDK的SSLEngine,也可以是Netty 的OpenSslEngine,推荐使用Netty的OpenSslEngine,它性能更好,通过SslHandler进行解密和加密的过程。

一般情况下,SslHandler在ChannelPipeline中是第一个ChannelHandler,确保只有在所有其它的ChannelHandler将它们的逻辑应用到数据之后,才会进行加密。

参考:SSL认证,代码案例,单向认证和双向认证的代码分享

实现HTTP服务

package com.what21.netty02.demo01.http;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;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 java.net.InetSocketAddress;public class HttpServer {    private String serverName;    private String bindHost;    private int bindPort;    public HttpServer(String serverName, String bindHost, int bindPort) {        this.serverName = serverName;        this.bindHost = bindHost;        this.bindPort = bindPort;    }    public void start() throws Exception {        // 用于Acceptor的主"线程池"        EventLoopGroup bossEventGroup = new NioEventLoopGroup();        // 初始化==>用于I/O工作的从"线程池"        EventLoopGroup workerEventGroup = new NioEventLoopGroup();        try {            ServerBootstrap serverBootstrap = new ServerBootstrap();            // group方法设置主从线程池            serverBootstrap.group(bossEventGroup, workerEventGroup);            // 指定通道channel类型,服务端为:NioServerSocketChannel            serverBootstrap.channel(NioServerSocketChannel.class);            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);            serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));            serverBootstrap.childHandler(new HttpServerInitializer());            ChannelFuture bindFuture = null;            if ("0.0.0.0".equalsIgnoreCase(this.bindHost)) {                bindFuture = serverBootstrap.bind(this.bindPort).sync();            } else {                InetSocketAddress bindAddress = new InetSocketAddress(this.bindHost, this.bindPort);                bindFuture = serverBootstrap.bind(bindAddress).sync();            }            Channel parentChannel = bindFuture.channel();            bindFuture.addListener(new ChannelFutureListener() {                @Override                public void operationComplete(ChannelFuture channelFuture) throws Exception {                    if (channelFuture.isSuccess()) {                        System.out.println(HttpServer.this.serverName + ",绑定监听成功," + channelFuture.channel().localAddress());                    } else {                        System.err.println(HttpServer.this.serverName + ",绑定监听失败!" + channelFuture.cause());                    }                }            });            ChannelFuture closeFuture = bindFuture.channel().closeFuture().sync();            closeFuture.addListener(new ChannelFutureListener() {                @Override                public void operationComplete(ChannelFuture channelFuture) throws Exception {                    if (channelFuture.isSuccess()) {                        System.out.println(HttpServer.this.serverName + ",停止监听成功," + channelFuture.channel().localAddress());                    } else {                        System.err.println(HttpServer.this.serverName + ",停止监听失败!" + channelFuture.cause());                    }                }            });        } catch (Exception e) {            e.printStackTrace();        } finally {            // 优雅退出,释放"线程池"            if (bossEventGroup != null) {                bossEventGroup.shutdownGracefully();            }            if (workerEventGroup != null) {                workerEventGroup.shutdownGracefully();            }        }    }    public static void main(String[] args) {        try {            new HttpServer("Web服务器","0.0.0.0",8888).start();        } catch (Exception e) {            e.printStackTrace();        }    }}
package com.what21.netty02.demo01.http;import io.netty.channel.ChannelHandler;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;import java.util.Map;public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {    @Override    protected void initChannel(SocketChannel channel) throws Exception {        ChannelPipeline pipeline = channel.pipeline();        // http 编解码        pipeline.addLast(new HttpServerCodec());        // http 消息聚合器  512*1024为接收的最大contentlength        pipeline.addLast("httpAggregator", new HttpObjectAggregator(512 * 1024));        // 请求处理器        pipeline.addLast(new HttpRequestHandler());        // 打印一下,每次http请求都打印        Map<String, ChannelHandler> handlerMap = pipeline.toMap();        for (String key : handlerMap.keySet()) {            System.out.println(key + "=" + handlerMap.get(key));        }    }}
package com.what21.netty02.demo01.http;import io.netty.buffer.ByteBuf;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 java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.net.SocketAddress;import java.util.HashMap;import java.util.Map;public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {    @Override    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {        //100 Continue        if (HttpUtil.is100ContinueExpected(httpRequest)) {            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));        }        // 远程地址        SocketAddress clientSocketAddress = ctx.channel().remoteAddress();        String uri = httpRequest.uri();        if (uri.equals("/")) {            uri = "/index.html";        }        String physicalPath = "D:/Users/www/dist/";        File file = new File(physicalPath + uri);        if (!file.exists()) {            file = new File(physicalPath + "/index.html");        }        try {            byte[] responseBytes = getContent(file.getPath());            ByteBuf content = Unpooled.copiedBuffer(responseBytes);            boolean validateHeaders = false;            FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content, validateHeaders);            ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);        } catch (IOException e) {            e.printStackTrace();            HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR;            FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus);            ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);        }    }    /**     * @param filePath     * @return     * @throws IOException     */    private byte[] getContent(String filePath) throws IOException {        File file = new File(filePath);        long fileSize = file.length();        if (fileSize > Integer.MAX_VALUE) {            return null;        }        FileInputStream fi = new FileInputStream(file);        byte[] buffer = new byte[(int) fileSize];        int offset = 0;        int numRead = 0;        while (offset < buffer.length                && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {            offset += numRead;        }        // 确保所有数据均被读取        if (offset != buffer.length) {            throw new IOException("Could not completely read file " + file.getName());        }        fi.close();        return buffer;    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) {        ctx.flush();    }}
实现HTTP/HTTPS服务,共用端口
package com.what21.netty02.demo01.https;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.*;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 java.net.InetSocketAddress;public class HttpServer {    private String serverName;    private String bindHost;    private int bindPort;    public HttpServer(String serverName, String bindHost, int bindPort) {        this.serverName = serverName;        this.bindHost = bindHost;        this.bindPort = bindPort;    }    public void start() throws Exception {        // 用于Acceptor的主"线程池"        EventLoopGroup bossEventGroup = new NioEventLoopGroup();        // 初始化==>用于I/O工作的从"线程池"        EventLoopGroup workerEventGroup = new NioEventLoopGroup();        try {            ServerBootstrap serverBootstrap = new ServerBootstrap();            // group方法设置主从线程池            serverBootstrap.group(bossEventGroup, workerEventGroup);            // 指定通道channel类型,服务端为:NioServerSocketChannel            serverBootstrap.channel(NioServerSocketChannel.class);            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);            serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));            serverBootstrap.childHandler(new HttpServerInitializer());            ChannelFuture bindFuture = null;            if ("0.0.0.0".equalsIgnoreCase(this.bindHost)) {                bindFuture = serverBootstrap.bind(this.bindPort).sync();            } else {                InetSocketAddress bindAddress = new InetSocketAddress(this.bindHost, this.bindPort);                bindFuture = serverBootstrap.bind(bindAddress).sync();            }            Channel parentChannel = bindFuture.channel();            bindFuture.addListener(new ChannelFutureListener() {                @Override                public void operationComplete(ChannelFuture channelFuture) throws Exception {                    if (channelFuture.isSuccess()) {                        System.out.println(HttpServer.this.serverName + ",绑定监听成功," + channelFuture.channel().localAddress());                    } else {                        System.err.println(HttpServer.this.serverName + ",绑定监听失败!" + channelFuture.cause());                    }                }            });            ChannelFuture closeFuture = bindFuture.channel().closeFuture().sync();            closeFuture.addListener(new ChannelFutureListener() {                @Override                public void operationComplete(ChannelFuture channelFuture) throws Exception {                    if (channelFuture.isSuccess()) {                        System.out.println(HttpServer.this.serverName + ",停止监听成功," + channelFuture.channel().localAddress());                    } else {                        System.err.println(HttpServer.this.serverName + ",停止监听失败!" + channelFuture.cause());                    }                }            });        } catch (Exception e) {            e.printStackTrace();        } finally {            // 优雅退出,释放"线程池"            if (bossEventGroup != null) {                bossEventGroup.shutdownGracefully();            }            if (workerEventGroup != null) {                workerEventGroup.shutdownGracefully();            }        }    }    public static void main(String[] args) {        try {            new HttpServer("Web服务器(https)","0.0.0.0",8443).start();        } catch (Exception e) {            e.printStackTrace();        }    }}
package com.what21.netty02.demo01.https;import com.what21.netty01.demo01.cert2.KeyStoreUtils;import com.what21.netty02.demo01.http.HttpRequestHandler;import io.netty.buffer.ByteBuf;import io.netty.channel.*;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpServerCodec;import io.netty.handler.ssl.SslContext;import io.netty.handler.ssl.SslContextBuilder;import io.netty.handler.ssl.SslHandler;import java.security.PrivateKey;import java.security.cert.Certificate;import java.security.cert.X509Certificate;import java.util.Map;public class HttpServerInitializer extends ChannelInitializer<SocketChannel> {    @Override    protected void initChannel(SocketChannel channel) throws Exception {        ChannelPipeline pipeline = channel.pipeline();        //RuleBasedIpFilter ruleBasedIpFilter = new RuleBasedIpFilter(rule1, rejectAll);        //channel.pipeline().addLast("ipFilter", ruleBasedIpFilter);        // http 编解码        channel.pipeline().addLast("httpServerCodec", new HttpServerCodec());        // http 消息聚合器  512*1024为接收的最大contentlength        channel.pipeline().addLast("httpObjectAggregator", new HttpObjectAggregator(512 * 1024));        // 请求处理器        channel.pipeline().addLast("serverHandler", new HttpRequestHandler());        // HTTPS支持        channel.pipeline().addFirst(new ChannelInboundHandlerAdapter() {            @Override            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {                if (((ByteBuf) msg).getByte(0) == 22) {                    KeyStoreUtils.KeyStoreEntry keyStoreEntry = KeyStoreUtils.readToKeyStoreEntry();                    PrivateKey privateKey = keyStoreEntry.getPrivateKey();                    Certificate certificate = keyStoreEntry.getCertificate();                    SslContext sslContext = SslContextBuilder.forServer(privateKey, (X509Certificate) certificate).build();                    SslHandler sslHandler = sslContext.newHandler(channel.alloc());                    // 将处理 https 的处理程序添加至 HttpServerCodec 之前                    ctx.pipeline().addBefore("httpServerCodec", "sslHandler", sslHandler);                }                ctx.pipeline().remove(this);                super.channelRead(ctx, msg);            }        });        // 打印一下,每次http请求都打印        Map<String, ChannelHandler> handlerMap = pipeline.toMap();        for (String key : handlerMap.keySet()) {            System.out.println(key + "=" + handlerMap.get(key));        }    }}
package com.what21.netty02.demo01.https;import lombok.Data;import sun.misc.BASE64Encoder;import java.io.FileInputStream;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.cert.Certificate;public class KeyStoreUtils {    /**     * @param storePath   秘钥库文件路径     * @param storePasswd 秘钥库密码     * @param alias       证书别名     * @param trustPasswd 证书密码     * @return     * @throws Exception     */    public static KeyStoreEntry readToKeyStoreEntry(String storePath, String storePasswd, String alias, String trustPasswd)            throws Exception {        KeyStore keyStore = KeyStore.getInstance("JKS");        keyStore.load(new FileInputStream(storePath), storePasswd.toCharArray());        PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, trustPasswd.toCharArray());        PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey();        Certificate certificate = keyStore.getCertificate(alias);        BASE64Encoder encoder = new BASE64Encoder();        String privateKeyEncoded = encoder.encode(privateKey.getEncoded());        String publicKeyEncoded = encoder.encode(publicKey.getEncoded());        KeyStoreEntry keyStoreEntry = new KeyStoreEntry();        keyStoreEntry.setKeyStore(keyStore);        keyStoreEntry.setPrivateKey(privateKey);        keyStoreEntry.setPublicKey(publicKey);        keyStoreEntry.setCertificate(certificate);        keyStoreEntry.setPrivateKeyEncoded(privateKeyEncoded);        keyStoreEntry.setPublicKeyEncoded(publicKeyEncoded);        return keyStoreEntry;    }    @Data    public static class KeyStoreEntry {        private KeyStore keyStore;        private PrivateKey privateKey;        private PublicKey publicKey;        private Certificate certificate;        private String privateKeyEncoded;        private String publicKeyEncoded;    }    @Data    public static class KeyStoreParam {        public String storePath = "d:/server.jks";        public String storePasswd = "123456";        public String alias = "server";        public String trustPasswd = "123456";    }    /**     * @param keyStoreParam     * @return     */    public static KeyStoreEntry readToKeyStoreEntry(KeyStoreParam keyStoreParam) {        try {            return readToKeyStoreEntry(keyStoreParam.getStorePath(), keyStoreParam.getStorePasswd(),                    keyStoreParam.getAlias(), keyStoreParam.getTrustPasswd());        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    /**     * @return     */    public static KeyStoreEntry readToKeyStoreEntry() {        String storePath = "d:/server.jks";        String storePasswd = "123456";        String alias = "server";        String trustPasswd = "123456";        try {            return readToKeyStoreEntry(storePath, storePasswd, alias, trustPasswd);        } catch (Exception e) {            e.printStackTrace();        }        return null;    }}
package com.what21.netty02.demo01.https;import io.netty.buffer.ByteBuf;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 java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.net.SocketAddress;public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {    @Override    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest httpRequest) throws Exception {        //100 Continue        if (HttpUtil.is100ContinueExpected(httpRequest)) {            ctx.write(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE));        }        // 远程地址        SocketAddress clientSocketAddress = ctx.channel().remoteAddress();        String uri = httpRequest.uri();        if (uri.equals("/")) {            uri = "/index.html";        }        String physicalPath = "D:/Users/www/dist/";        File file = new File(physicalPath + uri);        if (!file.exists()) {            file = new File(physicalPath + "/index.html");        }        try {            byte[] responseBytes = getContent(file.getPath());            ByteBuf content = Unpooled.copiedBuffer(responseBytes);            boolean validateHeaders = false;            FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, content, validateHeaders);            ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);        } catch (IOException e) {            e.printStackTrace();            HttpResponseStatus responseStatus = HttpResponseStatus.INTERNAL_SERVER_ERROR;            FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, responseStatus);            ctx.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);        }    }    /**     * @param filePath     * @return     * @throws IOException     */    private byte[] getContent(String filePath) throws IOException {        File file = new File(filePath);        long fileSize = file.length();        if (fileSize > Integer.MAX_VALUE) {            return null;        }        FileInputStream fi = new FileInputStream(file);        byte[] buffer = new byte[(int) fileSize];        int offset = 0;        int numRead = 0;        while (offset < buffer.length                && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {            offset += numRead;        }        // 确保所有数据均被读取        if (offset != buffer.length) {            throw new IOException("Could not completely read file " + file.getName());        }        fi.close();        return buffer;    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) {        ctx.flush();    }}

标签: #https搭建教程netty #java请求https #netty注册https服务