文档章节

基于java底层api实现的nio

一不留神
 一不留神
发布于 2017/04/07 16:25
字数 1161
阅读 10
收藏 2

服务器端

package nio;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class Server {
	
	private static ByteBuffer recvBuf = ByteBuffer.allocate(1024);
	
	private static ByteBuffer respBuf = ByteBuffer.allocate(1024);

	private static Map<SelectionKey,Object> sessionMessage = new ConcurrentHashMap<SelectionKey,Object>();
	public static void main(String[] args) throws IOException {
		//多路复用器
		Selector selector = Selector.open();
		
		//创建server
		ServerSocketChannel server = ServerSocketChannel.open();
		server.socket().bind(new InetSocketAddress(9999));//绑定端口
		server.configureBlocking(false);//设置非阻塞
		
		//server上注册一个多路复用器  事件标签OP_ACCEPT接收客户端连接 这样多路复用器将不断轮询server,当有客户端连接过来,就会被轮询出来  
		server.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("服务器已经启动");
		//启动监听
		listener(server, selector);
		
	}
	
	
	public static void listener(ServerSocketChannel server, Selector selector ) throws IOException{
		while (true) {
			int eventCount = selector.select();//如果有客户端连接过来,就会被轮询出来
			if (eventCount <= 0) {//小于等于0,说明没有客户端接入
				continue;
			}
			Set<SelectionKey> keys = selector.selectedKeys();//获取有事件触发的selectkey
			Iterator<SelectionKey> iter = keys.iterator();
			while (iter.hasNext()) {
				SelectionKey key = iter.next();
				//处理事件
				try {
					process(server,selector,key);
				} catch (IOException e) {//客户端异常关闭
					close(key);
				}
				iter.remove();//处理完移除这个事件
			}
		}
	}
	
	public static void process(ServerSocketChannel server,  Selector selector ,SelectionKey key) throws IOException{
		SocketChannel client = null;
		if (key.isValid() && key.isAcceptable() ) {//事件是否是有效的
			client = server.accept();//有客户端接入,把客户端连接通道也注册到多路复用器上
			
			client.configureBlocking(false);//非阻塞的
			client.register(selector, SelectionKey.OP_READ);//客户端连接过来,注册读事件
		}else if(key.isValid() && key.isReadable()){//可读的,说明注册在多路复用上的客户端有数据发过来了
			recvBuf.clear();//清空缓冲区
			client = (SocketChannel) key.channel();//通过key获取到客户端,这个客户端是在Acceptable时连接过来的客户端
			int length = client.read(recvBuf);//将数据读到缓冲区
			if (length > 0) {//读取到内容的时候
				String message = new String(recvBuf.array(),0,length);
				sessionMessage.put(key, message);//将数据放到sessionmessage中
				key.interestOps(SelectionKey.OP_WRITE);//读完数据后,这个客户端在多路复用器上的事件由read变为write,可以写了
			}else{
				if (client.isConnected()) {
					System.out.println("客户端关闭");
					close(key);
				}
			}
		}else if(key.isValid() && key.isWritable()){//可写的,说明注册在多路复用上的客户端数据已经接收完了,可以往客户端写数据了
			if (!sessionMessage.containsKey(key)) {
				return;
			}
			//获取到客户端发送过来的数据
			client = (SocketChannel) key.channel();
			Object resp = handler(sessionMessage.get(key));//将读取到的数据暴露给用户处理,得到响应数据
			respBuf.clear();
			respBuf.put((ByteBuffer)resp);
			respBuf.flip();
			client.write(respBuf);//响应客户端
			key.interestOps(SelectionKey.OP_READ);//写完数据后,这个客户端在多路复用器上的事件又由write变为read,可以读了
		}else{
			client = server.accept();//有客户端接入,把客户端连接通道也注册到多路复用器上
			System.out.println("连接状态::"+client.isConnected());
		}
	}
	
	public static ByteBuffer handler(Object message){
		System.out.println("来自客户端的消息:"+message);
		return ByteBuffer.wrap((message+"这是响应").getBytes());
		
	}
	
	public static void close(SelectionKey key){
		key.cancel();//取消注册
		SocketChannel client = (SocketChannel) key.channel();
		try {
			if (client != null) {
				client.socket().close();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

客户端

package nio;

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.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class Client {
	private static ByteBuffer sendBuf = ByteBuffer.allocate(1024);
	
	private static ByteBuffer respBuf = ByteBuffer.allocate(1024);
	
	private static Map<SelectionKey,Object> sessionMessage = new ConcurrentHashMap<SelectionKey,Object>();
	public static void main(String[] args) throws IOException {
		//多路复用器
	    Selector selector = Selector.open();
		
		//客户端
		SocketChannel client = SocketChannel.open();
		client.configureBlocking(false);
		client.register(selector, SelectionKey.OP_CONNECT);//注册连接事件
		client.connect(new InetSocketAddress("localhost", 9999));//连接服务器,触发connect事件
		//监听客户端事件
		listener(client,selector);
	}

	private static void listener(SocketChannel client, Selector selector) throws IOException {
		while (true) {
				int eventCount = selector.select();//如果有客户端连接过来,就会被轮询出来
				if (eventCount <= 0) {//小于等于0,说明没有事件触发
					continue;
				}
				Set<SelectionKey> keys = selector.selectedKeys();//获取有事件触发的selectkey
				Iterator<SelectionKey> iter = keys.iterator();
				while (iter.hasNext()) {
					SelectionKey key = iter.next();
					//处理事件
					try {
						process(client,selector,key);
					} catch (IOException e) {//客户端异常关闭
						//close(key);
					}
					iter.remove();//处理完移除这个事件
				}
			
		}
	}

	private static void process(SocketChannel client, Selector selector,
			SelectionKey key) throws IOException{
		Scanner scan = new Scanner(System.in);
		SocketChannel channel = null;
		if (key.isValid() && key.isConnectable() ) {//事件是否是有效的
			channel = (SocketChannel) key.channel();
			if (channel.isConnectionPending()) {//如果正在连接,完成连接
				channel.configureBlocking(false);
				channel.finishConnect();
				System.out.println("客户端连接成功");
			}
			client.register(selector, SelectionKey.OP_WRITE);//客户端连接上了,可以写了
		}else if(key.isValid() && key.isWritable()){//可写了,向服务器端发送消息
			System.out.println("可以写了-----");
			String line = scan.next();
			if (line.equals("finsh")) {
				System.out.println("关闭了...");
				key.cancel();
				client.close();
				System.exit(1);
			}else{
				sendBuf.clear();
				sendBuf.put(line.getBytes());
				sendBuf.flip();
				client.write(sendBuf);//响应客户端
				key.interestOps(SelectionKey.OP_READ);//写完数据后,这个客户端在多路复用器上的事件又由write变为read,可以读了
			}
		}else if(key.isValid() && key.isReadable()){//可读,获取服务器端响应
			System.out.println("可以读了-----");
			respBuf.clear();//清空缓冲区
			int length = client.read(respBuf);//将数据读到缓冲区
			if (length > 0) {//读取到内容的时候
				String message = new String(respBuf.array(),0,length);
				System.out.println("client-响应信息::::"+message);
				key.interestOps(SelectionKey.OP_WRITE);//读完数据后,这个客户端在多路复用器上的事件由read变为write,可以写了
			}
		}		
	}
}

 

© 著作权归作者所有

共有 人打赏支持
一不留神
粉丝 6
博文 31
码字总数 23780
作品 0
郑州
后端工程师
跳槽时,这些Java面试题99%会被问到

我在 Oracle 已经工作了近 7 年,面试过从初级到非常资深的Java工程师,且由于 Java 组工作任务的特点,我非常注重面试者的计算机科学基础和编程语言的理解深度,可以不要求面试者非要精通 ...

Java小铺
08/15
0
0
Apache Mina 网络通信

Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我...

Mr&Cheng
2013/01/20
0
0
Java NIO原理 图文分析及代码实现

Java NIO原理图文分析及代码实现 前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术...

囚兔
2015/04/29
0
0
Java NIO原理图文分析及代码实现

前言: 最近在分析hadoop的RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。可以参考:http://baik...

SunnyWu
2014/11/05
0
1
Linux IO模型与Java NIO

概述 看Java NIO一篇文章的时候又看到了“异步非阻塞”这个概念,一直处于似懂非懂的状态,想解释下到底什么是异步 什么是非阻塞,感觉抓不住重点。决定仔细研究一下。 本文试图研究以下问题...

yingtju
06/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

kubeadm部署kubernetes集群

一、环境要求 这里使用RHEL7.5 master、etcd:192.168.10.101,主机名:master node1:192.168.10.103,主机名:node1 node2:192.168.10.104,主机名:node2 所有机子能基于主机名通信,编辑...

人在艹木中
今天
6
0
Shell特殊符号总结以及cut,sort,wc,uniq,tee,tr,split命令

特殊符号总结一 * 任意个任意字符 ? 任意一个字符 # 注释字符 \ 脱义字符 | 管道符 # #号后的备注被忽略[root@centos01 ~]# ls a.txt # 备注 a.txt[root@centos01 ~]# a=1[root@centos01...

野雪球
今天
2
0
OSChina 周二乱弹 —— 程序员圣衣

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @达尔文:分享Skeeter Davis的单曲《The End of the World》 《The End of the World》- Skeeter Davis 手机党少年们想听歌,请使劲儿戳(这里...

小小编辑
今天
14
0
[ python import module ] 导入模块

import moudle_name ----> import module_name.py ---> import module_name.py文件路径 -----> sys.path (这里进行查找文件) # from app.web import Personimport app.web.Person as Pe......

_______-
昨天
5
0
Redis性能问题排查解决手册

一、性能相关的数据指标 通过Redis-cli命令行界面访问到Redis服务器,然后使用info命令获取所有与Redis服务相关的信息。通过这些信息来分析文章后面提到的一些性能指标。 nfo命令输出的数据可...

IT--小哥
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部