概述
保持客户端与服务器端连接的方案常用的有3种
1.长连接,也就是客户端与服务器端一直保持连接,适用于客户端比较少的情况。
2.定时段连接,比如在某一天的凌晨建立连接,适用于对实时性要求不高的情况。
3.设置连接超时,比如超过1分钟没有传输数据就断开连接,等下次需要的时候再建立连接,这种方案比较常用。
netty的ReadTimeOut实现方案3
服务端
大部分代码都保持不变,有变化的代码在第30行,设置服务端的超时时间
1 import io.netty.bootstrap.ServerBootstrap;
2 import io.netty.channel.ChannelFuture;
3 import io.netty.channel.ChannelInitializer;
4 import io.netty.channel.ChannelOption;
5 import io.netty.channel.EventLoopGroup;
6 import io.netty.channel.nio.NioEventLoopGroup;
7 import io.netty.channel.socket.SocketChannel;
8 import io.netty.channel.socket.nio.NioServerSocketChannel;
9 import io.netty.handler.logging.LogLevel;
10 import io.netty.handler.logging.LoggingHandler;
11 import io.netty.handler.timeout.ReadTimeoutHandler;
12
13 public class Server {
14
15 public static void main(String[] args) throws Exception{
16
17 EventLoopGroup pGroup = new NioEventLoopGroup();
18 EventLoopGroup cGroup = new NioEventLoopGroup();
19
20 ServerBootstrap b = new ServerBootstrap();
21 b.group(pGroup, cGroup)
22 .channel(NioServerSocketChannel.class)
23 .option(ChannelOption.SO_BACKLOG, 1024)
24 //设置日志
25 .handler(new LoggingHandler(LogLevel.INFO))
26 .childHandler(new ChannelInitializer<SocketChannel>() {
27 protected void initChannel(SocketChannel sc) throws Exception {
28 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
29 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
30 sc.pipeline().addLast(new ReadTimeoutHandler(5));
31 sc.pipeline().addLast(new ServerHandler());
32 }
33 });
34
35 ChannelFuture cf = b.bind(8765).sync();
36
37 cf.channel().closeFuture().sync();
38 pGroup.shutdownGracefully();
39 cGroup.shutdownGracefully();
40
41 }
42 }
ServerHandler代码也没有什么变化
1 import io.netty.channel.ChannelHandlerAdapter;
2 import io.netty.channel.ChannelHandlerContext;
3
4 public class ServerHandler extends ChannelHandlerAdapter{
5
6 @Override
7 public void channelActive(ChannelHandlerContext ctx) throws Exception {
8
9 }
10
11 @Override
12 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
13 Request request = (Request)msg;
14 System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getRequestMessage());
15 Response response = new Response();
16 response.setId(request.getId());
17 response.setName("response" + request.getId());
18 response.setResponseMessage("响应内容" + request.getId());
19 ctx.writeAndFlush(response);//.addListener(ChannelFutureListener.CLOSE);
20 }
21
22 @Override
23 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
24
25 }
26
27 @Override
28 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
29 ctx.close();
30 }
31
32
33
34 }
客户端
客户端的代码也设置了超时时间(实际上只要服务器端设置也就可以了,有人说客户端不设置会出问题,现在还没有发现什么问题)。主要看getChannelFuture这个方法,this.cf == null是第一次连接的时候用到的,!this.cf.channel().isActive() 是连接超时后重新发起连接用到的。再看main方法,可以发现for(int i = 1; i <= 3; i++ ) 这个循环中,每个循环停顿4秒,也就是每隔4秒发送一次请求,而服务器端的超时时间设置为5秒,那么在这个for循环期间连接是不会断开的,等for循环结束 cf.channel().closeFuture().sync(); 断开连接this.cf.channel().isActive() 变为否,在new Thread()中再次发送请求,getChannelFuture会重新建立连接。
1 import io.netty.bootstrap.Bootstrap;
2 import io.netty.channel.ChannelFuture;
3 import io.netty.channel.ChannelInitializer;
4 import io.netty.channel.EventLoopGroup;
5 import io.netty.channel.nio.NioEventLoopGroup;
6 import io.netty.channel.socket.SocketChannel;
7 import io.netty.channel.socket.nio.NioSocketChannel;
8 import io.netty.handler.logging.LogLevel;
9 import io.netty.handler.logging.LoggingHandler;
10 import io.netty.handler.timeout.ReadTimeoutHandler;
11
12 import java.util.concurrent.TimeUnit;
13
14
15 /**
16 * Best Do It
17 */
18 public class Client {
19
20 private static class SingletonHolder {
21 static final Client instance = new Client();
22 }
23
24 public static Client getInstance(){
25 return SingletonHolder.instance;
26 }
27
28 private EventLoopGroup group;
29 private Bootstrap b;
30 private ChannelFuture cf ;
31
32 private Client(){
33 group = new NioEventLoopGroup();
34 b = new Bootstrap();
35 b.group(group)
36 .channel(NioSocketChannel.class)
37 .handler(new LoggingHandler(LogLevel.INFO))
38 .handler(new ChannelInitializer<SocketChannel>() {
39 @Override
40 protected void initChannel(SocketChannel sc) throws Exception {
41 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
42 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
43 //超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
44 sc.pipeline().addLast(new ReadTimeoutHandler(5));
45 sc.pipeline().addLast(new ClientHandler());
46 }
47 });
48 }
49
50 public void connect(){
51 try {
52 this.cf = b.connect("127.0.0.1", 8765).sync();
53 System.out.println("远程服务器已经连接, 可以进行数据交换..");
54 } catch (Exception e) {
55 e.printStackTrace();
56 }
57 }
58
59 public ChannelFuture getChannelFuture(){
60
61 if(this.cf == null){
62 this.connect();
63 }
64 if(!this.cf.channel().isActive()){
65 this.connect();
66 }
67
68 return this.cf;
69 }
70
71 public static void main(String[] args) throws Exception{
72 final Client c = Client.getInstance();
73 //c.connect();
74
75 ChannelFuture cf = c.getChannelFuture();
76 for(int i = 1; i <= 3; i++ ){
77 Request request = new Request();
78 request.setId("" + i);
79 request.setName("pro" + i);
80 request.setRequestMessage("数据信息" + i);
81 cf.channel().writeAndFlush(request);
82 TimeUnit.SECONDS.sleep(4);
83 }
84
85 cf.channel().closeFuture().sync();
86
87
88 new Thread(new Runnable() {
89 @Override
90 public void run() {
91 try {
92 System.out.println("进入子线程...");
93 ChannelFuture cf = c.getChannelFuture();
94 System.out.println(cf.channel().isActive());
95 System.out.println(cf.channel().isOpen());
96
97 //再次发送数据
98 Request request = new Request();
99 request.setId("" + 4);
100 request.setName("pro" + 4);
101 request.setRequestMessage("数据信息" + 4);
102 cf.channel().writeAndFlush(request);
103 cf.channel().closeFuture().sync();
104 System.out.println("子线程结束.");
105 } catch (InterruptedException e) {
106 e.printStackTrace();
107 }
108 }
109 }).start();
110
111 System.out.println("断开连接,主线程结束..");
112
113 }
114
115
116
117 }
clientHandler没有什么变化
1 import io.netty.channel.ChannelHandlerAdapter;
2 import io.netty.channel.ChannelHandlerContext;
3 import io.netty.util.ReferenceCountUtil;
4
5 public class ClientHandler extends ChannelHandlerAdapter{
6
7 @Override
8 public void channelActive(ChannelHandlerContext ctx) throws Exception {
9
10 }
11
12 @Override
13 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
14 try {
15 Response resp = (Response)msg;
16 System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());
17 } finally {
18 ReferenceCountUtil.release(msg);
19 }
20 }
21
22 @Override
23 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
24
25 }
26
27 @Override
28 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
29 ctx.close();
30 }
31
32 }
工厂类不变
1 import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
2 import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
3 import io.netty.handler.codec.marshalling.MarshallerProvider;
4 import io.netty.handler.codec.marshalling.MarshallingDecoder;
5 import io.netty.handler.codec.marshalling.MarshallingEncoder;
6 import io.netty.handler.codec.marshalling.UnmarshallerProvider;
7
8 import org.jboss.marshalling.MarshallerFactory;
9 import org.jboss.marshalling.Marshalling;
10 import org.jboss.marshalling.MarshallingConfiguration;
11
12 /**
13 * Marshalling工厂
14 * @author(alienware)
15 * @since 2014-12-16
16 */
17 public final class MarshallingCodeCFactory {
18
19 /**
20 * 创建Jboss Marshalling解码器MarshallingDecoder
21 * @return MarshallingDecoder
22 */
23 public static MarshallingDecoder buildMarshallingDecoder() {
24 //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
25 final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
26 //创建了MarshallingConfiguration对象,配置了版本号为5
27 final MarshallingConfiguration configuration = new MarshallingConfiguration();
28 configuration.setVersion(5);
29 //根据marshallerFactory和configuration创建provider
30 UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
31 //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
32 MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
33 return decoder;
34 }
35
36 /**
37 * 创建Jboss Marshalling编码器MarshallingEncoder
38 * @return MarshallingEncoder
39 */
40 public static MarshallingEncoder buildMarshallingEncoder() {
41 final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
42 final MarshallingConfiguration configuration = new MarshallingConfiguration();
43 configuration.setVersion(5);
44 MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
45 //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
46 MarshallingEncoder encoder = new MarshallingEncoder(provider);
47 return encoder;
48 }
49 }
自定义的Request和Response对象没有什么变化,这里不再赘述