這篇博客主要讀述使用netty實現簡單的聊天室功能 ,當然真正的聊天功能絕對不會這麼簡單,說簡單只是相對於JDK原生的NIO模型來說。理解這個demo你需要對NIO和Netty的流程有一定的瞭解。推薦可以去看一下《Scalable IO in JAVA》
話不多說,來看代碼
Server端代碼
package com.patrick.netty.chat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
public class ChatServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workGroup = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
try {
bootstrap.group(bossGroup , workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG , 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast("encoder",new StringEncoder(CharsetUtil.UTF_8))
.addLast("decoder",new StringDecoder(CharsetUtil.UTF_8))
.addLast(new ChatServerHandler());
}
});
System.out.println("---------聊天室服務器已啓動-------------");
ChannelFuture cf = bootstrap.bind(9000).sync();
cf.channel().closeFuture().sync();
} catch (Exception e){
e.printStackTrace();
}finally {
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
package com.patrick.netty.chat;
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.util.concurrent.GlobalEventExecutor;
public class ChatServerHandler extends SimpleChannelInboundHandler<String> {
private static ChannelGroup defaultChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
Channel connectChannel = channelHandlerContext.channel();
for (Channel defaultChannel : defaultChannels) {
if (defaultChannel !=connectChannel) {
defaultChannel.writeAndFlush("客戶端"+connectChannel.remoteAddress()+"發送信息:"+msg);
}else{
defaultChannel.writeAndFlush("服務端"+connectChannel.remoteAddress()+"通知:"+msg);
}
}
System.out.println("客戶端" + connectChannel.remoteAddress() + "發送信息:" + msg);
}
/**
* 監聽客戶端連接情況
* @param ctx
* @throws Exception
*/
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.writeAndFlush("客戶端"+channel.remoteAddress()+"已上線");
defaultChannels.add(channel);
System.out.println("客戶端"+channel.remoteAddress()+"已上線");
//向所有客戶端通知上線信息
for (Channel defaultChannel : defaultChannels) {
if (defaultChannel !=channel) {
defaultChannel.writeAndFlush("客戶端"+channel.remoteAddress()+"已上線");
}
}
}
/**
* 監聽客戶端斷開情況
* @param ctx
* @throws Exception
*/
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.writeAndFlush("客戶端"+channel.remoteAddress()+"下線了");
System.out.println("客戶端"+channel.remoteAddress()+"下線了");
}
/**
* 異常處理
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.fireExceptionCaught(cause);
}
}
Client端
package com.patrick.netty.chat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ChatClient {
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.group(group) //設置線程組
.channel(NioSocketChannel.class) //採用NioSocketChannel作爲客戶端連接通道
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline()
.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8))
.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8))
.addLast(new ChatClientHandler());
}
});
System.out.println("------客戶端已啓動------");
ChannelFuture channelFuture = bootstrap.connect("localhost", 9000).sync();
//獲取channel
Channel channel = channelFuture.channel();
//往channel中寫數據
for (; true; ) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
channel.writeAndFlush(bufferedReader.readLine() + "\r\n");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
package com.patrick.netty.chat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
System.out.println(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}