Netty基本实现
Netty实现通信的步骤:
1. 创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写。
2. 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等。
3. 创建一个实际处理的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、以及实际处理数据的接口。
4. 绑定端口,执行同步阻塞方法等待服务器端启动即可。
public class ServerHandler extends ChannelHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// ((ByteBuf) msg).release();
//do something
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];//readableBytes()可读数据
buf.readBytes(bytes);
String request = new String(bytes,"utf-8");
System.out.println("Server : " + request);
//写给客户端
String response = "服务器端响应数据:888";
ctx.write(Unpooled.copiedBuffer(response.getBytes())) //write方法会帮助释放消息,可以不用finally释放消息
.addListener(ChannelFutureListener.CLOSE); //监听,把数据发送后主动断开与客户端的连接
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
public class Server {
public static void main(String[] args) throws InterruptedException {
//1.第一个线程组用于接收client端连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
//2.第二个线程组用于实际业务处理
EventLoopGroup workGroup = new NioEventLoopGroup();
//3.创建一个辅助类BootStrap,就是对server进行一系列配置
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast(new ServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture future = b.bind(6666).sync();
//阻塞
future.channel().closeFuture().sync();
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public class ClientHandler extends ChannelHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];//readableBytes()可读数据
buf.readBytes(bytes);
String request = new String(bytes, "utf-8");
System.out.println("Client : " + request);
} finally {
ReferenceCountUtil.release(msg);//释放消息
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
public class Client {
public static void main(String[] args) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture cf = b.connect("127.0.0.1",6666).sync();
//buf
// cf.channel().write(Unpooled.copiedBuffer("777".getBytes()));
// cf.channel().flush();
cf.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
//阻塞
cf.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
TCP粘包、拆包问题
TCP是一个“流”协议,所谓流就是没有界限的遗传数据。TCP底层并不了解上层的业务数据具体的含义,它会跟进TCP缓冲区的实际情况进行包的划分,在业务上,我们一个完整的包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的TCP粘包、拆包问题。
TCP粘包、拆包问题的产生原因:
1. 应用程序write写入的字节大小大于套接口发送缓冲区的大小
2. 进行MSS大小的TCP分段
3. 以太网帧的payload大于MTU进行IP分片
粘包、拆包主流解决方案:
1. 消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格;
2. 在包尾部增加特殊字符进行分割,例如加回车等;
3. 将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理
Netty解决粘包、拆包问题:
1. 分隔符类 DElimiterBasedFrameDecoder(自定义分隔符)
2. FixedLengthFrameDecoder(定长)
public class ServerHandler1 extends ChannelHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel active...");
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String request = (String) msg;
System.out.println("Server: " + request );
String response = "服务器端响应:" + msg + "$_";
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
public class Server1 {
public static void main(String[] args) throws InterruptedException {
//1.创建线程,接收客户端
EventLoopGroup pGroup = new NioEventLoopGroup();
//2.创建线程,用于处理数据传输
EventLoopGroup wGroup = new NioEventLoopGroup();
//3.创建服务器辅助类
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, wGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_SNDBUF, 32*1024)
.option(ChannelOption.SO_RCVBUF, 32*1024)
.option(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//设置特殊分隔符
ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, buf));
//设置字符串形式的编码
sc.pipeline().addLast(new StringDecoder());
sc.pipeline().addLast(new ServerHandler1());
}
});
//4.绑定连接
ChannelFuture cf = b.bind(6666).sync();
//等待服务器监听端口关闭
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
wGroup.shutdownGracefully();
}
}
public class ClientHandler1 extends ChannelHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client active..");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
String response = (String) msg;
System.out.println("client: " + msg);
} finally {
ReferenceCountUtil.release(msg);//释放消息
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class Client1 {
public static void main(String[] args) throws InterruptedException {
//1.创建线程组
EventLoopGroup group = new NioEventLoopGroup();
//2.创建客户端辅助类
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//创建分隔符
ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());
sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
sc.pipeline().addLast(new StringDecoder());
sc.pipeline().addLast(new ClientHandler1());
}
});
//3.连接服务器
ChannelFuture cf = b.connect("127.0.0.1", 6666).sync();
cf.channel().writeAndFlush(Unpooled.wrappedBuffer("bbbb$_".getBytes()));
cf.channel().writeAndFlush(Unpooled.wrappedBuffer("ccc$_".getBytes()));
//等待服务器监听端口关闭
cf.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
public class ServerHandler1 extends ChannelHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("server channel active..");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String request = (String) msg;
System.out.println("server : " + request);
String response = request;
ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class Server1 {
public static void main(String[] args) throws InterruptedException {
//1.创建线程,管理连接
EventLoopGroup pGroup = new NioEventLoopGroup();
//2.创建线程,处理数据传输
EventLoopGroup wGroup = new NioEventLoopGroup();
//3.创建服务端辅助类
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, wGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_SNDBUF, 32*1024)
.option(ChannelOption.SO_RCVBUF, 32*1024)
.option(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//设置定长字符串接收
sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
//设置字符串形式编码
sc.pipeline().addLast(new StringDecoder());
sc.pipeline().addLast(new ServerHandler1());
}
});
//4.绑定端口
ChannelFuture cf = b.bind(6666).sync();
//等待服务器监听端口关闭
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
wGroup.shutdownGracefully();
}
}
public class ClientHandler1 extends ChannelHandlerAdapter{
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("client channel active..");
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
String response = (String) msg;
System.out.println("client: " + msg);
} finally {
ReferenceCountUtil.release(msg);//释放消息
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
public class Client1 {
public static void main(String[] args) throws InterruptedException {
//1.创建线程
EventLoopGroup group = new NioEventLoopGroup();
//2.创建辅助类
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
//设置定长字符串接收
sc.pipeline().addLast(new FixedLengthFrameDecoder(5));
sc.pipeline().addLast(new StringDecoder());
sc.pipeline().addLast(new ClientHandler1());
}
});
//3.连接服务端
ChannelFuture cf = b.connect("127.0.0.1", 6666).sync();
cf.channel().writeAndFlush(Unpooled.wrappedBuffer("aaaabbbb".getBytes()));
cf.channel().writeAndFlush(Unpooled.copiedBuffer("ccccccc".getBytes()));
//等待客户端关闭
cf.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
Netty编解码
可以使用java进行对象序列化,netty去传输,但是java序列化的缺点多,比如java序列化没法跨语言、序列化后码流太大、序列化性能太低等等。
主流的编解码框架:
JBoss的Marshalling包
google的Protobuf
基于Protobuf的Kyro
MessagePack框架
Jboss Marshalling是一个java对象序列化包,对JDK默认的序列化框架进行了优化,但又保持跟java.io.Serializable接口的兼容,同时增加了一些可调的参数和附加特性。
工具类
/**
* 解压缩工具类
*/
public class GzipUtil {
private final static Logger logger = Logger.getLogger(GzipUtil.class);
public static byte[] gzip(byte[] data) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gzipos = null;
byte[] result = null ;
try {
gzipos = new GZIPOutputStream(baos);
gzipos.write(data);
gzipos.finish();
result = baos.toByteArray();
} catch (IOException e) {
logger.error("压缩文件失败!",e);
throw new IOException();
} finally {
if (gzipos != null) {
try {
gzipos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
gzipos = null;
baos = null;
}
return result;
}
public static byte[] ungzip(byte[] data) throws IOException {
ByteArrayInputStream bais = new ByteArrayInputStream(data);
GZIPInputStream gzipis = null;
ByteArrayOutputStream baos = null;
byte[] result = null;
try {
gzipis = new GZIPInputStream(bais);
byte[] buf = new byte[1024];
int num = -1;
baos = new ByteArrayOutputStream();
while ((num = gzipis.read(buf, 0, buf.length)) != -1) {
baos.write(buf, 0, num);
}
result = baos.toByteArray();
baos.flush();
} catch (IOException e) {
logger.error("解压文件失败!",e);
throw new IOException();
} finally {
if (gzipis != null) {
try {
gzipis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
gzipis = null;
bais = null;
baos = null;
}
return result;
}
//测试
public static void main(String[] args) throws Exception {
//文件路径
String readPath = System.getProperty("user.dir") + File.separator + "sources" + File.separator + "001.jpg";
File file = new File(readPath);
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[fis.available()];
fis.read(data);
fis.close();
System.out.println("文件原始大小:" + data.length);
//文件压缩
byte[] result = GzipUtil.gzip(data);
System.out.println("文件压缩后大小:" + result.length);
//文件解压
byte[] ungzipFile = GzipUtil.ungzip(result);
System.out.println("文件解压后大小:" + ungzipFile.length);
//写出文件
String writePath = System.getProperty("user.dir") + File.separator + "receive" + File.separator + "001.jpg";
FileOutputStream fos = new FileOutputStream(writePath);
fos.write(ungzipFile);
fos.close();
}
}
/**
* Marshalling工厂
*/
public final class MarshallingCodeCFactory {
/**
* 创建Jboss Marshalling解码器MarshallingDecoder
* @return MarshallingDecoder
*/
public static MarshallingDecoder buildMarshallingDecoder() {
//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
//创建了MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
//根据marshallerFactory和configuration创建provider
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024 * 1024 * 1);
return decoder;
}
/**
* 创建Jboss Marshalling编码器MarshallingEncoder
* @return MarshallingEncoder
*/
public static MarshallingEncoder buildMarshallingEncoder() {
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
MarshallingEncoder encoder = new MarshallingEncoder(provider);
return encoder;
}
}
Netty
public class Request implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String requestMessage;
private byte[] attachment;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRequestMessage() {
return requestMessage;
}
public void setRequestMessage(String requestMessage) {
this.requestMessage = requestMessage;
}
public byte[] getAttachment() {
return attachment;
}
public void setAttachment(byte[] attachment) {
this.attachment = attachment;
}
}
public class Response implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String responseMessage;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
}
public class ServerHandlerExt extends ChannelHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Req req = (Req) msg;
System.out.println("Server:" + req.getId() + "," + req.getName() + "," + req.getRequestMessage());
byte[] attachment = GzipUtil.gzip(req.getAttachment());
String path = System.getProperty("user.dir") + File.separator + "receive" + File.separator + "001.jpg";
FileOutputStream fos = new FileOutputStream(path);
fos.write(attachment);
fos.close();
Resp resp = new Resp();
resp.setId(req.getId());
resp.setName("resp" + req.getId());
resp.setResponseMessage("响应内容:" + req.getId());
ctx.writeAndFlush(req);//.addListener(ChannelFutureListener.CLOSE)
}
}
public class ServerExt {
public static void main(String[] args) throws InterruptedException {
//创建两个现场
EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup wGroup = new NioEventLoopGroup();
//创建辅助类
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, wGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingFactory.buildMarshallingEncoder());
sc.pipeline().addLast(MarshallingFactory.buildMarshallingDecoder());
sc.pipeline().addLast(new ServerHandlerExt());
}
});
b.bind(6666).channel().closeFuture().sync();
//关闭连接
pGroup.shutdownGracefully();
wGroup.shutdownGracefully();
}
}
public class ClientHandlerExt extends ChannelHandlerAdapter{
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Resp resp = (Resp) msg;
System.out.println("Client:" + resp.getId() + "," + resp.getName() + "," + resp.getResponseMessage());
} finally{
ReferenceCountUtil.release(msg);//释放消息
}
}
}
public class ClientExt {
public static void main(String[] args) throws Exception {
//创建线程
EventLoopGroup group = new NioEventLoopGroup();
//创建工具类
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingFactory.buildMarshallingEncoder());
sc.pipeline().addLast(MarshallingFactory.buildMarshallingDecoder());
sc.pipeline().addLast(new ClientHandlerExt());
}
});
ChannelFuture cf = b.connect("127.0.0.1", 6666).sync();
for (int i = 0; i < 5; i++) {
Req req = new Req();
req.setId("" + i);
req.setName("pro" + i);
req.setRequestMessage("数据信息" + i);
String path = System.getProperty("user.dir") + File.separator + "sources" + File.separator + "001.jpg";
File file = new File(path);
FileInputStream fis = new FileInputStream(file);
byte[] data = new byte[fis.available()];
fis.read(data);
fis.close();
req.setAttachment(GzipUtil.gzip(data));
cf.channel().writeAndFlush(req);
}
cf.channel().closeFuture().sync();
group.shutdownGracefully();
}
}
Netty的UDP协议
UDP协议全称是用户数据报协议,在网络中它与TCP协议一样用于处理数据包,是一种无连接的协议。
public class ServerHandlerExt extends SimpleChannelInboundHandler<DatagramPacket>{
// 谚语列表
private static final String[] DICTIONARY = {
"只要功夫深,铁棒磨成针。",
"旧时王谢堂前燕,飞入寻常百姓家。",
"洛阳亲友如相问,一片冰心在玉壶。",
"一寸光阴一寸金,寸金难买寸光阴。",
"老骥伏枥,志在千里。烈士暮年,壮心不已!"
};
private String nextQuote(){
int quoteId = ThreadLocalRandom.current().nextInt(DICTIONARY.length);
return DICTIONARY[quoteId];
}
protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
String req = packet.content().toString(CharsetUtil.UTF_8);
System.out.println(req);
if ("谚语字典查询?".equals(req)) {
ctx.writeAndFlush(
new DatagramPacket(Unpooled.copiedBuffer("谚语查询结果:"+ nextQuote(),
CharsetUtil.UTF_8),packet.sender()));
}
}
}
public class ServerExt {
public void run(int port) throws InterruptedException{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioDatagramChannel.class)
.option(ChannelOption.SO_BROADCAST, true)
.handler(new ServerHandlerExt());
b.bind(port).sync().channel().closeFuture().await();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new ServerExt().run(6666);
new ServerExt().run(8888);
}
}
public class ClientHandlerExt extends SimpleChannelInboundHandler<DatagramPacket>{
@Override
protected void messageReceived(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
String response = packet.content().toString(CharsetUtil.UTF_8);
if (response.startsWith("谚语查询结果:")) {
System.out.println(response);
ctx.close();
}
}
}
public class ClientExt {
public void run(int port) throws InterruptedException{
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioDatagramChannel.class).option(ChannelOption.SO_BACKLOG, 1024)
.handler(new ClientHandlerExt());
Channel channel = b.bind(0).sync().channel();
// 向网段内的所有机器广播UDP消息
channel.writeAndFlush(new DatagramPacket(Unpooled.copiedBuffer("谚语字典查询?", CharsetUtil.UTF_8),
new InetSocketAddress("255.255.255.255", port))).sync();
if (!channel.closeFuture().await(15000)) {
System.out.println("查询超时!");
}
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new ClientExt().run(6666);
}
}
Netty的WebSocket
webSocket将网络套接字引入到客户端和服务端,WebSocke特点:
- 单一的tcp连接,双方可通信
- 对代理、防火墙和路由器透明
- 无头部信息、Cookie和身份验证
- 无安全开销
- 通过ping/pong帧保持链路激活
- 服务器可主动传递消息给客户端,不再需要客户端轮询
网络超时设置
public class Server {
public static void main(String[] args) throws Exception{
EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup cGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5)); //网络读写超时设置
sc.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture cf = b.bind(8765).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
public class Client {
private static class SingletonHolder {
static final Client instance = new Client();
}
public static Client getInstance(){
return SingletonHolder.instance;
}
private EventLoopGroup group;
private Bootstrap b;
private ChannelFuture cf ;
private Client(){
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ClientHandler());
}
});
}
public void connect(){
try {
this.cf = b.connect("127.0.0.1", 8765).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
} catch (Exception e) {
e.printStackTrace();
}
}
public ChannelFuture getChannelFuture(){
if(this.cf == null){
this.connect();
}
if(!this.cf.channel().isActive()){
this.connect();
}
return this.cf;
}
public static void main(String[] args) throws Exception{
final Client c = Client.getInstance();
//c.connect();
ChannelFuture cf = c.getChannelFuture();
for(int i = 1; i <= 3; i++ ){
Request request = new Request();
request.setId("" + i);
request.setName("pro" + i);
request.setRequestMessage("数据信息" + i);
cf.channel().writeAndFlush(request);
TimeUnit.SECONDS.sleep(4);
}
cf.channel().closeFuture().sync();
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("进入子线程...");
ChannelFuture cf = c.getChannelFuture();
System.out.println(cf.channel().isActive());
System.out.println(cf.channel().isOpen());
//再次发送数据
Request request = new Request();
request.setId("" + 4);
request.setName("pro" + 4);
request.setRequestMessage("数据信息" + 4);
cf.channel().writeAndFlush(request);
cf.channel().closeFuture().sync();
System.out.println("子线程结束.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
System.out.println("断开连接,主线程结束..");
}
}