Netty应用:一个简单的C/S通信模型
作者:互联网
利用Netty实现一个简单的通信模型,看程序:
1.服务器端:
Server类:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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 Server {
public void bind(int port) {
/**
* NioEventLoopGroup 是用来处理I/O操作的多线程事件处理器
* Netty 提供了许多不同的 EventLoopGroup的实现来处理不同的传输
* 本例中实现服务端的应用,因此会有2个NioEventLoopGroup 会被使用
* 一个是main,也叫做boss,用来接收进来的连接
* 一个是work,用来处理已经接受的连接
* 一旦boss接收到连接,就会把信息注册到worker上
* 如何知道多少个线程已经被使用,如何映射到已经创建的channel上都需要依赖于EventLoopGroup的实现
* 并且可以通过构造函数来配置他们的关系
*/
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
NioEventLoopGroup workGroup = new NioEventLoopGroup();
try {
/**
* ServerBootstrap 是一个启动NIO的辅助启动类
* 可以在这个服务中直接使用channel
*/
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workGroup)
//这里指定使用NioServerSocketChannel类来举例说明一个新的channel如何接受进来的连接
.channel(NioServerSocketChannel.class)
// .localAddress(port)
/**
* 这里的事件处理类经常被用来处理一个最近结束的channel
* 匿名内部类 继承自ChannelInitializer 是一个特殊的处理类,他的目的是帮助使用者配置一个新的channel
*/
.childHandler( new ChannelInitializer<SocketChannel>() {
//添加serverHandler到channel的channelPipeline
//通过ServerHandler给每一个新来的Channel初始化
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("handler", new ServerHandler());
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
}
})
//设置TCP协议的请求等待队列
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
//绑定端口,调用sync()同步阻塞方法等待完成
ChannelFuture sync = bootstrap.bind(port).sync();
System.out.println("服务端监听端口:"+port +" 启动成功");
//使用sync方法进行阻塞,等待服务端链路关闭之后main函数退出
sync.channel().closeFuture().sync();
} catch (Exception e) {
} finally {
//释放线程池资源
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("server 关闭了");
}
}
}
ServerHandler类:该类主要负责具体的业务实现
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.net.SocketAddress;
public class ServerHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = ctx.channel();
// 通过channel获取到对应的客户端ip地址
SocketAddress remoteAddress = channel.remoteAddress();
// 获取通道中的信息
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String s1 = new String(bytes, "UTF-8");
System.out.println(remoteAddress+":"+s1);
channel.writeAndFlush("[server recv]"+s1);
}
@Override
public void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress()+"上线了");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println(ctx.channel().remoteAddress()+"下线了");
}
}
2.客户端
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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 java.net.InetSocketAddress;
import java.util.Scanner;
public class Client {
private String host;
private int port;
public Client(String host, int port) {
this.host = host;
this.port = port;
}
public void start(){
// 与服务器端类似
NioEventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("handler", new ClientHandler());
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
}
});
Channel channel = bootstrap.connect().sync().channel();
System.out.println("客户端连接服务器成功");
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\n");
while (scanner.hasNext()) {
String msg = scanner.nextLine();
if ("exit".equals(msg)) {
System.out.println("我下线了");
channel.close();
return;
}
if (" ".equals(msg)) {
continue;
}
channel.writeAndFlush(msg);
}
} catch (Exception e) {
e.getStackTrace();
} finally {
group.shutdownGracefully();
System.out.println("客户端关闭了");
}
}
}
ClientHandler类:主要负责客户端的功能实现
public class ClientHandler extends SimpleChannelInboundHandler<String> {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String s1 = new String(bytes, "UTF-8");
System.out.println(s1);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
// System.out.println("recv:"+msg);
}
}
注:这里有个问题是有时候channelRead()方法中可以读取到通道中的信息,有时候channelRead0()方法会读取到同道中的信息,个人猜测与JDK版本相关,使用时应先试一试哪个方法可用,两个方法的区别在于channelRead()方法获取到的信息需要通过ByteBuf 转化成String型,而channelRead0()方法获取到的直接是Strring型。
3.服务器与客户端的启动
服务器:
public class ServerTest {
public static void main(String[] args) {
Server server = new Server();
server.bind(9999);
}
}
客户端:
public class ClientTest {
public static void main(String[] args) {
Client client = new Client("127.0.0.1", 9999);
client.start();
}
}
标签:Netty,io,netty,通信模型,应用,import,new,public,channel 来源: https://blog.csdn.net/qq_40178464/article/details/90137076