网络编程 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();
	}
}

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个基于Netty的socket线连的代码片段: ```java public class NettyClient { private Bootstrap bootstrap; private EventLoopGroup group; private Channel channel; private String host; private int port; public NettyClient(String host, int port) { this.host = host; this.port = port; this.bootstrap = new Bootstrap(); this.group = new NioEventLoopGroup(); this.bootstrap.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 添加自定义的handler pipeline.addLast(new MyHandler()); } }); } public void start() { try { ChannelFuture future = bootstrap.connect(host, port).sync(); channel = future.channel(); System.out.println("连接成功"); channel.closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { group.shutdownGracefully(); System.out.println("开连接"); // 线连 reconnect(); } } private void reconnect() { while (true) { try { Thread.sleep(5000); ChannelFuture future = bootstrap.connect(host, port).sync(); channel = future.channel(); System.out.println("新连接成功"); channel.closeFuture().sync(); break; } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 在上述代码中,我们创建了一个NettyClient类,其中包含了一个start()方法和一个reconnect()方法。在start()方法中,我们首先使用bootstrap.connect()方法连接到指定的主机和端口,然后等待连接成功。如果连接成功,我们就可以使用channel对象进行数据传输。当连接开时,我们会进入finally块,关闭连接并调用reconnect()方法进行线连。在reconnect()方法中,我们使用while循环不尝试新连接,直到连接成功为止。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值