推荐阅读IBM developerWorks中NIO的入门教程,尤其是对块I/O和流I/O不太清楚的开发者。

说到socket服务器,第一反应是java.net.Socket这个类。事实上在并发和响应时间要求不高的场合,是可以用java.net.Socket来实现的,比如写一个局域网聊天工具、发送文件等。但它的缺点也很明显,需要自行对接受的线程进行维护,管理缓冲区的分配等,我尝试过用java.net.Socket完成一个瞬时负载在千人左右的服务器,却因后期改动和维护异常麻烦而放弃。

Java自1.4以后,加入了新IO特性,这便是本文要介绍的NIO。下面是一段服务器的示例代码(引用自xpbug的Blog):

 public class EchoServer {
public static SelectorLoop connectionBell;
public static SelectorLoop readBell;
public boolean isReadBellRunning=false; public static void main(String[] args) throws IOException {
new EchoServer().startServer();
} // 启动服务器
public void startServer() throws IOException {
// 准备好一个闹钟.当有链接进来的时候响.
connectionBell = new SelectorLoop(); // 准备好一个闹装,当有read事件进来的时候响.
readBell = new SelectorLoop(); // 开启一个server channel来监听
ServerSocketChannel ssc = ServerSocketChannel.open();
// 开启非阻塞模式
ssc.configureBlocking(false); ServerSocket socket = ssc.socket();
socket.bind(new InetSocketAddress("localhost",7878)); // 给闹钟规定好要监听报告的事件,这个闹钟只监听新连接事件.
ssc.register(connectionBell.getSelector(), SelectionKey.OP_ACCEPT);
new Thread(connectionBell).start();
} // Selector轮询线程类
public class SelectorLoop implements Runnable {
private Selector selector;
private ByteBuffer temp = ByteBuffer.allocate(1024); public SelectorLoop() throws IOException {
this.selector = Selector.open();
} public Selector getSelector() {
return this.selector;
} @Override
public void run() {
while(true) {
try {
// 阻塞,只有当至少一个注册的事件发生的时候才会继续.
this.selector.select(); Set<SelectionKey> selectKeys = this.selector.selectedKeys();
Iterator<SelectionKey> it = selectKeys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
// 处理事件. 可以用多线程来处理.
this.dispatch(key);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void dispatch(SelectionKey key) throws IOException, InterruptedException {
if (key.isAcceptable()) {
// 这是一个connection accept事件, 并且这个事件是注册在serversocketchannel上的.
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
// 接受一个连接.
SocketChannel sc = ssc.accept(); // 对新的连接的channel注册read事件. 使用readBell闹钟.
sc.configureBlocking(false);
sc.register(readBell.getSelector(), SelectionKey.OP_READ); // 如果读取线程还没有启动,那就启动一个读取线程.
synchronized(EchoServer.this) {
if (!EchoServer.this.isReadBellRunning) {
EchoServer.this.isReadBellRunning = true;
new Thread(readBell).start();
}
} } else if (key.isReadable()) {
// 这是一个read事件,并且这个事件是注册在socketchannel上的.
SocketChannel sc = (SocketChannel) key.channel();
// 写数据到buffer
int count = sc.read(temp);
if (count < 0) {
// 客户端已经断开连接.
key.cancel();
sc.close();
return;
}
// 切换buffer到读状态,内部指针归位.
temp.flip();
String msg = Charset.forName("UTF-8").decode(temp).toString();
System.out.println("Server received ["+msg+"] from client address:" + sc.getRemoteAddress()); Thread.sleep(1000);
// echo back.
sc.write(ByteBuffer.wrap(msg.getBytes(Charset.forName("UTF-8")))); // 清空buffer
temp.clear();
}
}
} }

此外,还有一个看上去更“规范”的示例《ServerSocketChannel与SocketChannel的使用》,这里就不再引用了。

OK,原文的注释已经很详细了,这里进一步解析这段代码。

首先是java.nio.channels.ServerSocketChannel这个类,引用官方的描述:

public abstract class ServerSocketChannelextends AbstractSelectableChannel

A selectable channel for stream-oriented listening sockets.

Server-socket channels are not a complete abstraction of listening network sockets. Binding and the manipulation of socket options must be done through an associated ServerSocket object obtained by invoking the socket method. It is not possible to create a channel for an arbitrary, pre-existing server socket, nor is it possible to specify the SocketImpl object to be used by a server socket associated with a server-socket channel.

A server-socket channel is created by invoking the open method of this class. A newly-created server-socket channel is open but not yet bound. An attempt to invoke the accept method of an unbound server-socket channel will cause a NotYetBoundException to be thrown. A server-socket channel can be bound by invoking one of the bind methods of an associated server socket.

Server-socket channels are safe for use by multiple concurrent threads.

再看选择器Selector的用法:请戳这里。

Java NIO 非阻塞Socket服务器构建的更多相关文章

  1. Java NIO非阻塞理论学习

    Java NIO和阻塞IO的区别: 阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回:同样,在调用ServerSocket.accept() ...

  2. java socket编程开发简单例子 与 nio非阻塞通道

    基本socket编程 1.以下只是简单例子,没有用多线程处理,只能一发一收(由于scan.nextLine()线程会进入等待状态),使用时可以根据具体项目功能进行优化处理 2.以下代码使用了1.8新特 ...

  3. JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信

    阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...

  4. 用Java实现非阻塞通信

    用ServerSocket和Socket来编写服务器程序和客户程序,是Java网络编程的最基本的方式.这些服务器程序或客户程序在运行过程中常常会阻塞.例如当一个线程执行ServerSocket的acc ...

  5. NIO非阻塞式编程

    /** * NIO非阻塞式编程<p> * 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件. * 我们以服务端 ...

  6. Linux 网络编程七(非阻塞socket:epoll--select)

    阻塞socket --阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. --对于文件操作 read,fread函数调用会将线程阻塞(平常使用read感觉不出来阻塞, 因 ...

  7. linux网络编程中阻塞和非阻塞socket的区别

    读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返 回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当 ...

  8. 阻塞和非阻塞socket的区别

    读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当s ...

  9. 【2018.08.13 C与C++基础】网络通信:阻塞与非阻塞socket的基本概念及简单实现

    一.前言 最近在做Matalb/Simulink与C/C++的混合编程,主要是完成TCP.UDP.SerialPort等常见通信方式的中间件设计,为Simulink模型提供数据采集及解析模块. 问题在 ...

随机推荐

  1. winxp可以禁用的服务

    要注意的是: 虽然某个服务你设置成了手动, 而且在services.msc中好像也没有启动, 但是并不表示这个服务不可以被启动 因为某些软件, 可能在程序内部进行了编程的设置, 它可以在内部去启动 服 ...

  2. Hadoop伪分布式搭建(一)

     下面内容主要说明在Windows虚拟机上面,怎么搭建一个Hadoop伪分布式,并如何运行wordcount程序和网页查看HDFS文件系统. 1 相关软件下载和安装 APACH官网提供hadoop版本 ...

  3. php分页代码简单实现

    版权声明:本文为博主原创文章,未经博主允许不得转载. 数据库操作类代码:mysqli.func.php <?php // 数据库连接常量 define('DB_HOST', 'localhost ...

  4. 让我们一起学Node.js-文章列表

    新浪的博客最近不给力,只好在博客园落个窝.至此之后,技术随笔会在博客园以及新浪的博客上同时更新,如果新浪给力的话~~~ 如果你想看先前新浪博客上分享的技术,请点击此处 忘尘子新浪博客! 我是拜读了朴灵 ...

  5. 几个主流java连接池

    池(Pool)技术在一定程度上可以明显优化服务器应用程序的性能,提高程序执行效率和降低系统资源开销.这里所说的池是一种广义上的池,比如数据库连接池.线程池.内存池.对象池等.其中,对象池可以看成保存对 ...

  6. POJ 3292 Semi-prime H-numbers

    类似素数筛... Semi-prime H-numbers Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6873 Accept ...

  7. Lab1--关于安装JUnit的简要描述

    安装JUnit的过程描述: 下载两个jar包: hamcrest-all-1.3.jar junit-4.12.jar 注意在导入完成jar包之后不要随意改变jar包的路径. 创建java程序,书写如 ...

  8. [Asp.net MVC]Asp.net MVC5系列——添加数据

    目录 概述 显示添加数据时所用表单 处理HTTP-POST 总结 系列文章 [Asp.net MVC]Asp.net MVC5系列——第一个项目 [Asp.net MVC]Asp.net MVC5系列 ...

  9. python 临时变量使用心得

    在函数里面的临时变量也可以定义为一个函数名.变量名,这样就可以通过对象来访问这个变量了,函数使用完之后不会消除.因为函数也是对象,python里面一切皆为对象.

  10. OpenCV颜色直方图

    #include "stdafx.h" void myShowHist(IplImage* image1,IplImage* image2); IplImage* cvShowHi ...