博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java Socket NIO示例总结
阅读量:4505 次
发布时间:2019-06-08

本文共 6403 字,大约阅读时间需要 21 分钟。

Java NIO是非阻塞IO的实现,基于事件驱动,非常适用于服务器需要维持大量连接,但是数据交换量不大的情况,例如一些即时通信的服务等等,它主要有三个部分组成:

  • Channels
  • Buffers
  • Selectors

Channel有两种ServerSocketChannel 和 SocketChannel,ServerSocketChannel可以监听新加入的Socket连接,SocketChannel用于读和写操作。NIO总是把缓冲区的数据写入通道,或者把通道里的数据读出到缓冲区。

Buffer本质上是一块用于读写的内存,只是被包装成了buffer对象,你可以通过allocateDirect()或者allocate()申请内存空间(allocate分配方式产生的内存开销是在JVM中的,而allocateDirect的分配方式产生的开销在JVM之外,以就是系统级的内存分配,使用allocateDirect尤其注意内存溢出问题),Buffer尤其需要理解三个概念,

capacity、position、limit,capacity是固定大小,position是当前读写位置,limit是一个类似于门限的值,用于控制读写的最大的位置。Buffer的常用方法有clear、compact、flip等等,还有比如Buffer的静态方法wrap等等,这些需要根据capacity、position、limit的值进行理解,上面ifeve上的文章就很详细了,我就不再累述了。

Selector用于检测通道,我们通过它才知道哪个通道发生了哪个事件,所以如果需要用selector的话就需要首先进行register,然后遍历SelectionKey对事件进行处理。它一共有SelectionKey.OP_CONNECT、SelectionKey.OP_ACCEPT、SelectionKey.OP_READ、SelectionKey.OP_WRITE四种事件类型。

我在这里只是粗略的总结,关于NIO的概念 http://ifeve.com/java-nio-all/ 可以看这里。

http://blog.csdn.net/ns_code/article/details/15545057 兰亭风雨的这个demo不错,我直接照搬过来了。

import java.io.IOException;import java.net.InetSocketAddress;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.util.Iterator;public class NIOServer {        private static int BUFF_SIZE=1024;    private static int TIME_OUT = 2000;    public static void main(String[] args) throws IOException {                Selector selector = Selector.open();        ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();        serverSocketChannel.bind(new InetSocketAddress(10083));        serverSocketChannel.configureBlocking(false);        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);                TCPProtocol protocol = new EchoSelectorProtocol(BUFF_SIZE);                 while (true) {            if(selector.select(TIME_OUT)==0){                //在等待信道准备的同时,也可以异步地执行其他任务,  这里打印*                System.out.print("*");                  continue;              }            Iterator
keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); //如果服务端信道感兴趣的I/O操作为accept if (key.isAcceptable()){ protocol.handleAccept(key); } //如果客户端信道感兴趣的I/O操作为read if (key.isReadable()){ protocol.handleRead(key); } //如果该键值有效,并且其对应的客户端信道感兴趣的I/O操作为write if (key.isValid() && key.isWritable()) { protocol.handleWrite(key); } //这里需要手动从键集中移除当前的key keyIter.remove(); } } }}
import java.io.IOException;import java.nio.channels.SelectionKey;public interface TCPProtocol {    void handleAccept(SelectionKey key) throws IOException;    void handleRead(SelectionKey key) throws IOException;    void handleWrite(SelectionKey key) throws IOException;}
import java.io.IOException;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;public class EchoSelectorProtocol implements TCPProtocol {        private int bufSize; // 缓冲区的长度      public EchoSelectorProtocol(int bufSize){      this.bufSize = bufSize;      }    @Override    public void handleAccept(SelectionKey key) throws IOException {        System.out.println("Accept");        SocketChannel socketChannel = ((ServerSocketChannel)key.channel()).accept();        socketChannel.configureBlocking(false);        socketChannel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));            }    @Override    public void handleRead(SelectionKey key) throws IOException {        SocketChannel clntChan = (SocketChannel) key.channel();          //获取该信道所关联的附件,这里为缓冲区          ByteBuffer buf = (ByteBuffer) key.attachment();        buf.clear();        long bytesRead = clntChan.read(buf);        //如果read()方法返回-1,说明客户端关闭了连接,那么客户端已经接收到了与自己发送字节数相等的数据,可以安全地关闭          if (bytesRead == -1){               clntChan.close();          }else if(bytesRead > 0){            //如果缓冲区总读入了数据,则将该信道感兴趣的操作设置为为可读可写              key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);          }              }    @Override    public void handleWrite(SelectionKey key) throws IOException {        // TODO Auto-generated method stub        ByteBuffer buffer=(ByteBuffer) key.attachment();        buffer.flip();        SocketChannel clntChan = (SocketChannel) key.channel();          //将数据写入到信道中          clntChan.write(buffer);          if (!buffer.hasRemaining()){                    //如果缓冲区中的数据已经全部写入了信道,则将该信道感兴趣的操作设置为可读            key.interestOps(SelectionKey.OP_READ);          }         //为读入更多的数据腾出空间          buffer.compact();               }}
import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SocketChannel;public class NIOClient {        public static void main(String[] args) throws IOException {        SocketChannel clntChan = SocketChannel.open();        clntChan.configureBlocking(false);        if (!clntChan.connect(new InetSocketAddress("localhost", 10083))){              //不断地轮询连接状态,直到完成连接              while (!clntChan.finishConnect()){                  //在等待连接的时间里,可以执行其他任务,以充分发挥非阻塞IO的异步特性                  //这里为了演示该方法的使用,只是一直打印"."                  System.out.print(".");                }           }                //为了与后面打印的"."区别开来,这里输出换行符          System.out.print("\n");          //分别实例化用来读写的缓冲区                  ByteBuffer writeBuf = ByteBuffer.wrap("send send send".getBytes());        ByteBuffer readBuf = ByteBuffer.allocate("send".getBytes().length-1);                while (writeBuf.hasRemaining()) {            //如果用来向通道中写数据的缓冲区中还有剩余的字节,则继续将数据写入信道                  clntChan.write(writeBuf);                      }        StringBuffer stringBuffer=new StringBuffer();         //如果read()接收到-1,表明服务端关闭,抛出异常              while ((clntChan.read(readBuf)) >0){                readBuf.flip();                stringBuffer.append(new String(readBuf.array(),0,readBuf.limit()));                readBuf.clear();            }                //打印出接收到的数据          System.out.println("Client Received: " +  stringBuffer.toString());          //关闭信道          clntChan.close();      }}

 

转载于:https://www.cnblogs.com/ywind/p/4555625.html

你可能感兴趣的文章
Linux环境下的C/C+基础调试技术2——程序控制
查看>>
wpf动画同步闪烁
查看>>
3.16上午 复习雅思核心词+新单词100个
查看>>
Html5 部分特性
查看>>
前端工具集合记录
查看>>
浅析负载均衡的6种算法,Ngnix的5种算法
查看>>
OpenCV——图像修补
查看>>
自定义 DateTime 格式字符串
查看>>
设计模式--工厂模式Factory
查看>>
五年修炼SEO、一年五万,多嘛?(看时间如何管理?五点论……)
查看>>
Mesos源码分析(16): mesos-docker-executor的运行
查看>>
echarts柱状图点击阴影部分触发事件
查看>>
3771: Triple
查看>>
使用PyPDF2库对pdf文件进行指定页面删除操作
查看>>
Python:yield关键字
查看>>
EasyRTSPClient:基于live555封装的支持重连的RTSP客户端RTSPClient
查看>>
EasyDarwin云存储方案调研:海康萤石云采用的是MPEG-PS打包的方式进行的存储
查看>>
MySQL巡检
查看>>
学习笔记之传说中的圣杯布局
查看>>
oh-my-zsh的使用
查看>>