我们都知道TCP是面向连接的传输层协议,一个socket必定会有绑定一个连接,在普通的BIO(阻塞式IO)中,需要有三次握手,然后一般的socket编程就是这样的形式。

Socket服务器端流程如下:加载套接字->创建监听的套接字->绑定套接字->监听套接字->处理客户端相关请求。

Socket客户端同样需要先加载套接字,然后创建套接字,不过之后不用绑定和监听了,而是直接连接服务器,发送相关请求。

他们一直就占用这个连接,如果有信息发送,那么就响应,否则就一直阻塞着。如果有多连接,那么就要使用多线程,一个线程处理一个连接,在连接还少的情况下,是允许的,但如果同时处理的连接过多比如说1000,那么在win平台上就会遇到瓶颈了如果2000,那么在linux上就遇到瓶颈了,因为在不同的平台上每一个进程能够创建的线程数是有限度的,并且过多的线程必将会引起系统对线程调度的效率问题,再怎么也要保证线程优先队列,阻塞队列;假设一千个线程,一个线程最少一兆的栈大小,对内存也是一个很大的消耗。

总之阻塞式的IO是:一连接<一一一>一线程 

然后出现了NIO,在java1.4引入了java.nio包,java new I/O。引入了操作系统中常用的缓冲区和通道等概念。

缓冲区: 在操作系统中缓冲区是为了解决CPU的计算速度和外设输入输出速度不匹配的问题,因为外设太慢了,如果没有缓冲区,那么CPU在外设输入的时候就要一直等着,就会造成CPU处理效率的低下,引入了缓冲之后,外设直接把数据放到缓冲中,当数据传输完成之后,给CPU一个中断信号,通知CPU:“我的数据传完了,你自己从缓冲里面去取吧”。如果是输出也是一样的道理。

通道: 那么通道用来做什么呢?其实从他的名字就可以看出,它就是一条通道,您想传递出去的数据被放置在缓冲区中,然后缓冲区中怎么从哪里传输出去呢?或者外设怎么把数据传输到缓冲中呢?这里就要用到通道。它可以进一步的减少CPU的干预,同时更有效率的提高整个系统的资源利用率,例如当CPU要完成一组相关的读操作时,只需要向I/O通道发送一条指令,以给出其要执行的通道程序的首地址和要访问的设备,通道执行通道程序便可以完成CPU指定的I/O任务。

  选择器: 另外一项创新是选择器,当我们使用通道的时候也许通道没有准备好,或者有了新的请求过来,或者线程遇到了阻塞,而选择器恰恰可以帮助CPU了解到这些信息,但前提是将这个通道注册到了这个选择器。

下面一个例子是我看过的一个讲述的很贴切的例子:

一辆从 A 开往 B 的公共汽车上,路上有很多点可能会有人下车。司机不知道哪些点会有哪些人会下车,对于需要下车的人,如何处理更好?

1. 司机过程中定时询问每个乘客是否到达目的地,若有人说到了,那么司机停车,乘客下车。 ( 类似阻塞式 )

2. 每个人告诉售票员自己的目的地,然后睡觉,司机只和售票员交互,到了某个点由售票员通知乘客下车。 ( 类似非阻塞 )

很显然,每个人要到达某个目的地可以认为是一个线程,司机可以认为是 CPU 。在阻塞式里面,每个线程需要不断的轮询,上下文切换,以达到找到目的地的结果。而在非阻塞方式里,每个乘客 ( 线程 ) 都在睡觉 ( 休眠 ) ,只在真正外部环境准备好了才唤醒,这样的唤醒肯定不会阻塞。

在非阻塞式IO中实现的是:一请求<一一一>一线程

下面这个例子实现了一个线程监听两个ServerSocket,只有等到请求的时候才会有处理。

下面是代码,一个Server,两个Client

Server代码如下:Server监听了两个端口,对应下面的两个Client

package com.sogou.study.nio;/*** Created by denglinjie on 2016/9/27.*/
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.Iterator;/*** TCP/IP的NIO非阻塞方式* 服务器端* */
public class Server implements Runnable {//第一个端口private Integer port1 = 8099;//第二个端口private Integer port2 = 9099;//第一个服务器通道 服务Aprivate ServerSocketChannel serversocket1;//第二个服务器通道 服务Bprivate ServerSocketChannel serversocket2;//连接1private SocketChannel clientchannel1;//连接2private SocketChannel clientchannel2;//选择器,主要用来监控各个通道的事件private Selector selector;//缓冲区private ByteBuffer buf = ByteBuffer.allocate(512);public Server() {init();}/*** 这个method的作用* 1:是初始化选择器* 2:打开两个通道* 3:给通道上绑定一个socket* 4:将选择器注册到通道上* */public void init() {try {//创建选择器this.selector = SelectorProvider.provider().openSelector();//打开第一个服务器通道this.serversocket1 = ServerSocketChannel.open();//告诉程序现在不是阻塞方式的this.serversocket1.configureBlocking(false);//获取现在与该通道关联的套接字this.serversocket1.socket().bind(new InetSocketAddress("localhost", this.port1));//将选择器注册到通道上,返回一个选择键//OP_ACCEPT用于套接字接受操作的操作集位this.serversocket1.register(this.selector, SelectionKey.OP_ACCEPT);//然后初始化第二个服务端this.serversocket2 = ServerSocketChannel.open();this.serversocket2.configureBlocking(false);this.serversocket2.socket().bind(new InetSocketAddress("localhost", this.port2));this.serversocket2.register(this.selector, SelectionKey.OP_ACCEPT);} catch (Exception e) {e.printStackTrace();}}/*** 这个方法是连接* 客户端连接服务器* @throws IOException* */public void accept(SelectionKey key) throws IOException {ServerSocketChannel server = (ServerSocketChannel) key.channel();if (server.equals(serversocket1)) {clientchannel1 = server.accept();clientchannel1.configureBlocking(false);//OP_READ用于读取操作的操作集位clientchannel1.register(this.selector, SelectionKey.OP_READ);} else {clientchannel2 = server.accept();clientchannel2.configureBlocking(false);//OP_READ用于读取操作的操作集位clientchannel2.register(this.selector, SelectionKey.OP_READ);}}/*** 从通道中读取数据* 并且判断是给那个服务通道的* @throws IOException* */public void read(SelectionKey key) throws IOException {this.buf.clear();//通过选择键来找到之前注册的通道//但是这里注册的是ServerSocketChannel为什么会返回一个SocketChannel??SocketChannel channel = (SocketChannel) key.channel();//从通道里面读取数据到缓冲区并返回读取字节数int count = channel.read(this.buf);if (count == -1) {//取消这个通道的注册key.channel().close();key.cancel();return;}//将数据从缓冲区中拿出来String input = new String(this.buf.array()).trim();//那么现在判断是连接的那种服务if (channel.equals(this.clientchannel1)) {System.out.println("欢迎您使用服务A");System.out.println("您的输入为:" + input);} else {System.out.println("欢迎您使用服务B");System.out.println("您的输入为:" + input);}}public void run() {while (true) {try {System.out.println("running ... ");//选择一组键,其相应的通道已为 I/O 操作准备就绪。这里在没有客户端请求时会阻塞this.selector.select();//返回此选择器的已选择键集Iterator selectorKeys = this.selector.selectedKeys().iterator();while (selectorKeys.hasNext()) {System.out.println("running2 ... ");//这里找到当前的选择键SelectionKey key = (SelectionKey) selectorKeys.next();//然后将它从返回键队列中删除selectorKeys.remove();if (!key.isValid()) { // 选择键无效continue;}if (key.isAcceptable()) {//如果遇到请求那么就响应this.accept(key);} else if (key.isReadable()) {//读取客户端的数据this.read(key);}}} catch (Exception e) {e.printStackTrace();}}}public static void main(String[] args) {Server server = new Server();Thread thread = new Thread(server);thread.start();}
}

ClientOne的代码如下:

package com.sogou.study.nio;/*** Created by denglinjie on 2016/9/27.*/
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.net.InetAddress;/*** TCP/IP的NIO非阻塞方式* 客户端* */
public class ClientOne {//创建缓冲区private ByteBuffer buffer = ByteBuffer.allocate(512);//访问服务器public void query(String host, int port) throws IOException {InetSocketAddress address = new InetSocketAddress(InetAddress.getByName(host), port);SocketChannel socket = null;byte[] bytes = new byte[512];while (true) {try {System.in.read(bytes);socket = SocketChannel.open();socket.connect(address);buffer.clear();buffer.put(bytes);buffer.flip();socket.write(buffer);buffer.clear();} catch (Exception e) {e.printStackTrace();} finally {if (socket != null) {socket.close();}}}}public static void main(String[] args) throws IOException {new ClientOne().query("localhost", 8099);}
}

ClientTwo的代码就只是将ClientOne中的端口号8099改成9099就可以了

启动Server后,可以依次启动ClientOne和ClientTwo,在Client中输入一些文字后,可以在Server端接收到

Java NIO操作Socket的用法相关推荐

  1. 基于Java NIO的Socket通信

    基于Java NIO的Socket通信 Java NIO模式的Socket通信,是一种同步非阻塞IO设计模式,它为Reactor模式实现提供了基础. 下面看看,Java实现的一个服务端和客户端通信的例 ...

  2. Java NIO编写Socket服务器的一个例子

    最近一直在忙着JAVA NIO的知识,花了一下午的时间,总算写出了一个可以运行的程序,废话少说,上代码! Java代码: import java.io.IOException; import java ...

  3. 详述 Java NIO 以及 Socket 处理粘包和断包方法

    文章目录 Java NIO 通道 缓冲区 代码示例 第一部分 第二部分 选择器 Socket 处理粘包 & 断包问题 第一个问题:对于粘包问题的解决 第二个问题:对于断包问题的解决 示例代码 ...

  4. 手写简单的HttpServer基于Java nio 实现socket异步通信(请求映射注解方式)

    HttpServer服务类 1 package javax.servlet.http.server2; 2 3 import java.io.IOException; 4 import java.ne ...

  5. zbb20180930 java,nio,netty Netty5.0用法

    Netty5.0用法 Maven坐标       <dependencies>            <!-- https://mvnrepository.com/artifact/ ...

  6. java中socket类_Java中的Socket的用法

    Java中的Socket的用法 Java中的Socket分为普通的Socket和NioSocket. 普通Socket的用法 Java中的网络通信时通过Socket实现的,Socket分为Server ...

  7. java nio keyiterator.remove()_Java SelectionKey.isValid方法代碼示例

    本文整理匯總了Java中java.nio.channels.SelectionKey.isValid方法的典型用法代碼示例.如果您正苦於以下問題:Java SelectionKey.isValid方法 ...

  8. java.nio.ByteBuffer用法小结

    转载自  java.nio.ByteBuffer用法小结 在NIO中,数据的读写操作始终是与缓冲区相关联的.读取时信道(SocketChannel)将数据读入缓冲区,写入时首先要将发送的数据按顺序填入 ...

  9. java实现一个socks5代理 一了解nio Selector的基本用法

    上面浏览器可以放心的将数据交给代理了,接下来做的是怎样处理这些数据,以及面对浏览器突然过来的很多连接如何处理他们,这就要用到javax.nio包下的东西了,下面做简单介绍,不会nio可以先去学习下ni ...

最新文章

  1. C语言二叉查找树练习:单词查找
  2. 5g虚拟技术旅游_5G赋能VR产业变革
  3. 这 8 份「Paper + Code」,你一定用得上 | PaperDaily #08
  4. linux推出mysql对话_以及如何配置它以与Linux平台上的MySQL数据库对话
  5. Android中文图混排时文图的居中对齐 FontMetrics以及自定义ImageSpan实现
  6. YBTOJ:染颜色(KDtree)
  7. SSM整合(相关jar包需求)
  8. c语言side输出空心正方形,回溯法--正方形(蛋糕切分)问题
  9. 服务器远程桌面日志,Windows记录远程桌面3389登录日志
  10. 时间计算题100道_史上最全50道初中数学几何必刷题(上)
  11. python合并大量ts文件_Python3 根据m3u8下载视频,批量下载ts文件并且合并
  12. 计算机网络重置点命令,重置网络命令 重装系统如何重置网络命令
  13. CESM优化——Intel编译器编译安装NetCDF库(C+Fortran)
  14. 软件设计是怎样炼成的(5)——规划系统的骨架(架构设计)(上篇)
  15. 最值得收藏的Bootstrap免费字体和图标网站
  16. 多家技术公司喊停的人脸识别业务,被这家波兰网站玩火了!
  17. 51单片机c语言dac0832产生波形,基于51单片机的DAC0832波形发生器设计
  18. 哥德巴赫猜想:任意大于6的偶数都可以被分解成两个素数之和
  19. kotlin-协程-lzf
  20. c51单片机汇编语言语法错误,关于c51单片机交通灯汇编程序怎么加一个紧急状态按键的问题...

热门文章

  1. javascript xml转json
  2. LeetCode 145 二叉树的后序遍历(非递归)
  3. 映射内网ftp服务器到公网报错问题解决
  4. netty的使用部署
  5. window端口号被占用解决
  6. python读取文件乱码
  7. 【分布计算环境学习笔记】3 软件构件结构
  8. [English] notes
  9. 【python】Python的基本数据类型之数字类型与字符串类型
  10. 【elasticsearch】org.elasticsearch.cluster.block.ClusterBlockException: blocked by: [SERVICE_UNAVAILA