因需要https服務器,準備用netty3.x版本搭建一個https服務器,但是怎麼弄都沒有搞好.所以就選了netty4.x版本.(netty3.x最低JDK1.5,netty4.x最低JDK1.6).下面介紹下netty4.x搭建https服務器.
1. HTTPS服務器啓動類
如果想啓動多個服務器可以採用多個線程啓動多個端口號
package best.nettyserver.https;
import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;
public class ActivorHttpsServer {
public static void main(String[] args) {
new ActivorHttpsServer().start(9998);
}
public void start(int port){
// 創建EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup(); //創建BOSS線程組 用於服務端接受客戶端的連接
EventLoopGroup workerGroup = new NioEventLoopGroup(); //創建WORK線程組 用於進行SocketChannel的網絡讀寫
try {
// 創建ServerBootStrap實例
// ServerBootstrap 用於啓動NIO服務端的輔助啓動類,目的是降低服務端的開發複雜度
ServerBootstrap b = new ServerBootstrap();
// 綁定Reactor線程池
b.group(bossGroup, workerGroup)
// 設置並綁定服務端Channel
// 指定所使用的NIO傳輸的Channel
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new HttpsServerInitializer());
System.out.println("接口"+port+"準備就緒");
// 綁定端口,同步等待成功
ChannelFuture future = b.bind(port).sync();
// 等待服務端監聽端口關閉
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 優雅地關閉
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2.初始化服務器參數
配置jks文件路徑,配置jks密鑰的密碼,配置handler處理類
package best.nettyserver.https;
import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
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 io.netty.handler.ssl.SslHandler;
public class HttpsServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline ph = ch.pipeline();
//添加sslhandler
char[] passArray = "999999".toCharArray(); //jks密碼
SSLContext sslContext = SSLContext.getInstance("TLSv1");
KeyStore ks = KeyStore.getInstance("JKS");
//加載keytool 生成的文件
FileInputStream inputStream = new FileInputStream("e:/ysstech.jks");
ks.load(inputStream, passArray);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, passArray);
sslContext.init(kmf.getKeyManagers(), null, null);
inputStream.close();
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
ch.pipeline().addLast(new SslHandler(sslEngine));
//處理http服務的關鍵handler
//一個組合的HttpRequestDecoder和HttpResponseEncoder使得服務器端HTTP實現更容易。
ph.addLast("codec", new HttpServerCodec());
ph.addLast("aggregator", new HttpObjectAggregator(10*1024*1024));
ph.addLast("handler", new HttpServerHandler());// 服務端邏輯處理類
}
}
3. handler處理類
配置post,get請求方法,參數,還可以根據path判斷其路徑
package best.nettyserver.https;
import java.net.InetAddress;
import io.netty.buffer.ByteBuf;
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.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;
public class HttpServerHandler extends ChannelInboundHandlerAdapter {
private String result = "";
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (!(msg instanceof FullHttpRequest)) {
result = "未知請求!";
send(ctx, result, HttpResponseStatus.BAD_REQUEST);
return;
}
FullHttpRequest httpRequest = (FullHttpRequest) msg;
try {
String path = httpRequest.uri(); //獲取路徑 uri路徑,可以根據 這個判斷請求接口
String body = getBody(httpRequest); //獲取參數
HttpMethod method = httpRequest.method();//獲取請求方法
System.out.println("接收到:" + method + " 請求");
//如果是GET請求
if (HttpMethod.GET.equals(method)) {
//接受到的消息,做業務邏輯處理...
System.out.println("body:" + body);
result = "GET請求";
send(ctx, result, HttpResponseStatus.OK);
return;
}
//如果是POST請求
if (HttpMethod.POST.equals(method)) {
//接受到的消息,做業務邏輯處理...
System.out.println("body:" + body);
result = "POST請求";
send(ctx, result, HttpResponseStatus.OK);
return;
}
} catch (Exception e) {
System.out.println("處理請求失敗!");
e.printStackTrace();
} finally {
//釋放請求
httpRequest.release();
}
}
/**
* 獲取body參數
*/
private String getBody(FullHttpRequest request) {
ByteBuf buf = request.content();
return buf.toString(CharsetUtil.UTF_8);
}
/**
* 發送的返回值
*/
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());
ctx.writeAndFlush("客戶端" + InetAddress.getLocalHost().getHostName() + "成功與服務端建立連接! ");
super.channelActive(ctx);
}
}