Java nio 客户端连接Server

在做通信系统的开发过程中,经常需要使用Socket通信。java新的io机制给我提供了一个很好的异步socket通信方式,这段时间用java写了一个客户端用来连接server。发现运行效率还比较让人满意。下面是我实现的部分功能。

连接服务器的socket,多线程启动。如果连接失败就重连。

public class CommonSocket extends Thread {
	private SocketChannel socketChannel;
	private boolean stop = false;
	private int port = 0;
	private String ip = "";
	private Selector selector = null;
	private SocketAddress socketAddress = null;
	private Logger logger = Logger.getLogger(CommonSocket.class);

	public CommonSocket() {
		this.ip = SocketInfoUtils.TCP_IP;
		this.port = SocketInfoUtils.TCP_PORT;
	}

	public void run() {
		while (!stop) {
			socketConnet();
			try {
				sleep(5000);
			} catch (InterruptedException e) {
				logger.error("SocketConnect run error: InterruptedException");
			}
		}
	}

	public void socketBuilder() {
		try {
			selector = Selector.open();
		} catch (IOException e) {
			e.printStackTrace();
			logger.error("Open to selector failed: IOException");
		}
	}

	private void openSocketChannel() {
		try {
			socketAddress = new InetSocketAddress(ip, port);
			socketChannel = SocketChannel.open();
			socketChannel.socket().setReuseAddress(true);
			socketChannel.connect(socketAddress);
		} catch (ClosedChannelException e) {
			logger.warn("Channel is closed: ClosedChannelException");
		} catch (IOException e) {
			logger
					.warn("Connet is failed or time out,the system will automatically re-connected : IOException");
		}
	}

	/**
	 * do ClientBuilder if socket conncte success
	 */
	public void socketConnet() {
		try {
			openSocketChannel();
			if (socketChannel.isOpen()) {
				this.stop = true;
				socketBuilder();
				socketChannel.configureBlocking(false);
				socketChannel.register(selector, SelectionKey.OP_READ
						| SelectionKey.OP_WRITE);
				PackageBuilder clientBuilder = new PackageBuilder(socketChannel,
						selector);
				clientBuilder.start();
				logger.info("Has been successfully connected to " + ip
						+ "and port:    " + port);
			} else {
				socketChannel.close();
			}
		} catch (ClosedChannelException e) {
			logger.warn("Channel is closed: ClosedChannelException");
		} catch (IOException e) {
			logger
					.warn("Connet is failed or time out,the system will automatically re-connected : IOException");
		}

	}
}

 发送和接收事件处理,NIO是基于事件的驱动模型,这个类就是专门处理收发的。

public class PackageBuilder  extends Thread{
	private SocketChannel socketChannel = null;
	private Selector selector = null;
	private boolean stop = false;
	private byte[] array = new byte[1024];
	private ByteBuffer byteBuffer;
	private PackageQueue packageQueue;
	private Logger logger = Logger.getLogger(PackageBuilder.class);
	
	public PackageBuilder(SocketChannel socketChannel,Selector selectore){
		this.socketChannel = socketChannel;
		this.selector = selectore;
		packageQueue=new PackageQueue();
	}
	public void run(){
		try {
			while (!stop) {
				Thread.sleep(1);
				if(!socketChannel.isOpen()){
					reconnect();//通道没打开或者断开执行重连工作(Channel did not open the work of the implementation of re-connection )
					break;
				}
				if (selector.select(30) > 0) {
					doSelector();
				}
			}
		} catch (IOException e) {
			logger.error("CameraBuilder run error: IOException");
		} catch (InterruptedException e){
			logger.error("CameraBuilder run error: InterruptedException");
		}
	}
	public void doSelector(){
		for(SelectionKey key:selector.selectedKeys()){
			selector.selectedKeys().remove(key);
			if(!key.isValid()){
				continue;
			}
			doKeys(key);
		}
	}
	
	public void doKeys(SelectionKey key){
		SocketChannel channel = (SocketChannel)key.channel();
		if(key.isReadable()){
			readResponse(channel);
		}
		if(key.isWritable()){
			sendRequest(channel);
		}
	}
	private void readResponse(SocketChannel channel) {
		byteBuffer=ByteBuffer.wrap(array);
		byteBuffer.clear();
		int count = 0;
		try {
			count = channel.read(byteBuffer);
		} catch (IOException e) {
			reconnect();//通道没打开或者断开执行重连工作(Channel did not open the work of the implementation of re-connection )
			logger.error("Connection reset by peer: IOException");
		}
		if(count != -1){
			byteBuffer.flip();
			byte[] bs = new byte[count];
			byteBuffer.get(bs);
			ByteBuffer returnBuffer = ByteBuffer.allocate(count);
			returnBuffer.clear();
			returnBuffer.put(bs);
			returnBuffer.flip();
			PrintUtil.printBf(returnBuffer.array());
			ParseBufferData parseData=new ParseBufferData(returnBuffer);		
			parseData.parseBuffer();			
	  }
		if(count < 0){
			reconnect();
		}
	}
	/**
	 * send pakcet of request
	 * @param channel
	 */
	public void sendRequest(SocketChannel channel){
		byte[] array = packageQueue.takeMsgs();
		if(array!=null){
		ByteBuffer byteBuffer = ByteBuffer.wrap(array);
			try {
				channel.write(byteBuffer);
			 } catch (IOException e) {
				 reconnect();//通道没打开或者断开执行重连工作(Channel did not open the work of the implementation of re-connection )
				logger.warn("socket not connected or has been closed: IOException");
			 }
		 }
	}
	
	public void reconnect(){
		stopClient();
		logger.warn("socket not connected or has been closed");
		ThreadPoolUtil.getExecutor().execute(new CameraSocket());
	}
	
	public void stopClient(){
		this.stop = true;
		if(socketChannel.isConnected() && !socketChannel.isOpen()){
			try {
				socketChannel.close();
				logger.info("server_socket has connected");
			} catch (IOException e) {
				logger.warn("Channel closed to failed: IOException");
			}
		}
	}
}

 发送和接收数据存放在缓存中

public class PackageQueue {
	private static  List<byte[]> queue = new ArrayList<byte[]>();
	
	public PackageQueue(){	
	}
	
	public void pushMsgs(byte[] array){
		synchronized(queue){
			queue.add(array);
		}
	}
	
	public byte[] takeMsgs() {
		synchronized (queue) {
			byte[] sd=null;
			if(queue != null){
				if(queue.size() > 0){
					sd = queue.get(0);
					queue.remove(0);
				}
			}
			return sd;
		}
		
	}

	public static List<byte[]> getQueue() {
		return queue;
	}

	public static void setQueue(List<byte[]> queue) {
		PackageQueue.queue = queue;
	}
}

 以上就是客户端连接、发送、接收的代码。希望对大家有所帮助

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值