跳到主要内容

这个例子实现的聊天室可以多人在线聊天,SSL加密

SecureChatServer

package run.runnable.securechat;

import io.netty.bootstrap.ServerBootstrap;
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 io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;

/**
* 从{@link}修改的简单SSL聊天服务器。
*/
public final class SecureChatServer {

static final int PORT = Integer.parseInt(System.getProperty("port", "8992"));

public static void main(String[] args) throws Exception {
//SelfSignedCertificate可以生成用于测试的临时自签名证书。
SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
.build();

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
//使用日志框架记录所有事件的{@link ChannelHandler}。
//*默认情况下,所有事件都记录在<tt>DEBUG</tt>级别。
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new SecureChatServerInitializer(sslCtx));

b.bind(PORT).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}

SecureChatServerHandler

package run.runnable.securechat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.net.InetAddress;

/**
*服务端处理通道。
*/
public class SecureChatServerHandler extends SimpleChannelInboundHandler<String> {

static final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

@Override
public void channelActive(final ChannelHandlerContext ctx) {
// 会话安全后,发送问候语并将频道注册到全局频道
// list so the channel received the messages from others.
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
new GenericFutureListener<Future<Channel>>() {
@Override
public void operationComplete(Future<Channel> future) throws Exception {
ctx.writeAndFlush(
"Welcome to " + InetAddress.getLocalHost().getHostName() + " secure chat service!\n");
ctx.writeAndFlush(
"Your session is protected by " +
ctx.pipeline().get(SslHandler.class).engine().getSession().getCipherSuite() +
" cipher suite.\n");

channels.add(ctx.channel());
}
});
}

@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// 将收到的消息发送到除当前频道外的所有频道。
for (Channel c : channels) {
if (c != ctx.channel()) {
c.writeAndFlush("[" + ctx.channel().remoteAddress() + "] " + msg + '\n');
} else {
c.writeAndFlush("[you] " + msg + '\n');
}
}

// 如果客户端已发送“再见”,请关闭连接。
if ("bye".equals(msg.toLowerCase())) {
ctx.close();
}
}

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


SecureChatServerInitializer

package run.runnable.securechat;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;

/**
* 为新通道创建新配置的{@link ChannelPipeline}。
*/
public class SecureChatServerInitializer extends ChannelInitializer<SocketChannel> {

private final SslContext sslCtx;

public SecureChatServerInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}

@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();

// 首先添加SSL处理程序来加密和解密所有内容。
// 在本例中,我们在服务器端使用一个伪造的证书
// 并接受客户端中的任何无效证书。
// 你需要更复杂的东西来识别两者
// 以及在现实世界中的服务器。
pipeline.addLast(sslCtx.newHandler(ch.alloc()));

// 在SSL处理程序之上,添加文本行编解码器。
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));

//将请求的{@link String}编码为{@link ByteBuf}。[出站处理器]
pipeline.addLast(new StringEncoder());

/**
* 将收到的{@link ByteBuf}解码为{@link String}。
* 请注意,此解码器必须与适当的{@link ByteToMessageDecoder一起使用,
* 例如{@link DelimiterBasedFrameDecoder}或{@link linebasedframedframedecoder}
* 如果您使用的是基于流的传输,如TCP/IP。[入站处理器]
*/
pipeline.addLast(new StringDecoder());
// 然后是业务处理
pipeline.addLast(new SecureChatServerHandler());
}
}


SecureChatClient

package run.runnable.securechat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;


public class SecureChatClient {

static final String HOST = System.getProperty("host", "127.0.0.1");
static final int PORT = Integer.parseInt(System.getProperty("port", "8992"));

public static void main(String[] args) throws Exception {
//配置SSL
final SslContext sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();

EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new SecureChatClientInitializer(sslCtx));

// 启动连接尝试。
Channel ch = b.connect(HOST, PORT).sync().channel();

// 从stdin读取命令。
ChannelFuture lastWriteFuture = null;
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (; ; ) {
String line = in.readLine();
if (line == null) {
break;
}

// 将接收到的行发送到服务器。
lastWriteFuture = ch.writeAndFlush(line + "\r\n");

// 如果用户键入“bye”命令,请等待服务器关闭连接。
if ("bye".equals(line.toLowerCase())) {
ch.closeFuture().sync();
break;
}
}
// 在关闭频道之前,请等待所有消息刷新。
if (lastWriteFuture != null) {
lastWriteFuture.sync();
}
} finally {
// 连接在关机时自动关闭。
group.shutdownGracefully();
}
}

}

SecureChatClientHandler

package run.runnable.securechat;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

/**
* 客户端处理通道。
*/
public class SecureChatClientHandler extends SimpleChannelInboundHandler<String> {

@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.err.println(msg);
}

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

SecureChatClientInitializer

package run.runnable.securechat;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.ssl.SslContext;

/**
* 为新通道创建新配置的{@link ChannelPipeline}。
*/
public class SecureChatClientInitializer extends ChannelInitializer<SocketChannel> {

private final SslContext sslCtx;

public SecureChatClientInitializer(SslContext sslCtx) {
this.sslCtx = sslCtx;
}

@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();

//首先添加SSL处理程序来加密和解密所有内容。
//在本例中,我们在服务器端使用一个伪造的证书
//并接受客户端中的任何无效证书。
//你需要更复杂的东西来识别两者
//以及在现实世界中的服务器。
pipeline.addLast(sslCtx.newHandler(ch.alloc(), SecureChatClient.HOST, SecureChatClient.PORT));

// 在SSL处理程序的顶部,添加文本行编解码器。
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new StringDecoder());
pipeline.addLast(new StringEncoder());

// 然后是业务处理
pipeline.addLast(new SecureChatClientHandler());
}
}