Java Socket NIO示例总结

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<SelectionKey> 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();
    }
}
时间: 2024-11-08 12:24:20

Java Socket NIO示例总结的相关文章

多线程Java Socket编程示例(转)

这篇做为学习孙卫琴<<Java网络编程精解>>的学习笔记吧.其中采用Java 5的ExecutorService来进行线程池的方式实现多线程,模拟客户端多用户向同一服务器端发送请求. 1.服务端 package sterning; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import

Java Socket NIO详解(转)

java选择器(Selector)是用来干嘛的? 2009-01-12 22:21jsptdut | 分类:JAVA相关 | 浏览8901次 如题,不要贴api的,上面的写的我看不懂希望大家能给我个通熟易懂的例子 还有ServerSocketChannel这个类,java.nio这个包里的东西,我看api也不懂- 哎,恼火呀 又是你呵- ssc.register( selector, SelectionKey.OP_ACCEPT ); 这个方法的第二个参数是什么意思呀?还有这个Selection

java socket nio编程

上次写了一个socket的基本编程,但是有个问题,阻塞特别严重,于是小编便去找了nio学习了一下... 1 public class TimeServer { 2 3 public static void main(String[] args) { 4 MultipexerTimeServer timersServer=new MultipexerTimeServer("192.168.1.102", 8400); 5 new Thread(timersServer, "ni

多线程Java Socket编程示例

package org.merit.test.socket; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; public class Hand

【Java】Java Socket 通信示例

用socket(套接字)实现客户端与服务端的通信. 这里举两个例子: 第一种是每次客户端发送一个数据,服务端就做一个应答.(也就是要轮流发) 第二种是客户端可以连续的向服务端发数据,服务端也可以连续向客户端发数据.(多线程) 为了方便起见,客户端服务端都放在一个主机上,分别开两个进程[哈哈,进程间通信lol~] PS: 一个聊天程序雏形 代码也放在GitHub上了:https://github.com/MummyDing/socketChatDemo Demo 1: 简单的Socket通信,一人

Java SSL Socket通讯示例

上一篇<OpenSSL与KeyStore指令小集>里面说到,最近研究SSL加密,会给出一个Java的小示例.复制一份可以运行的代码到生产上是非常不负责任的行为,不过小示例可以带我们入门,快速看清事物的本质.罗马不是一天建成的. 本文将给出一个Java SSL Socket的小例子,包括了Server和Client.希望大家上手之后,要多去研究相关的资料,理解基础概念.Java的优点是封装得比较彻底,需要介入的地方比较少,缺点是随着Java版本的升级和发展,会有很多新的概念和类涌出来,都要搞清楚

JAVA SOCKET 通信总结 BIO、NIO、AIO ( NIO 2) 的区别和总结

1 同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己上街买衣服,自己亲自干这件事,别的事干不了.2 异步 异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知(异步的特点就是通知) 告诉朋友自己合适衣服的尺寸,大小,颜色,让朋友委托去卖,然后自己可以去干别的事.(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS)3 阻塞 所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当

JAVA Socket超时浅析(转)

套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的“终端”.针对一个特定的连接,每台机器上都有一个“套接字”,可以想象它们之间有一条虚拟的“线缆”.JAVA有两个基于数据流的套接字类:ServerSocket,服务器用它“侦听”进入的连接:Socket,客户端用它初始一次连接.侦听套接字只能接收新的连接请求,不能接收实际的数据包. 套接字是基于TCP/IP实现的,它是用来提供一个访问TCP的服务接口,或者说套接字socket是TCP的应用编程接口API,通过它应用层就

NIO示例1

package com.mzj.nio.java; 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.It