`
bat0906
  • 浏览: 26775 次
  • 性别: Icon_minigender_1
  • 来自: 第九天堂
社区版块
存档分类
最新评论
阅读更多
因为打算用java编写异步通信的server和client程序,笔者便学习使用java.nio
开发包,其间遇到一些问题,上网却发现网上对它的应用描述的不是很多。所以,笔者不惜班门弄斧,做些简单的讨论,以便大家更进一步的讨论。
对相关类的简单介绍
    java.nio.*, 据说它提供了一些更加底层的一些功能,如:类似windows环境下的
AsyncSocket类的异步操作的功能,能显著降低server端程序的线程管理开销。
    因为大多数应用是建立在TCP之上,所以在此只说说SocketChannel,
ServerSocketChannel, Selector和ByteBuffer这几个类.前三个最终都源自channel类。而channel 类,可以理解为在具体I/O或文件对象之上抽象的一个操作对象,我们通过操作channel的读写达到对其对应的文件或I/O对象(包括socket)读写的目的。读写的内容在内存中放在ByteBuffer类提供的缓冲区。总而言之,channel作为一个桥梁,连接了I/O对象和内存中的 ByteBuffer,实现了I/O的更高效的存取。
    一个基于TCP的服务器端程序,必然有个侦听端和若干个通信端,它们在nio中由对应的ServerSocketChannel 和SocketChannel类来实现。为了达到异步I/O操作的目的,需要Selector类,它能检测到I/O对象的状态。
    SocketChannel类是抽象类,通过调用它的静态函数open(),可生成一个
SocketChannel对象,该对象对应一个java.net.Socket,可通过SocketChannel.socket()获得,而其对应的Socket也可通过调用函数getChannel()得到已建立的相应SocketChannel。
    SocketChannel与它的socket是一一对应的。SocketChannel的操作与Socket也很相似.
    ServerSocketChannel也是通过调用它的静态函数open()生成的,只是它不能
直接调用bind()函数来绑定一个地址,需要它对应的ServerSocket来完成绑定工作,一般可按如下步骤做:
    ServerSocketChannel ssc = new ServerSocketChannel.open();
    ssc.socket().bind(InetSocketAddress(host,port));
    罗嗦了半天,还是看看最简单的C/S实现吧,服务器提供了基本的回射(echo)功
能,其中提供了较详细的注释。
源码分析
1.服务器端:
////////////////////////
//AsyncServer.java
//   by zztudou@163.com
////////////////////////
import java.nio.channels.SocketChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.spi.SelectorProvider;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Iterator;
import java.util.LinkedList;
import java.io.IOException;
class AsyncServer implements Runnable{
private ByteBuffer r_buff = ByteBuffer.allocate(1024);
private ByteBuffer w_buff = ByteBuffer.allocate(1024);
private static int port = 8848;

public AsyncServer(){
new Thread(this).start();
}

public void run(){  
try{
   //生成一个侦听端
   ServerSocketChannel ssc = ServerSocketChannel.open();
   //将侦听端设为异步方式
   ssc.configureBlocking(false);
   //生成一个信号监视器
   Selector s = Selector.open();
   //侦听端绑定到一个端口
   ssc.socket().bind(new InetSocketAddress(port));
   //设置侦听端所选的异步信号OP_ACCEPT
   ssc.register(s,SelectionKey.OP_ACCEPT);
 
   System.out.println("echo server has been set up ......");

   while(true){
    int n = s.select();
    if (n == 0) {//没有指定的I/O事件发生
     continue;
    }   
    Iterator it = s.selectedKeys().iterator();   
    while (it.hasNext()) {
     SelectionKey key = (SelectionKey) it.next();
     if (key.isAcceptable()) {//侦听端信号触发
      ServerSocketChannel server = (ServerSocketChannel) key.channel();
      //接受一个新的连接
      SocketChannel sc = server.accept();
      sc.configureBlocking(false);
      //设置该socket的异步信号OP_READ:当socket可读时,
     //触发函数DealwithData();
      sc.register(s,SelectionKey.OP_READ);
     } 
     if (key.isReadable()) {//某socket可读信号
      DealwithData(key);
     }   
     it.remove();
    }
   }
}
catch(Exception e){
   e.printStackTrace();
}
}

public void DealwithData(SelectionKey key) throws IOException{
int count;
//由key获取指定socketchannel的引用
SocketChannel sc = (SocketChannel)key.channel();
r_buff.clear();
//读取数据到r_buff
while((count = sc.read(r_buff))> 0)
   ;
//确保r_buff可读
r_buff.flip();

w_buff.clear();
//将r_buff内容拷入w_buff
w_buff.put(r_buff);
w_buff.flip();
//将数据返回给客户端
EchoToClient(sc);

w_buff.clear();
r_buff.clear();
}

public void EchoToClient(SocketChannel sc) throws IOException{
while(w_buff.hasRemaining())
   sc.write(w_buff);
}

public static void main(String args[]){
if(args.length > 0){
   port = Integer.parseInt(args[0]);
}
new AsyncServer();
}
}
在当前目录下运行:
    javac AsynServer.java
后,若无编译出错,接下来可运行:
    java AsynServer 或 java AsynServer ×××(端口号)
上述服务程序在运行时,可指定其侦听端口,否则程序会取8848为默认端口。
2.客户端的简单示例:
////////////////////////
//AsyncClient.java
//   by zztudou@163.com
////////////////////////
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
class AsyncClient{
private SocketChannel sc;
private final int MAX_LENGTH = 1024;
private ByteBuffer r_buff = ByteBuffer.allocate(MAX_LENGTH);
private ByteBuffer w_buff = ByteBuffer.allocate(MAX_LENGTH);
private static String host ;
private static int port = 8848;

public AsyncClient(){
try {
   InetSocketAddress addr = new InetSocketAddress(host,port);
   //生成一个socketchannel
   sc = SocketChannel.open();
       
   //连接到server
   sc.connect(addr);
   while(!sc.finishConnect())
    ;
   System.out.println("connection has been established!...");
  
   while(true){
    //回射消息
    String echo;
    try{
     System.err.println("Enter msg you'd like to send: ");
     BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
     //输入回射消息
     echo = br.readLine();
   
     //把回射消息放入w_buff中  
     w_buff.clear();
     w_buff.put(echo.getBytes());
     w_buff.flip();
    }catch(IOException ioe){
     System.err.println("sth. is wrong with br.readline() ");
    }  

    //发送消息
    while(w_buff.hasRemaining())
     sc.write(w_buff);
    w_buff.clear();  
  
    //进入接收状态
    Rec();
    //间隔1秒
    Thread.currentThread().sleep(1000);
   }
}catch(IOException ioe){
   ioe.printStackTrace();
}
catch(InterruptedException ie){
   ie.printStackTrace();
}
}
////////////
//读取server端发回的数据,并显示
public void Rec() throws IOException{
int count;
r_buff.clear();
count=sc.read(r_buff);
r_buff.flip();
byte[] temp = new byte[r_buff.limit()];
r_buff.get(temp);
System.out.println("reply is " + count +" long, and content is: " + new String(temp));
}

public static void main(String args[]){
if(args.length < 1){//输入需有主机名或IP地址
   try{
    System.err.println("Enter host name: ");
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    host = br.readLine();
   }catch(IOException ioe){
    System.err.println("sth. is wrong with br.readline() ");
   }
}
else if(args.length == 1){
   host = args[0];
}
else if(args.length > 1){
   host = args[0];
   port = Integer.parseInt(args[1]);
}
new AsyncClient();
}
}
在当前目录下运行:
javac AsynClient.java
后,若无编译出错,确认AsyncServer已经运行的情况下,接下来可运行:
java AsynClient hostname 或 java AsynClient hostname ×××(端口号)
并按提示进行操作即可。
总结
    总的来说,用nio进行网络编程还是很有新意的,服务器端软件能在一个线程中维护与众多客户端的通信连接。笔者在本文中试图用一个典型的回射例子说明如何用nio建立最基本的C/S应用。希望大家能试着用用它。
    另外,笔者在实践中也发现nio在应用中存在的一些难题,比如如何应用SocketChannel的继承类,以及如何在socketchannel之上应用SSL(Secure Socket Layer)等等,因而希望这篇文章只是抛砖引玉,引起大家对nio作进一步的讨论。
在当前目录下运行:
    javac AsynServer.java
后,若无编译出错,接下来可运行:
    java AsynServer 或 java AsynServer ×××(端口号)
上述服务程序在运行时,可指定其侦听端口,否则程序会取8848为默认端口。
2.客户端的简单示例:
////////////////////////
//AsyncClient.java
//   by zztudou@163.com
////////////////////////
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
class AsyncClient{
private SocketChannel sc;
private final int MAX_LENGTH = 1024;
private ByteBuffer r_buff = ByteBuffer.allocate(MAX_LENGTH);
private ByteBuffer w_buff = ByteBuffer.allocate(MAX_LENGTH);
private static String host ;
private static int port = 8848;

public AsyncClient(){
try {
   InetSocketAddress addr = new InetSocketAddress(host,port);
   //生成一个socketchannel
   sc = SocketChannel.open();
       
   //连接到server
   sc.connect(addr);
   while(!sc.finishConnect())
    ;
   System.out.println("connection has been established!...");
  
   while(true){
    //回射消息
    String echo;
    try{
     System.err.println("Enter msg you'd like to send: ");
     BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
     //输入回射消息
     echo = br.readLine();
   
     //把回射消息放入w_buff中  
     w_buff.clear();
     w_buff.put(echo.getBytes());
     w_buff.flip();
    }catch(IOException ioe){
     System.err.println("sth. is wrong with br.readline() ");
    }  

    //发送消息
    while(w_buff.hasRemaining())
     sc.write(w_buff);
    w_buff.clear();  
  
    //进入接收状态
    Rec();
    //间隔1秒
    Thread.currentThread().sleep(1000);
   }
}catch(IOException ioe){
   ioe.printStackTrace();
}
catch(InterruptedException ie){
   ie.printStackTrace();
}
}

////////////
// 读取server端发回的数据,并显示
public void Rec() throws IOException{
int count;
r_buff.clear();
while((count=sc.read(r_buff))>0)
   ;
r_buff.flip();
byte[] temp = new byte[r_buff.limit()];
System.out.println("reply is : " + new String(temp));
}

public static void main(String args[]){
if(args.length < 1){//输入需有主机名或IP地址
   try{
    System.err.println("Enter host name: ");
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    host = br.readLine();
   }catch(IOException ioe){
    System.err.println("sth. is wrong with br.readline() ");
   }
}
else if(args.length == 1){
   host = args[0];
}
else if(args.length > 1){
   host = args[0];
   port = Integer.parseInt(args[1]);
}
new AsyncClient();
}
}
在当前目录下运行:
   javac AsynClient.java
后,若无编译出错,确认AsyncServer已经运行的情况下,接下来可运行:
   java AsynClient hostname 或 java AsynClient hostname ×××(端口号)
并按提示进行操作即可。
总结
    总的来说,用nio进行网络编程还是很有新意的,服务器端软件能在一个线程中维护与众多客户端的通信连接。笔者在本文中试图用一个典型的回射例子说明如何用nio建立最基本的C/S应用。希望大家能试着用用它。
    另外,笔者在实践中也发现nio在应用中存在的一些难题,比如如何应用SocketChannel的继承类,以及如何在socketchannel之上应用SSL(Secure Socket Layer)等等,因而希望这篇文章只是抛砖引玉,引起大家对nio作进一步的讨论。
分享到:
评论
2 楼 1641606815 2012-09-22  
代码再整洁一些,并且把运行结果截图贴上去的话更好,起码让读者一看就知道你这个程序是干嘛的。
1 楼 huzhenyu 2010-03-25  
写得很不错啊,对初学者帮助很大。谢谢楼主!

相关推荐

    xnio-nio-3.8.0.Final-API文档-中文版.zip

    赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.0.Final.pom; 包含翻译后的API...

    JAVA NIO 按行读取大文件,支持 GB级别

    设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件结束 这样字节的判断,然后 返回行 如果 到达 fbb的结尾 还没有结束,就再通过nio读取一段字节,继续处理...

    httpcore-nio-4.4.6-API文档-中文版.zip

    赠送jar包:httpcore-nio-4.4.6.jar 赠送原API文档:httpcore-nio-4.4.6-javadoc.jar 赠送源代码:httpcore-nio-4.4.6-sources.jar 包含翻译后的API文档:httpcore-nio-4.4.6-javadoc-API文档-中文(简体)版.zip ...

    Java NIO 中文 Java NIO 中文 Java NIO 中文文档

    Java NIO 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...

    JavaNIO chm帮助文档

    Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...

    java nio中文版

    java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...

    Java IO, NIO and NIO.2 原版pdf by Friesen

    New I/O (NIO), and NIO.2 categories. You learn what each category offers in terms of its capabilities, and you also learn about concepts such as paths and Direct Memory Access. Chapters 2 through 5 ...

    JAVA NIO 按行读取大文件支持 GB级别-修正版

    设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件结束 这样字节的判断,然后 返回行 如果 到达 fbb的结尾 还没有结束,就再通过nio读取一段字节,继续处理。 ...

    java NIO和java并发编程的书籍

    java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...

    java nio 包读取超大数据文件

    Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据...

    Java IO, NIO and NIO.2(Apress,2015)

    Java I/O, NIO, and NIO.2 is a power-packed book that accelerates your mastery of Java's various I/O APIs. In this book, you'll learn about classic I/O APIs (File, RandomAccessFile, the stream classes ...

    java基于NIO实现Reactor模型源码.zip

    java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现...

    java NIO 视频教程

    Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...

    新输入输出NIO

    JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的知识,并...

    nio入门 IBM教材,pdf格式

    新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就...

    Java NIO英文高清原版

    Java NIO英文高清原版

    尚硅谷Java视频_NIO 视频教程

    尚硅谷_NIO_NIO 与 IO 区别 ·02. 尚硅谷_NIO_缓冲区(Buffer)的数据存取 ·03. 尚硅谷_NIO_直接缓冲区与非直接缓冲区 ·04. 尚硅谷_NIO_通道(Channel)的原理与获取 ·05. 尚硅谷_NIO_通道的数据传输与内存映射文件 ...

    httpcore-nio-4.4.15-API文档-中文版.zip

    赠送jar包:httpcore-nio-4.4.15.jar 赠送原API文档:httpcore-nio-4.4.15-javadoc.jar 赠送源代码:httpcore-nio-4.4.15-sources.jar 包含翻译后的API文档:httpcore-nio-4.4.15-javadoc-API文档-中文(简体)版....

    xnio-nio-3.8.4.Final-API文档-中文版.zip

    赠送jar包:xnio-nio-3.8.4.Final.jar; 赠送原API文档:xnio-nio-3.8.4.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.4.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.4.Final.pom; 包含翻译后的API...

    httpcore-nio-4.4.5-API文档-中文版.zip

    赠送jar包:httpcore-nio-4.4.5.jar; 赠送原API文档:httpcore-nio-4.4.5-javadoc.jar; 赠送源代码:httpcore-nio-4.4.5-sources.jar; 赠送Maven依赖信息文件:httpcore-nio-4.4.5.pom; 包含翻译后的API文档:...

Global site tag (gtag.js) - Google Analytics