Java I/O之NIO Socket
PS:本文简单介绍下旧I/O和NIO下的Socket通讯,仅以UDP来示例。
TCP/IP协议
首先简单回顾下TCP/IP协议
Application:应用程序;Socket:套接字;Host:主机;Channel:通信信道;Ethernet:以太网;Router:路由器;Network Layer:网络层;Transport Layer:传输层。
在TCP/IP协议族中,底层由基础的通信信道构成,如以太网或调制解调器拨号连接。这些信道由网络层(network layer)使用,而网络层则完成将分组报文传输到它们的目的地址的工作(也就是路由器的功能)。TCP/IP协议族中属于网络层的唯一协议是IP协议,它使两个主机间的一系列通信信道和路由器看起来像是一条单一的主机到主机的信道。
IP协议提供了一种数据报服务:每组分组报文都由网络独立处理和分发,就像信件或包裹通过邮政系统发送一样。为了实现这个功能,每个IP报文必须包含一个保存其目的地址(address)的字段,就像你所投递的每份包裹都写明了收件人地址。(我们随即会对地址进行更详细的说明。)尽管绝大部分递送公司会保证将包裹送达,但IP协议只是一个"尽力而为"(best-effort)的协议:它试图分发每一个分组报文,但在网络传输过程中,偶尔也会发生丢失报文,使报文顺序被打乱,或重复发送报文的情况。
IP协议层之上称为传输层(transport layer)。它提供了两种可选择的协议:TCP协议和UDP协议。这两种协议都建立在IP层所提供的服务基础上,但根据应用程序协议(application protocols)的不同需求,它们使用了不同的方法来实现不同方式的传输。TCP协议和UDP协议有一个共同的功能,即寻址。回顾一下,IP协议只是将分组报文分发到了不同的主机,很明显,还需要更细粒度的寻址将报文发送到主机中指定的应用程序,因为同一主机上可能有多个应用程序在使用网络。TCP协议和UDP协议使用的地址叫做端口号(port numbers),都是用来区分同一主机中的不同应用程序。TCP协议和UDP协议也称为端到端传输协议(end-to-end transport protocols),因为它们将数据从一个应用程序传输到另一个应用程序,而IP协议只是将数据从一个主机传输到另一主机。
TCP协议能够检测和恢复IP层提供的主机到主机的信道中可能发生的报文丢失、重复及其他错误。TCP协议提供了一个可信赖的字节流(reliable byte-stream)信道,这样应用程序就不需要再处理上述的问题。TCP协议是一种面向连接(connection-oriented)的协议:在使用它进行通信之前,两个应用程序之间首先要建立一个TCP连接,这涉及到相互通信的两台电脑的TCP部件间完成的握手消息(handshake messages)的交换。使用TCP协议在很多方面都与文件的输入输出(I/O, Input/Output)相似。实际上,由一个程序写入的文件再由另一个程序读取就是一个TCP连接的适当模型。另一方面,UDP协议并不尝试对IP层产生的错误进行修复,它仅仅简单地扩展了IP协议"尽力而为"的数据报服务,使它能够在应用程序之间工作,而不是在主机之间工作。因此,使用了UDP协议的应用程序必须为处理报文丢失、顺序混乱等问题做好准备。
旧I/O的Socket示例
//服务器端
DatagramSocket servSocket = null;
byte[] buf = new byte[1024];
DatagramPacket datapkg = new DatagramPacket(buf, buf.length);
try {
servSocket = new DatagramSocket(5008);
while (true) {
servSocket.receive(datapkg);
//一般使用多线程来处理....
System.out.println("服务器接收了客户端:" + datapkg.getAddress()
+ " 的数据:" + new String(buf, 0, datapkg.getLength()));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
servSocket.close();
}
//客户端
DatagramSocket clientSocket=new DatagramSocket();
byte[] msg="this is a test message".getBytes();
DatagramPacket datapkg=new DatagramPacket(msg, msg.length, new InetSocketAddress("127.0.0.1", 5008));
clientSocket.send(datapkg);
clientSocket.close();
上面只是简单地示例,实际用应用中需要多线程,特别是服务器端,接收到数据报文后一般需要使用多线程来处理的。
NIO的Socket
旧I/O Socket是阻塞式的,我们通过上例可以看到使用了while(true)在那忙等(很傻吧),当然你可以使用多线程来避开阻塞,但这个解决办法会产生它自己的问题 ― 即线程开销,线程开销同时影响性能和可伸缩性。
新NIO Socket可以使用非阻塞式的(如果你一定要把NIO Socket用作阻塞式的,也不会有人拦你的^_^),实际上是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,就会帮我们记录下来,当我们需要的时候再取出来,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。
而这个非阻塞模式就是由NIO中的类Selector来完成的,它类似一个观察者:只要我们把需要探知的channel事件注册到Selector上,我们接着做别的事情,当channel有事件发生时就会主动通知selector,我们可以轮询selector就知道那些channel可操作,然后我们再从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。
NIO中的通道和缓冲器在前面一篇博文中有介绍,我们来看下非阻塞式的代码示例
// 服务器端
DatagramChannel servChannel = DatagramChannel.open();
servChannel.socket().bind(new InetSocketAddress("127.0.0.1", 5008));
// 如果使用selector,此处必须设置为非阻塞式的
servChannel.configureBlocking(false); ByteBuffer buf = ByteBuffer.allocate(1024); Selector selector = Selector.open();
// 注册事件OP_ACCEPT和OP_CONNECT用于TCP,OP_READ和OP_WRITE可用于UDP和TCP
servChannel.register(selector, SelectionKey.OP_READ); // 轮询selector
while (true) {
if (selector.select() > 0) {
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
if (key.isReadable()) {
buf.clear();
SocketAddress clientAdrress = ((DatagramChannel) key
.channel()).receive(buf);
buf.flip();
System.out.println("服务器接收了客户端:" + clientAdrress
+ " 的数据:" + buf.asCharBuffer());
}
it.remove();
}
}
}
虽然说得天花乱坠,但到目前为止我还是没太明白使用这种观察者模式的好处,最后还是避不开忙等。(PS:关于I/O Socket和NIO Socket的对比优势可参考这里。)
推荐一本书:《Java Tcp/Ip Socket编程》
Java I/O之NIO Socket的更多相关文章
- java基础-网络编程(Socket)技术选型入门之NIO技术
java基础-网络编程(Socket)技术选型入门之NIO技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.传统的网络编程 1>.编写socket通信的MyServer ...
- Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制
Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...
- Java nio socket与as3 socket(粘包解码)连接的应用实例
对Java nio socket与as3 socket连接的简单应用 <ignore_js_op>Java nio socket与as3 socket连接的应用实例.rar (9.61 K ...
- NIO【同步非阻塞io模型】关于 NIO socket 的详细总结【Java客户端+Java服务端 + 业务层】【可以客户端间发消息】
1.前言 以前使用 websocket来实现双向通信,如今深入了解了 NIO 同步非阻塞io模型 , 优势是 处理效率很高,吞吐量巨大,能很快处理大文件,不仅可以 做 文件io操作, 还可以做sock ...
- Java I/O and NIO [reproduced]
Java I/O and NIO.2---Five ways to maximize Java NIO and NIO.2---Build more responsive Java applicati ...
- 深入浅出NIO Socket实现机制
前言 Java NIO 由以下几个核心部分组成: Buffer Channel Selector 以前基于net包进行socket编程时,accept方法会一直阻塞,直到有客户端请求的到来,并返回so ...
- java与C++之间进行SOCKET通讯要点简要解析
原文链接: http://blog.csdn.net/hslinux/article/details/6214594 java与C++之间进行SOCKET通讯要点简要解析 hslinux 0.篇外语 ...
- Java网络编程和NIO详解开篇:Java网络编程基础
Java网络编程和NIO详解开篇:Java网络编程基础 计算机网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为 ...
- Java网络编程和NIO详解8:浅析mmap和Direct Buffer
Java网络编程与NIO详解8:浅析mmap和Direct Buffer 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NI ...
随机推荐
- V8 Javascript 引擎设计理念
Netscape Navigator 在 90 在年代中期对 JavaScript 进行了集成,这让网页开发人员对 HTML 页面中诸如 form .frame 和 image 之类的元素的访问变得非 ...
- Canvas 图片灰度
我们可以通过下面几种方法,将其转换为灰度: 1.浮点算法:Gray=R*0.3+G*0.59+B*0.11 2.整数方法:Gray=(R*30+G*59+B*11)/100 3.移位方法:Gray = ...
- Canvas createRadialGradient API
Canvas createRadialGradient API <!DOCTYPE html> <html lang="en"> <head> ...
- 初识JSON
▓▓▓▓▓▓ 大致介绍 JSON(JavaScript Object Notation JavaScript对象表示法),JSON是一种数据格式,不是一种编程语言.虽然它的名字中有JavaScrip ...
- RabbitMQ安装和使用(和Spring集成)
一.安装Rabbit MQ Rabbit MQ 是建立在强大的Erlang OTP平台上,因此安装Rabbit MQ的前提是安装Erlang.通过下面两个连接下载安装3.2.3 版本: 下载并安装 E ...
- vue.js学习第一节
<div id="app" class="app"> <p>{{ message }}</p> <p>{{ in ...
- 自己动手系列——实现一个简单的ArrayList
ArrayList是Java集合框架中一个经典的实现类.他比起常用的数组而言,明显的优点在于,可以随意的添加和删除元素而不需考虑数组的大小.处于练手的目的,实现一个简单的ArrayList,并且把实现 ...
- vue自定义日期组件
vue-datepicker 基于 vuejs 2.x 可自定义主题的日期组件 github Usage 只需要在项目中加入 calendar.vue,就可以使用了. 向组件传入 prop 即可改变 ...
- 学习Cocos2d的博客 --推荐
http://xiaominghimi.blog.51cto.com/2614927/d-6/p-1
- PLSQL游标使用
游标是一个指针,它指向一块SQL区域,该区域用于存储处理过来的SELECT或者其他的DML操作返回的数据.由PLSQL创建并管理的游标成为隐式游标,用户创建并管理的成为显示游标.游标可以看做是指向记录 ...