网络编程 netty定时断线、重连

        客户端数量多,且需要传递的数据量级较大。可以周期性的发送数据的时候,使用该机制。要求对数据的即时性不高的时候,才可使用。

        优点是可以使用数据缓存。不是每条数据进行一次数据交互。可以定时回收资源,对资源利用率高。

        对服务端来说,主要是读数据,使用ReadTimeoutHandler类来控制,ReadTimeoutHandler定义一个定时断线处理器,当多长时间内,没有任何的可读取数据,自动断开连接。对客户端来说,则是写,使用的是WriteTimeoutHandler类。

        代码上,在客户端多了一个ChannelFuture的判断,通过channelFuture.channel().isActive()方法来判断是否已经断线了,如果断线,则再次进行服务端的连接。

服务端代码:

public class MyServer {

	// 监听线程组,监听客户端请求
	private EventLoopGroup acceptorGroup = null;
	// 处理客户端相关操作线程组,负责处理与客户端的数据通讯
	private EventLoopGroup clientGroup = null;
	// 服务启动相关配置信息
	private ServerBootstrap bootstrap = null;
	//端口
	private int port;
	
	public MyServer(int port) {
		this.port = port;
		init();
	}
	/**
	 * 初始化
	 */
	private void init() {
		acceptorGroup = new NioEventLoopGroup();
		clientGroup = new NioEventLoopGroup();
		bootstrap = new ServerBootstrap();
		
		// 绑定线程组
		bootstrap.group(acceptorGroup, clientGroup);
		// 设定通讯模式为NIO
		bootstrap.channel(NioServerSocketChannel.class);
		// 设定缓冲区大小
		bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
		// SO_SNDBUF发送缓冲区,SO_RCVBUF接收缓冲区,SO_KEEPALIVE开启心跳监测(保证连接有效)
		bootstrap.option(ChannelOption.SO_SNDBUF, 16*1024)
			.option(ChannelOption.SO_RCVBUF, 16*1024)
			.option(ChannelOption.SO_KEEPALIVE, true);
	}
	/**
	 * 执行监听
	 * @return
	 * @throws InterruptedException
	 */
	public ChannelFuture execAccept() throws InterruptedException {
		bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				// 定义一个定时断线处理器,当多长时间内,没有任何的可读取数据,自动断开连接。
				// 构造参数,就是间隔时长。 默认的单位是秒。
				// 自定义间隔时长单位。 new ReadTimeoutHandler(long times, TimeUnit unit);
				sc.pipeline().addLast(new ReadTimeoutHandler(3));
				sc.pipeline().addLast(new MyServerHandler());
			}
			
		});
		ChannelFuture future = bootstrap.bind(port).sync();
		return future;
	}
	/**
	 * 关闭资源
	 */
	public void release(){
		this.acceptorGroup.shutdownGracefully();
		this.clientGroup.shutdownGracefully();
	}

	public static void main(String[] args) {
		ChannelFuture future = null;
		MyServer server = null;
		try {
			server = new MyServer(8888);
			future = server.execAccept();
			System.out.println("server start...");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			if(future!=null) {
				try {
					future.channel().closeFuture().sync();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			if(server!=null) {
				server.release();
			}
		}
	}

}

public class MyServerHandler extends ChannelHandlerAdapter {

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// ByteBuf - netty提供的类,对NIO中的ByteBuffer进行了包装,使用时不再需要去flip
		ByteBuf buf = (ByteBuf)msg;
		// 开辟有效长度的字节数组大小
		byte[] bytes = new byte[buf.readableBytes()];
		// 将缓存中的数据读取到字节数组中。
		buf.readBytes(bytes);
		String message = new String(bytes,"UTF-8");
		System.out.println("[client message]: " + message);
		String backMsg = "The server receives the message";
		ctx.writeAndFlush(Unpooled.copiedBuffer(backMsg.getBytes("UTF-8")));
	}
	
	/**
	 * 客户端断线,会抛出异常
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.out.println("Client has been disconnected...");
		ctx.close();
	}
}

客户端代码:

public class MyClient {

	// 处理请求和处理服务端响应的线程组
	private EventLoopGroup group = null;
	// 服务启动相关配置信息
	private Bootstrap bootstrap = null;
	// 主机
	private String host;
	// 端口
	private int port;
	
	private ChannelFuture future = null;

	public MyClient(String host,int port) {
		this.host = host;
		this.port = port;
		init();
	}
	
	/**
	 * 初始化
	 */
	private void init() {
		group = new NioEventLoopGroup();
		bootstrap = new Bootstrap();
		// 绑定线程组
		bootstrap.group(group);
		// 设定通讯模式为NIO
		bootstrap.channel(NioSocketChannel.class);
	}
	
	/**
	 * 设置handler
	 */
	public void setHandler() {
		bootstrap.handler(new ChannelInitializer<SocketChannel>() {

			@Override
			protected void initChannel(SocketChannel sc) throws Exception {
				// 写操作自定断线。 在指定时间内,没有写操作,自动断线。
				sc.pipeline().addLast(new WriteTimeoutHandler(3));
				sc.pipeline().addLast(new MyClientHandler());
			}
		});
	}
	/**
	 * 获取future、断线重连
	 * @return
	 * @throws InterruptedException
	 */
	public ChannelFuture getFuture() throws InterruptedException {
		if(future==null) {
			future = bootstrap.connect(host, port).sync();
		}
		else if(!future.channel().isActive()) {
			future = bootstrap.connect(host, port).sync();
		}
		return future;
	}
	/**
	 * 关闭资源
	 */
	public void release(){
		this.group.shutdownGracefully();
	}
	
	public static void main(String[] args) {
		ChannelFuture future = null;
		MyClient client = null;
		
		try {
			client = new MyClient("127.0.0.1",8888);
			client.setHandler();
			future = client.getFuture();
			
			//每2秒发送一次消息,共3次
			for(int i=0;i<3;i++) {
				future.channel().writeAndFlush(Unpooled.copiedBuffer("Client sends message".getBytes("UTF-8")));
				Thread.sleep(2000);
			}
			
			System.out.println("--> After 10 seconds, the client sends the message again");
			//10秒后,再次发送消息
			Thread.sleep(10000);
			future = client.getFuture();
			future.channel().writeAndFlush(Unpooled.copiedBuffer("Client sends new message...".getBytes("UTF-8")));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(future!=null) {
				try {
					future.channel().closeFuture().sync();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			if(client!=null) {
				client.release();
			}
		}
	} 

}


public class MyClientHandler extends ChannelHandlerAdapter {
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		// ByteBuf - netty提供的类,对NIO中的ByteBuffer进行了包装,使用时不再需要去flip
		ByteBuf buf = (ByteBuf)msg;
		// 开辟有效长度的字节数组大小
		byte[] bytes = new byte[buf.readableBytes()];
		// 将缓存中的数据读取到字节数组中。
		buf.readBytes(bytes);
		String message = new String(bytes,"UTF-8");
		System.out.println("[server message]: " + message);
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		System.out.println("client exceptionCaught method run...");
		ctx.close();
	}
}

猜你喜欢

转载自blog.csdn.net/a15123837995/article/details/87358168