Java语言之Java Socket NIO示例
吕益平 2018-10-19 来源 : 阅读 1793 评论 0

摘要:本文主要向大家介绍了Java语言之Java Socket NIO示例,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了Java语言之Java Socket NIO示例,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

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四种事件类型。

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;  
            }
            IteratorkeyIter = 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();  
    }
}

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @职坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 2 不喜欢 | 1
看完这篇文章有何感觉?已经有3人表态,67%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论
本文作者 联系TA

熟悉企业软件开发的产品设计及开发

  • 57
    文章
  • 12451
    人气
  • 85%
    受欢迎度

已有47人表明态度,85%喜欢该老师!

进入TA的空间
求职秘籍 直通车
  • 索取资料 索取资料 索取资料
  • 答疑解惑 答疑解惑 答疑解惑
  • 技术交流 技术交流 技术交流
  • 职业测评 职业测评 职业测评
  • 面试技巧 面试技巧 面试技巧
  • 高薪秘笈 高薪秘笈 高薪秘笈
TA的其他文章 更多>>
java语言实现栈的顺序存储与链式存储
经验技巧 100% 的用户喜欢
一天天都在说hadoop,到底什么才是hadoop呢?
经验技巧 57% 的用户喜欢
WEB前端之div css 层级
经验技巧 100% 的用户喜欢
WEB前端之div css 绝对定位
经验技巧 100% 的用户喜欢
编程语言之Mybatis 一对一关联查询
经验技巧 100% 的用户喜欢
其他海同师资 更多>>
孔庆琦
孔庆琦 联系TA
对MVC模式和三层架构有深入的研究
周鸣君
周鸣君 联系TA
擅长Hadoop/Spark大数据技术
范佺菁
范佺菁 联系TA
擅长Java语言,只有合理的安排和管理时间你才能做得更多,行得更远!
金延鑫
金延鑫 联系TA
擅长与学生或家长及时有效沟通
程光淼
程光淼 联系TA
精通C、C++等语言、智能芯片开发
经验技巧30天热搜词 更多>>

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程