Java 非阻塞式NIO 案例(实现多人聊天功能)
一、使用java nio完成网络通信的三个核心
1.通道(channel):负责连接
java.nio.channels.channel 接口:
|--selectablechannel
|--socketchannel
|--serversocketchannel
|--datagramchannel
|--pipe.sinkchannel
|--pipe.sourcechannel
2.缓冲区(buffer):负责数据存取
3.选择器(selector):是selectablechannel 的多路复用器,用来检测selectablechannel的io状态
案例:使用非阻塞式实现简单的群聊天系统
一、实现客户端
1 public static void main(string[] args) throws exception { 2 socketchannel schannel = socketchannel.open(new inetsocketaddress("127.0.0.1", 8989)); 3 4 //2. 切换非阻塞模式 5 schannel.configureblocking(false); 6 7 //3. 分配指定大小的缓冲区 8 bytebuffer buf = bytebuffer.allocate(1024); 9 10 //4. 发送数据给服务端 11 scanner scan = new scanner(system.in); 12 13 while (scan.hasnext()) { 14 string str = scan.next(); 15 buf.put((new date().tostring() + "\n" + str).getbytes()); 16 buf.flip(); 17 schannel.write(buf); 18 buf.clear(); 19 } 20 21 //5. 关闭通道 22 schannel.close(); 23 }
二、实现服务端
@test public void server() { serversocketchannel sschannel = null; try { sschannel = serversocketchannel.open(); //配置非阻塞 sschannel.configureblocking(false); //绑定连接 sschannel.bind(new inetsocketaddress(8989)); selector selector = selector.open(); //将通道注册到监听器中,并且制定监听器的监听模式为“接受” sschannel.register(selector, selectionkey.op_accept); //轮询的选择已经就绪的事件 while (selector.select() > 0) { //获取当前监听 iterator<selectionkey> it = selector.selectedkeys().iterator(); while (it.hasnext()) { //获取准备就绪的事件 selectionkey sk = it.next(); if (sk.isacceptable()) { //如果接受就绪,则获取客户端的连接 socketchannel clientchannel = sschannel.accept(); //同样配置成非阻塞式 clientchannel.configureblocking(false); //把客户端的连接注册到选择器上 clientchannel.register(selector, selectionkey.op_read); } else if (sk.isreadable()) { //如果读取就绪,则获取读取的通道 socketchannel socketchannel = (socketchannel) sk.channel(); //配置成非阻塞模式 socketchannel.configureblocking(false); //读取数据 bytebuffer bytebuffer = bytebuffer.allocate(1024); int len = 0; while ((len = socketchannel.read(bytebuffer)) > 0) { bytebuffer.flip(); system.out.println(new string(bytebuffer.array(), 0, len)); bytebuffer.clear(); } } it.remove(); } } } catch (ioexception e) { e.printstacktrace(); } finally { if(sschannel!=null){ try { sschannel.close(); } catch (ioexception e) { e.printstacktrace(); } } } }
注意:这里服务端用到的org.junit.test;这个包方便测试,客户端因为需要读取输入所以写在main函数中(@test方法中测试出来好像不能读取输入)
需要下载包的地址如下:
链接:https://pan.baidu.com/s/14zhhonad3ldnvca3pmcojq
提取码:uqd9
datagramchannel(udp)的使用方法(和上个案例大同小异)
public static void main(string args[]) { datagramchannel datagramchannel = null; try { datagramchannel = datagramchannel.open(); datagramchannel.configureblocking(false); bytebuffer bytebuffer = bytebuffer.allocate(1024); scanner scanner = new scanner(system.in); while (scanner.hasnext()) { string str = scanner.next(); bytebuffer.put(str.getbytes()); bytebuffer.flip(); datagramchannel.send(bytebuffer, new inetsocketaddress("127.0.0.1", 9897)); bytebuffer.clear(); } } catch (ioexception e) { e.printstacktrace(); } finally { if(datagramchannel!=null){ try { datagramchannel.close(); } catch (ioexception e) { e.printstacktrace(); } } } } @test public void server(){ datagramchannel datagramchannel = null; try { datagramchannel=datagramchannel.open(); datagramchannel.bind(new inetsocketaddress(9897)); datagramchannel.configureblocking(false); selector selector = selector.open(); datagramchannel.register(selector, selectionkey.op_read); while(selector.select()>0){ iterator<selectionkey> st=selector.selectedkeys().iterator(); while(st.hasnext()){ selectionkey sk=st.next(); if(sk.isreadable()){ bytebuffer btf=bytebuffer.allocate(1024); datagramchannel.receive(btf); btf.flip(); system.out.println(new string(btf.array(),0,btf.limit())); btf.clear(); } } st.remove(); } } catch (ioexception e) { e.printstacktrace(); } finally { if(datagramchannel!=null){ try { datagramchannel.close(); } catch (ioexception e) { e.printstacktrace(); } } } }
pipe简介
pipe是两个线程之间单项数据连接,pipe有两个数据通道,sign通道负责写入,source通道负责读取。
案例如下:
@test public void test() throws exception { pipe pipe = pipe.open(); bytebuffer bytebuffer = bytebuffer.allocate(1024); bytebuffer.put("hello world".getbytes()); pipe.sinkchannel sinkchannel=pipe.sink(); bytebuffer.flip(); sinkchannel.write(bytebuffer); //读取 pipe.sourcechannel sourcechannel =pipe.source(); bytebuffer.flip(); int len=sourcechannel.read(bytebuffer); system.out.println("sourcechanel:"+new string(bytebuffer.array(),0,len)); bytebuffer.clear(); sinkchannel.close(); sourcechannel.close(); }
谢谢浏览,如有问题直接评论,我会及时更改我的错误。
上一篇: 电脑开始菜单中运行的搜索记录怎么清除?
下一篇: 网银U盾检测不到怎么办(已经插入U盾)