2019独角兽企业重金招聘Python工程师标准>>>
服务端
package com.ninemax.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
@SuppressWarnings("all")
public class SimpleChatServer {
private static int port = 8080;
public SimpleChatServer(int port) {
this.port = port;
}
public void run() throws Exception {
// 用来处理I/O操作的多线程事件循环器
// 用来接收进来的连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
// 用来处理已经被接收的连接
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 一个启动NIO服务的辅助启动类
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
// 用来接收新的连接
.channel(NioServerSocketChannel.class)
// 目的是帮助使用者配置一个新的Channel
.childHandler(new SimpleChatServerInitializer())
// 提供给NioServerSocketChannel用来接收进来的连接
.option(ChannelOption.SO_BACKLOG, 128)
// 提供给由父管道ServerChannel接收到的连接
.childOption(ChannelOption.SO_KEEPALIVE, true);
System.out.println("server 启动了");
// 绑定端口并启动去接收进来的连接
ChannelFuture f = b.bind(port).sync();
// 这里会一直等待,直到socket被关闭
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("server close!!!");
}
}
public static void main(String[] args) throws Exception {
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new SimpleChatServer(port).run();
}
}
服务端初始化
package com.ninemax.netty;
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;
public class SimpleChatServerInitializer extends ChannelInitializer {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("handler",new SimpleChatServerHandler());
// 远程IP地址 ch.remoteAddress();
System.out.println("client" + ch.remoteAddress() + "连接上");
}
}
服务端处理器
package com.ninemax.netty;
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 SimpleChatServerHandler extends SimpleChannelInboundHandler {
public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
for (Channel channel : channels) {
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 加入\n");
}
channels.add(ctx.channel());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
for(Channel channel : channels){
channel.writeAndFlush("[SERVER] - " + incoming.remoteAddress() + " 离开\n");
}
channels.add(ctx.channel());
}
//优先级高于messageReceived方法,有了这个方法就会屏蔽messageReceived方法
/***
* 这里我们覆盖了chanelRead()事件处理方法。
* 每当从客户端收到新的数据时,
* 这个方法会在收到消息时被调用,
* 这个例子中,收到的消息的类型是ByteBuf
* @param ctx 通道处理的上下文信息
* @param msg 接收的消息
*/
// @Override
// public void channelRead(ChannelHandlerContext ctx, String msg) throws Exception {
// System.out.println("channelRead");
// Channel incoming = ctx.channel();
// for (Channel channel : channels) {
// if (channel != incoming) {
// channel.writeAndFlush("[" + incoming.remoteAddress() + "]" + msg + "\n");
// } else {
// channel.writeAndFlush("server: " + msg + "\n");
// }
// }
// }
/**
* channelActive()方法将会在连接被建立并且准备进行通信时被调用。
*
* @param ctx
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception{
Channel incoming = ctx.channel();
System.out.println("client" + incoming.remoteAddress() + "在线");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
Channel incoming = ctx.channel();
System.out.println("client" + incoming.remoteAddress() + "掉线");
}
@Override
protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception{
Channel incoming = ctx.channel();
for(Channel channel : channels){
if(channel != incoming){
channel.writeAndFlush("[" + incoming.remoteAddress() + "] " + msg + "\n");
}else {
// 缓冲区中数据强行输出
channel.writeAndFlush("server: " + msg + "\n");
}
}
}
/***
* 这个方法会在发生异常时触发
* @param ctx
* @param cause
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
Channel incoming = ctx.channel();
System.out.println("client "+incoming.remoteAddress()+" 异常");
// 当出现异常就关闭连接
cause.printStackTrace();
ctx.close();
}
}
客户端
package com.ninemax.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.util.Scanner;
@SuppressWarnings("all")
public class SimpleChatClient {
// IP
private static String host = "localhost";
// 端口
private static int port = 8080;
// 构造函数
public SimpleChatClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws Exception {
// 用来处理I/O操作的多线程事件循环器
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new SimpleChatClientInitializer());
//用connect()方法代替了bind()方法
Channel channel = bootstrap.connect(host, port).sync().channel();
Scanner sc = new Scanner(System.in);
System.out.println("Please Enter....");
boolean exit = false;
// 输入exit,退出
while(!exit){
String str = sc.next();
channel.writeAndFlush(str + "\r\n");
if(str.equalsIgnoreCase("exit")){
exit = true;
channel.close();
}
}
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
new SimpleChatClient(host, port).run();
}
}
客户端初始化
package com.ninemax.netty;
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;
public class SimpleChatClientInitializer extends ChannelInitializer{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("handler",new SimpleChatClientHandler());
}
}
客户端处理器
package com.ninemax.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class SimpleChatClientHandler extends SimpleChannelInboundHandler {
protected void messageReceived(ChannelHandlerContext ctx, String msg)
throws Exception {
System.out.println(msg);
}
// @Override
// protected void channelRead(ChannelHandlerContext ctx, String msg)
// throws Exception {
// System.out.println(msg);
// }
}
运行结果图
client01
client02
server
流程:
1.服务端SimpleChatServer启动
2.客户端SimpleChatClient启动
2.1.调用SimpleChatServerInitializer类的initChannel()方法
2.2.调用SimpleChatServerHandler类的channelActive()方法
3.当客户端输出的时候,调用SimpleChatServerHandler类messageReceived()方法
4.当客户端再启动一个线程的时候,依然调用第2步,然后会再调用SimpleChatServerHandler类的handlerAdded方法()
5.当客户端停止服务的时候,调用SimpleChatServerHandler类的channelInactive()和handlerRemoved()方法
总结:
服务端调用initChannel(),channelActive(),channelInactive()方法
客户端调用服务端hanlerAdded(),messageReceived(),handlerRemoved()方法