因为产品升级,需要将原生的Java Socket通信改为Netty框架所以学习了Netty,这是一个多人聊天室,就算是第一个小作品吧。
使用的时候先开启服务端,之后开启任意多个客户端,即可实现多人聊天。
此处只需要导入一个netty4.x的jar包即可。
服务器端:
package com.ittuzi;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
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;
public class ChatServer {
public static void main(String[] args) {
ChatServer server=new ChatServer(9996);
server.run();
}
private int port;
public ChatServer(int port) {
this.port = port;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public void run() {
EventLoopGroup bossGroup=new NioEventLoopGroup();
EventLoopGroup workerGroup=new NioEventLoopGroup();
ServerBootstrap serverBootstrap=new ServerBootstrap();
try {
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline=ch.pipeline();
/*
* 网络数据都是二进制数据,但是业务数据一般都不是二进制数据,
* 这时就需要将业务数据转换为二进制,接收的二进制再转换为业务需要的数据
* 业务端String进入网络,成为二进制数据,StringDecoder将二进制转换为字符串
* 业务处理后,StringEncoder将字符串转换为二进制传输出去
*/
//往pipeline链中添加解码器
pipeline.addLast("decoder",new StringDecoder());
//往pipeline链中添加编码器
pipeline.addLast("encoder",new StringEncoder());
//将业务将入pipeline链,解码器和编码器一定要在业务类之前。
pipeline.addLast(new ChatServerHandler());
}
});
System.out.println("服务器端已就绪");
ChannelFuture future=serverBootstrap.bind(port);
future.sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
服务器端业务代码:
package com.ittuzi;
import java.util.ArrayList;
import java.util.List;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ChatServerHandler extends SimpleChannelInboundHandler<String>{
//通道集合,一个通道就是一个客户端,将所有客户端放在一个集合中
private static List<Channel> channels=new ArrayList<Channel>();
//通道就绪,就是客户端连接上了
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel inChannel=ctx.channel();
//在线就把该通道(客户端)添加到集合中
channels.add(inChannel);
System.out.println("[服务器端]:"+inChannel.remoteAddress().toString().substring(1)+"上线");
}
//通道未就绪,就是客户端未连接
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel inChannel=ctx.channel();
//离线就把该客户端踢出集合
channels.remove(inChannel);
System.out.println("[服务器端]:"+inChannel.remoteAddress().toString().substring(1)+"离线");
}
//读取客户端数据
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
//获取给服务器发消息的当前通道
Channel inChannel=ctx.channel();
//读取的数据广播出去,但不能给当前通道
for(Channel channel:channels) {
if(channel!=inChannel) {
channel.writeAndFlush("["+inChannel.remoteAddress().toString().substring(1)+"]"+":"+msg+"\n");
}
}
}
}
客户端:
package com.ittuzi;
import java.util.Scanner;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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;
public class ChatClient {
public static void main(String[] args) {
ChatClient client=new ChatClient("127.0.0.1", 9996);
client.run();
}
private String IP;
private int port;
public String getIP() {
return IP;
}
public void setIP(String iP) {
IP = iP;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public ChatClient(String iP, int port) {
IP = iP;
this.port = port;
}
public void run() {
EventLoopGroup group=new NioEventLoopGroup();
try {
Bootstrap bootstrap=new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline=ch.pipeline();
//解码
pipeline.addLast("decoder",new StringDecoder());
//编码
pipeline.addLast("encoder",new StringEncoder());
//业务处理类
pipeline.addLast(new ChatClientHandler());
}
});
ChannelFuture future=bootstrap.connect(IP, port);
future.sync();
Channel channel=future.channel();
Scanner scanner=new Scanner(System.in);
while(scanner.hasNext()) {
String text=scanner.nextLine();
channel.writeAndFlush(text+"\n\r");
}
channel.closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
group.shutdownGracefully();
}
}
}
客户端业务代码:
package com.ittuzi;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class ChatClientHandler extends SimpleChannelInboundHandler<String>{
private String message;
public String getMessage() {
return message;
}
//读取服务器广播的消息
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
message=msg.trim();
System.out.println(message);
}
}