因为打算用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作进一步的讨论。
分享到:
相关推荐
赠送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...
设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件结束 这样字节的判断,然后 返回行 如果 到达 fbb的结尾 还没有结束,就再通过nio读取一段字节,继续处理...
赠送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 深入探讨了 1.4 版的 I/O 新特性,并告诉您如何使用这些特性来极大地提升您所写的 Java 代码的执行效率。这本小册子就程序员所面临的有代表性的 I/O 问题作了详尽阐述,并讲解了 如何才能充分利用新的 I/O ...
Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道之间的数据传输 Java NIO系列教程(六)...
java NIO是 java New IO 的简称,在 jdk1.4 里提供的新 api 。 Sun 官方标榜的特性如下: – 为所有的原始类型提供 (Buffer) 缓存支持。 – 字符集编码解码解决方案。 – Channel :一个新的原始 I/O 抽象。 – 支持...
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 ...
设计思想: 每次通过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...
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 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据文件Java nio 超大数据文件 超大数据...
java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现...
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。 Java NIO: Channels and Buffers(通道和缓冲区) 标准的IO基于字节流和字符流进行操作的,...
JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的知识,并...
新的输入/输出 (NIO) 库是在 JDK 1.4 中引入的。NIO 弥补了原来的 I/O 的不足,它在标准 Java 代码中提供了高速的、面向块的 I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO 不用使用本机代码就...
Java NIO英文高清原版
尚硅谷_NIO_NIO 与 IO 区别 ·02. 尚硅谷_NIO_缓冲区(Buffer)的数据存取 ·03. 尚硅谷_NIO_直接缓冲区与非直接缓冲区 ·04. 尚硅谷_NIO_通道(Channel)的原理与获取 ·05. 尚硅谷_NIO_通道的数据传输与内存映射文件 ...
赠送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文档-中文(简体)版....
java NIO.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...