Java NIO 非阻塞Socket服务器构建
推荐阅读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 AbstractSelectableChannelA 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
ServerSocketobject obtained by invoking thesocketmethod. It is not possible to create a channel for an arbitrary, pre-existing server socket, nor is it possible to specify theSocketImplobject to be used by a server socket associated with a server-socket channel.A server-socket channel is created by invoking the
openmethod of this class. A newly-created server-socket channel is open but not yet bound. An attempt to invoke theacceptmethod of an unbound server-socket channel will cause aNotYetBoundExceptionto be thrown. A server-socket channel can be bound by invoking one of thebindmethods of an associated server socket.Server-socket channels are safe for use by multiple concurrent threads.
再看选择器Selector的用法:请戳这里。
Java NIO 非阻塞Socket服务器构建的更多相关文章
- Java NIO非阻塞理论学习
Java NIO和阻塞IO的区别: 阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回:同样,在调用ServerSocket.accept() ...
- java socket编程开发简单例子 与 nio非阻塞通道
基本socket编程 1.以下只是简单例子,没有用多线程处理,只能一发一收(由于scan.nextLine()线程会进入等待状态),使用时可以根据具体项目功能进行优化处理 2.以下代码使用了1.8新特 ...
- JAVA基础知识之网络编程——-基于NIO的非阻塞Socket通信
阻塞IO与非阻塞IO 通常情况下的Socket都是阻塞式的, 程序的输入输出都会让当前线程进入阻塞状态, 因此服务器需要为每一个客户端都创建一个线程. 从JAVA1.4开始引入了NIO API, NI ...
- 用Java实现非阻塞通信
用ServerSocket和Socket来编写服务器程序和客户程序,是Java网络编程的最基本的方式.这些服务器程序或客户程序在运行过程中常常会阻塞.例如当一个线程执行ServerSocket的acc ...
- NIO非阻塞式编程
/** * NIO非阻塞式编程<p> * 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,该对象能检测一个或多个通道 (channel) 上的事件. * 我们以服务端 ...
- Linux 网络编程七(非阻塞socket:epoll--select)
阻塞socket --阻塞调用是指调用结果返回之前,当前线程会被挂起.函数只有在得到结果之后才会返回. --对于文件操作 read,fread函数调用会将线程阻塞(平常使用read感觉不出来阻塞, 因 ...
- linux网络编程中阻塞和非阻塞socket的区别
读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返 回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当 ...
- 阻塞和非阻塞socket的区别
读操作 对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返回.当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数.当s ...
- 【2018.08.13 C与C++基础】网络通信:阻塞与非阻塞socket的基本概念及简单实现
一.前言 最近在做Matalb/Simulink与C/C++的混合编程,主要是完成TCP.UDP.SerialPort等常见通信方式的中间件设计,为Simulink模型提供数据采集及解析模块. 问题在 ...
随机推荐
- 大熊君大话NodeJS之------Global Objects全局对象
一,开篇分析 在上个章节中我们学习了NodeJS的基础理论知识,对于这些理论知识来说理解是至关重要的,在后续的章节中,我们会对照着官方文档逐步学习里面的各部分模块,好了该是本文主角登台亮相的时候了,G ...
- iframe框架嵌套技巧(全屏,去双滚动条)
一般情况下我们很少用到iframe(框架),但有些特殊的情况下我们不得不使用iframe,那么或许或遇到嵌套内容不全屏,网页周围有边框,双滚动条等等情况,下面来说一下处理技巧. 全屏与边框处理: &l ...
- 搭建 Windows Server 2012 FTP 服务器
在Server2012打开 服务器管理器,选择 添加角色与功能,添加Web服务下的FTP服务器 单击安装 我们现在C盘创建一个名字为FTP的文件夹,里面创建一个ftp的文件,做测试用,如图 打开服务器 ...
- Cache and Virtual Memory
Cache存储器:电脑中为高速缓冲存储器,是位于CPU和主存储器DRAM(DynamicRandonAccessMemory)之间,规模较小,但速度很高的存储器,通常由SRAM(StaticRando ...
- Xcode 8 打印输出: Class PLBuildVersion is implemented in both...
在xcode8中,屏蔽了一些奇怪的输出之后,又发现了一个.具体啥原因还不是太清楚.但是可以解决这个问题,让其停止打印这个信息. 在 info.plist 中,添加两个键值对: 针对photo: key ...
- Laravel 5.1 文档攻略 —— Eloquent Collection
简介 像all()和get(),还有一些处理模型关系这种会返回多条数据的方法,在Eloquent里面会返回一个collection对象集合(对象装在对象里),而不是像DQB的数组结果集合(对象装在数组 ...
- OOP复习笔记
/*OOP相关的代名词不做讲解*/ OOP的三大特征: 封装 - 继承 - 多态 -----------------------------------目录---------------------- ...
- hibernate 学习知识总结
1.最近用hibernate 学会很多知识,总结如下: (1)数据库表格已经设置默认值,在进行数据插入的时候,bean里面不赋值的话,插入之后该字段依旧是null 是因为hibernate默认插入和更 ...
- c++ SOA Axis2c 编译安装
Axis2C 安装过程 1设置环境变量 export AXIS2C_HOME=/usr/local/axis2c 2.下载源码包解压编译安装 cd axis2c-src-1.6.0 ./configu ...
- centos 谷歌浏览器安装
首先,这个是坑 http://www.tecmint.com/install-google-chrome-on-redhat-centos-fedora-linux/ 安装会报错,按照错误找到以下资源 ...