一、下载
Netty是最新的5.0 Alpha2版本,下载地址是:http://dl.bintray.com/netty/downloads/netty-5.0.0.Alpha2.tar.bz2
二、生成自签证书
打开cmd输入
keytool -genkey -alias securechat -keysize 2048 -validity 365 -keyalg RSA -dname "CN=localhost" -keypass sNetty -storepass sNetty -keystore sChat.jks 即可生成一个名为sChat.jks的文件
keytool为JDK提供的生成证书工具
- -keysize 2048 密钥长度2048位(这个长度的密钥目前可认为无法被暴力破解)
- -validity 365 证书有效期365天
- -keyalg RSA 使用RSA非对称加密算法
- -dname "CN=gornix.com" 设置Common Name为gornix.com,这是我的域名
- -keypass 654321 密钥的访问密码为654321
- -storepass 123456 密钥库的访问密码为123456(其实这两个密码也可以设置一样,通常都设置一样,方便记)
- -keystore gornix.jks 指定生成的密钥库文件为gornix.jks
完了之后就拿到了gornix.jks这个密钥库文件了,把它放到自己的home目录下,比如:/home/guogangj/gornix.jks
三、生成SSLContent
keyStore ks = KeyStore.getInstance("JKS"); InputStream ksInputStream = new FileInputStream("/home/guogangj/gornix.jks"); ks.load(ksInputStream, "123456".toCharArray()); keyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(ks, "654321".toCharArray()); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(kmf.getKeyManagers(), null, null);
这个过程在整个程序周期只需要做一次,最好try-catch一下,以便检查异常,好了之后保存好sslContext,后面用到 四、设置netty的ssl
SSLEngine sslEngine = sslContext.createSSLEngine(); sslEngine.setUseClientMode(false); //服务器端模式 sslEngine.setNeedClientAuth(false); //不需要验证客户端 socketChannel.pipeline().addLast("ssl", new SslHandler(sslEngine)); //搞定
sslContext就是前面生成的那个sslContext,通常可以用参数的形式传入到ChannelInitializer中。还有,记得把SslHandler放在其它Handler的前面。 五: 最后放一个创建netty服务器包含ssl以及连接握手的例子; 首先是使用ssl文件生辰sslContentext:
public final class ModbusSslContextFactory {
private static OMCLog log = OMCLogFactory.getOMCLog(ModbusSslContextFactory.class);
private static final SSLContext SERVER_CONTEXT;
private static final String key_file = "server_keystore.jks";
private static final String CERT_ALGORITHM_TYPE = "SunX509";
private static final String KEYSTORE_TYPE = "JKS";
private static final String KEYSTORE_PASS = "NetEco123";
static{
SSLContext sslContext = null;
try{
sslContext = SSLContext.getInstance("SSLv3");
}
catch(NoSuchAlgorithmException e1){
log.error("have Exception:",e1);
}
try{
if(null != getKeyManagers() && null != getTrustManagers()){
sslContext.init(getKeyManagers(), getTrustManagers(), null);
}
}
catch(KeyManagementException e){
log.error("have Exception:",e);
}
catch(IOException e){
log.error("have Exception:",e);
}
catch(GeneralSecurityException e){
log.error("have Exception:",e);
}
sslContext.createSSLEngine().getSupportedCipherSuites();
SERVER_CONTEXT = sslContext;
}
public static SSLContext getServerContext() {
return SERVER_CONTEXT;
}
private ModbusSslContextFactory() {
}
private static FileInputStream getKeyFileIs() throws Exception {
try{
URL keyFileUrl = ModbusSslContextFactory.class.getClassLoader()
.getResource("spring/" + key_file);
FileInputStream is = new FileInputStream(keyFileUrl.getFile());
return is;
}
catch(Exception e){
log.error("have Exception:",e);
throw e;
}
}
private static KeyManager[] getKeyManagers() throws IOException,
GeneralSecurityException {
FileInputStream is = null;
KeyStore ks = null;
KeyManagerFactory kmFact = null;
KeyManager[] kms = null;
try{
// 获得KeyManagerFactory对象.
kmFact = KeyManagerFactory.getInstance(CERT_ALGORITHM_TYPE);
is = getKeyFileIs();
ks = KeyStore.getInstance(KEYSTORE_TYPE);
ks.load(is, KEYSTORE_PASS.toCharArray());
// 使用获得的KeyStore初始化KeyManagerFactory对象
kmFact.init(ks, KEYSTORE_PASS.toCharArray());
// 获得KeyManagers对象
kms = kmFact.getKeyManagers();
}
catch(Exception e){
log.error("have Exception:",e);
}
finally{
if(is != null){
is.close();
}
}
return kms;
}
private static TrustManager[] getTrustManagers() throws IOException,
GeneralSecurityException {
FileInputStream is = null;
TrustManagerFactory kmFact = null;
KeyStore ks = null;
TrustManager[] kms = null;
// 获得KeyManagerFactory对象.
try{
kmFact = TrustManagerFactory.getInstance(CERT_ALGORITHM_TYPE);
// 配置KeyManagerFactory对象使用的KeyStoree.我们通过一个文件加载
// is = new FileInputStream(cert_path);
is = getKeyFileIs();
ks = KeyStore.getInstance(KEYSTORE_TYPE);
ks.load(is, KEYSTORE_PASS.toCharArray());
// 使用获得的KeyStore初始化KeyManagerFactory对象
kmFact.init(ks);
// 获得KeyManagers对象
kms = kmFact.getTrustManagers();
}
catch(Exception e){
log.error("have Exception:",e);
}
finally{
if(is != null){
is.close();
}
}
return kms;
}
}
接着是服务端设置并绑定端口
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))//设置父类handler
.childHandler(new ModbusServerInitializer())//
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.SO_LINGER, 0)
.option(ChannelOption.SO_BACKLOG, 1500)//设置两队列大小
.option(ChannelOption.SO_RCVBUF, 1024 * 256)
.option(ChannelOption.SO_SNDBUF, 1024 * 256)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)//池化内存
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
//绑定端口
b.bind(port).sync();
log.info("init modbus server complete");
} catch(Exception e){
log.error("init modbus server exception:",e);
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
其中的ModbusServerInitializer类如下:
public class ModbusServerInitializer extends ChannelInitializer<SocketChannel>{
private static final OMCLog log = OMCLogFactory
.getOMCLog(ModbusServerInitializer.class);
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//add a ssl filter
if(MedConstant.MODBUS_SSL_ENABLE){
SSLEngine engine = ModbusSslContextFactory.getServerContext().createSSLEngine();
engine.setUseClientMode(false);
engine.setNeedClientAuth(true);
pipeline.addLast("ssl", new SslHandler(engine));
}
pipeline.addLast("idleHandler",new ModbusIdleHandler(MedConstant.MODBUS_SERVER_READIDLE,
MedConstant.MODBUS_SERVER_WRITEIDLE,
MedConstant.MODBUS_SERVER_ALLIDLE));
//pipeline.addLast("decoder", new ModbusMessageDecoder(/*ByteOrder.BIG_ENDIAN,1024*10,5,1,0,0,true*/));
pipeline.addLast("decoder", new LengthDecoder(ByteOrder.BIG_ENDIAN,1024*10,4,2,0,0,true));
pipeline.addLast("encoder", new ModbusMessageEncoder());
pipeline.addLast("handler", new ModbusServerHandler());
//pipeline.addLast("idleHandler", new ModbusIdleHandler(3 * 60, 90, 5 * 60));
}
private static class ModbusIdleHandler extends IdleStateHandler{
public ModbusIdleHandler(int readerIdleTimeSeconds,
int writerIdleTimeSeconds, int allIdleTimeSeconds) {
super(readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds);
}
@Override
protected void channelIdle(ChannelHandlerContext ctx,IdleStateEvent evt) throws Exception {
//log.info("the channl is idle:"+ctx.channel().remoteAddress());
if(IdleStateEvent.class.isAssignableFrom(evt.getClass())){
IdleStateEvent event = (IdleStateEvent) evt;
if(event.state() == IdleState.READER_IDLE){
log.error("the channel is read idle:"+ctx.channel().remoteAddress()+":will be closed");
ctx.close();
}
else if(event.state() == IdleState.WRITER_IDLE){
log.error("the channel is write idle:"+ctx.channel().remoteAddress()+":will be heart beat");
sendHeartBeatFrame(ctx);
}
else if(event.state() == IdleState.ALL_IDLE){
log.error("the channel is all idle");
}
}
}
/**
* 在此线程上做同步操作会阻塞single线程池接收报文,所以用异步
* @param ctx
*/
private void sendHeartBeatFrame(ChannelHandlerContext ctx){
String ip = IecUtils.getIpByChannel(ctx);
ReadAndWriteRegisters rw = new ReadAndWriteRegisters();
if(ip != null && !"".equals(ip)){
rw.readRegisterAsyn(ip, 0, ModbusConstant.HEART_BEAT_ADDRESS, 1);
log.debug("the smart idle heartbeat info is:"+ip);
}
}
}
}
其中包含了设置sslhandler 最后是握手环节,握手是在ModbusServerHandler中,也就是接收消息发送连接创建的业务类:
@Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
if(MedConstant.MODBUS_SSL_ENABLE){
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
new GenericFutureListener<Future<Channel>>() {
//when handshake complete,start search devices
@Override
public void operationComplete(Future<Channel> future) throws Exception {
log.info("the handshake is success: "+future.isSuccess()+";"+future.cause());
if(future.isSuccess()){
// 握手成功后要做的事
}
}
});
}else{
//没有使用ssl就不握手直接是连接建立后。。。
}
}