git地址:https://github.com/lkj41110/netty_dome
/**
* 多人聊天例子服务器
* @author lkj41110
* @version time:2017年1月16日 下午9:54:55
*/
public class ServerMain {
private int port;
public ServerMain(int port) {
this.port = port;
}
public static void main(String[] args) {
new ServerMain(2000).run();
}
public void run() {
EventLoopGroup acceptor = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
bootstrap.group(acceptor, worker);//设置循环线程组,前者用于处理客户端连接事件,后者用于处理网络IO
bootstrap.channel(NioServerSocketChannel.class);//用于构造socketchannel工厂
bootstrap.childHandler(new ServerIniterHandler());//为处理accept客户端的channel中的pipeline添加自定义处理函数
try {
// 服务器绑定端口监听
Channel channel = bootstrap.bind(port).sync().channel();
System.out.println("server strart running in port:" + port);
// 监听服务器关闭监听
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 退出
acceptor.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
服务端启动的时候先要创建一个ServerBootstrap 实例。然后需要绑定内置的传输模式。netty内置了NIO,Epoll,OIO,JVM内部传输的传输模式,分别对应了NioEventLoopGroup , EpollEventLoopGroup , OioEventLoopGroup , LocalEventLoopGroup四个模式。一般都使用NIO,所以我们使用NioEventLoopGroup。
EventLoopGroup acceptor = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
//都是使用了NIO。
bootstrap.group(acceptor, worker);
bootstrap.channel(NioServerSocketChannel.class);
从上面的代码我们看到了bootstrap绑定了两个NioEventLoopGroup(可以看做线程池),这种是Reactor
主从线程模型,用来接受和处理新的连接,并且将Channel的类型指定为NioServer- SocketChannel 。在此之后,你将本地地址设置为一个具有选定端口的 InetSocket- Address 。服务器将绑定到这个地址以监听新的连接请求。
public class ServerIniterHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
pipeline.addLast("decode",new StringDecoder());
pipeline.addLast("encode",new StringEncoder());
pipeline.addLast("chat",new ChatServerHandler());
}
}
ServerIniterHandler里主要是绑定ChannelPipeline链,里面我们设置了StringDecoder,StringEncoder帮助我们接收到的信息未为String类型,并设置了ChatServerHandler业务处理类,服务端的业务处理逻辑主要由ChatServerHandler实现。
/**
* 服务器主要的业务逻辑
*/
public class ChatServerHandler extends SimpleChannelInboundHandler {
//保存所有活动的用户
public static final ChannelGroup group = new DefaultChannelGroup(
GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext arg0, String arg1)
throws Exception {
Channel channel = arg0.channel();
//当有用户发送消息的时候,对其他用户发送信息
for (Channel ch : group) {
if (ch == channel) {
ch.writeAndFlush("[you]:" + arg1 + "\n");
} else {
ch.writeAndFlush(
"[" + channel.remoteAddress() + "]: " + arg1 + "\n");
}
}
System.out.println("[" + channel.remoteAddress() + "]: " + arg1 + "\n");
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
for (Channel ch : group) {
ch.writeAndFlush(
"[" + channel.remoteAddress() + "] " + "is comming");
}
group.add(channel);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
for (Channel ch : group) {
ch.writeAndFlush(
"[" + channel.remoteAddress() + "] " + "is comming");
}
group.remove(channel);
}
//在建立链接时发送信息
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println("[" + channel.remoteAddress() + "] " + "online");
ctx.writeAndFlush("[server]: welcome");
}
//退出链接
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println("[" + channel.remoteAddress() + "] " + "offline");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.out.println(
"[" + ctx.channel().remoteAddress() + "]" + "exit the room");
ctx.close().sync();
}
}
客户端的代码也基本类似与服务端,最大的区别是客户端的启动代码是创建的Bootstrap示例,并且只设置了NioEventLoopGroup一个线程池来处理连接。
/**
* 多人聊天客户端(在服务端开启后开启,可开多个)
* @author lkj41110
* @version time:2017年1月16日 下午9:55:41
*/
public class ClientMain {
private String host;
private int port;
private boolean stop = false;
public ClientMain(String host, int port) {
this.host = host;
this.port = port;
}
public static void main(String[] args) throws IOException {
new ClientMain("127.0.0.1", 2000).run();
}
public void run() throws IOException {
//设置一个worker线程,使用
EventLoopGroup worker = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(worker);
//指定所使用的 NIO 传输 Channel
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ClientIniterHandler());
try {
//使用指定的 端口设置套 接字地址
Channel channel = bootstrap.connect(host, port).sync().channel();
while (true) {
//向服务端发送内容
BufferedReader reader = new BufferedReader(
new InputStreamReader(System.in));
String input = reader.readLine();
if (input != null) {
if ("quit".equals(input)) {
System.exit(1);
}
channel.writeAndFlush(input);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}
}
public class ClientIniterHandler extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel arg0) throws Exception {
ChannelPipeline pipeline = arg0.pipeline();
pipeline.addLast("stringD", new StringDecoder());
pipeline.addLast("stringC", new StringEncoder());
pipeline.addLast("http", new HttpClientCodec());
pipeline.addLast("chat", new ChatClientHandler());
}
}
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext arg0, String arg1)
throws Exception {
//客户端主要用来接收服务器发送的消息
System.out.println(arg1);
}
}
运行结果
//多个用户连接在线。服务端
server strart running in port:2000
[/127.0.0.1:60628] online
[/127.0.0.1:60628]: 123
[/127.0.0.1:60628]: 12312
[/127.0.0.1:60628]: 312312
[/127.0.0.1:60744] online
[/127.0.0.1:60744]: 123123
[/127.0.0.1:60744]: 1231231
[/127.0.0.1:60744] offline
[/127.0.0.1:60628]: 1232131
[/127.0.0.1:60628] offline
//客户端
[server]: welcome
123
[you]:123
12312
[you]:12312
312312
[you]:312312
[/127.0.0.1:60744] is comming
[/127.0.0.1:60744]: 123123
[/127.0.0.1:60744]: 1231231
[/127.0.0.1:60744] is comming
1232131
[you]:1232131
以上是一个简单的netty Dome。其中用到了netty重要的组件(以后会一一分析):