java nio实现非阻塞Socket通信实例

服务器

package com.java.xiong.Net17;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

public class NServer {

	// 用于检测所有的Channel状态的selector
	private Selector selector = null;
	static final int PORT = 30000;
	// 定义实现编码、解码的字符串集对象
	private Charset charse = Charset.forName("GBK");

	public void init() throws IOException {
		selector = Selector.open();
		// 通过open方法来打开一个未绑定的ServerSocketChannel是咧
		ServerSocketChannel server = ServerSocketChannel.open();
		InetSocketAddress isa = new InetSocketAddress("127.0.0.1", PORT);
		// 将该ServerSocketChannel绑定到指定的IP地址
		server.bind(isa);
		// 设置serverSocket已非阻塞方式工作
		server.configureBlocking(false);
		// 将server注册到指定的selector对象
		server.register(selector, SelectionKey.OP_ACCEPT);
		while (selector.select() > 0) {
			// 一次处理selector上的每个选择的SelectionKey
			for (SelectionKey sk : selector.selectedKeys()) {
				// 从selector上已选择的Kye集中删除正在处理的SelectionKey
				selector.selectedKeys().remove(sk);
				// 如果sk对应的Channel包含客户端的连接请求
				if (sk.isAcceptable()) {
					// 调用accept方法接收连接,产生服务器段的SocketChennal
					SocketChannel sc = server.accept();
					// 设置采用非阻塞模式
					sc.configureBlocking(false);
					// 将该SocketChannel注册到selector
					sc.register(selector, SelectionKey.OP_READ);
				}
				// 如果sk对应的Channel有数据需要读取
				if (sk.isReadable()) {
					// 获取该SelectionKey对银行的Channel,该Channel中有刻度的数据
					SocketChannel sc = (SocketChannel) sk.channel();
					// 定义备注执行读取数据源的ByteBuffer
					ByteBuffer buff = ByteBuffer.allocate(1024);
					String content = "";
					// 开始读取数据
					try {
						while (sc.read(buff) > 0) {
							buff.flip();
							content += charse.decode(buff);
						}
						System.out.println("读取的数据:" + content);
						// 将sk对应的Channel设置成准备下一次读取
						sk.interestOps(SelectionKey.OP_READ);
					}
					// 如果捕获到该sk对银行的Channel出现了异常,表明
					// Channel对应的Client出现了问题,所以从Selector中取消
					catch (IOException io) {
						// 从Selector中删除指定的SelectionKey
						sk.cancel();
						if (sk.channel() != null) {
							sk.channel().close();
						}
					}
					// 如果content的长度大于0,则连天信息不为空
					if (content.length() > 0) {
						// 遍历selector里注册的所有SelectionKey
						for (SelectionKey key : selector.keys()) {
							// 获取该key对应的Channel
							Channel targerChannel = key.channel();
							// 如果该Channel是SocketChannel对象
							if (targerChannel instanceof SocketChannel) {
								// 将读取到的内容写入该Channel中
								SocketChannel dest = (SocketChannel) targerChannel;
								dest.write(charse.encode(content));
							}
						}
					}
				}
			}
		}

	}
	
	public static void main(String [] args) throws IOException{
		new NServer().init();
	}

}

客户端

package com.java.xiong.Net17;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Scanner;

public class NClient {
	
	//定义检测Sockethannel的Selector对象
	private Selector  selector=null;
	static final int PORT=30000;
	//定义处理编码的字符集
	private Charset charset=Charset.forName("GBK");
	//客户端SocketChannel
	private SocketChannel sc=null;
	
	public void init() throws IOException{
		selector=Selector.open();
		InetSocketAddress isa=new InetSocketAddress("127.0.0.1", PORT);
		//调用open的静态方法创建连接指定的主机的SocketChannel
		sc=SocketChannel.open(isa);
		//设置该sc已非阻塞的方式工作
		sc.configureBlocking(false);
		//将SocketChannel对象注册到指定的Selector
		sc.register(selector, SelectionKey.OP_READ);
		//启动读取服务器数据端的线程
		new ClientThread().start();
		//创建键盘输入流
		Scanner scan=new Scanner(System.in);
		while(scan.hasNextLine()){
			//读取键盘的输入
			String line=scan.nextLine();
			//将键盘的内容输出到SocketChanenel中
			sc.write(charset.encode(line));
		}
	}
	
	//定义读取服务器端的数据的线程
	private class ClientThread extends Thread{

		@Override
		public void run() {
			try{
				while(selector.select()>0){
					//遍历每个有可能的IO操作的Channel对银行的SelectionKey
					for(SelectionKey sk:selector.selectedKeys()){
						//删除正在处理的SelectionKey
						selector.selectedKeys().remove(sk);
						//如果该SelectionKey对应的Channel中有可读的数据
						if(sk.isReadable()){
							//使用NIO读取Channel中的数据
							SocketChannel sc=(SocketChannel)sk.channel();
							String content="";
							ByteBuffer bff=ByteBuffer.allocate(1024);
							while(sc.read(bff)>0){
								sc.read(bff);
								bff.flip();
								content+=charset.decode(bff);
							}
							//打印读取的内容
							System.out.println("聊天信息:"+content);
							sk.interestOps(SelectionKey.OP_READ);
							
						}
					}
				}
				
			}catch(IOException io){
				io.printStackTrace();
			}
		}
		
	}
	
	public static void main(String [] args) throws IOException{
		new NClient().init();
	}

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小哥、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值