1.基于tcp的io阻塞式线程io的线程版本
一个服务器端一般都需要同时为多个客户端提供通讯,如果需要同时支持多个客户端,则必须使用前面介绍的线程的概念。简单来说,也就是当服务器端接收到一个连接时,启动一个专门的线程处理和该客户端的通讯。
按照这个思路改写的服务端示例程序将由两个部分组成,MulThreadSocketServer类实现服务器端控制,实现接收客户端连接,然后开启专门的逻辑线程处理该连接,LogicThread类实现对于一个客户端连接的逻辑处理,将处理的逻辑放置在该类的run方法中。该示例的代码实现为:
server端:

package tcp;import java.net.ServerSocket;
import java.net.Socket;
/*** 支持多客户端的服务器端实现*/
public class MulThreadSocketServer {public static void main(String[] args) {ServerSocket serverSocket = null;Socket socket = null;//监听端口号int port = 10000;try {//建立连接serverSocket = new ServerSocket(port);System.out.println("服务器已启动:");while(true){//获得连接socket = serverSocket.accept();//启动线程new LogicThread(socket);}} catch (Exception e) {e.printStackTrace();}finally{try{//关闭连接serverSocket.close();}catch(Exception e){}}}
}

客户线程端:

package tcp;import java.io.*;
import java.net.*;
/*** 服务器端逻辑线程*/
public class LogicThread extends Thread {Socket socket;InputStream is;OutputStream os;public LogicThread(Socket socket){this.socket = socket;start(); //启动线程}public void run(){byte[] b = new byte[1024];try{//初始化流os = socket.getOutputStream();is = socket.getInputStream();for(int i = 0;i < 3;i++){//读取数据int n = is.read(b);//逻辑处理byte[] response = logic(b,0,n);//反馈数据os.write(response);}}catch(Exception e){e.printStackTrace();}finally{close();}}/*** 关闭流和连接*/private void close(){try{//关闭流和连接os.close();is.close();socket.close();}catch(Exception e){}}/*** 逻辑处理方法,实现echo逻辑* @param b 客户端发送数据缓冲区* @param off 起始下标* @param len 有效数据长度* @return*/private byte[] logic(byte[] b,int off,int len){byte[] response = new byte[len];//将有效数据拷贝到数组response中System.arraycopy(b, 0, response, 0, len);return response;}
}

NIO的编程
第一步:创建 Selector 选择器
第二步:创建 ServerSocketChannel 通道,并绑定监听端口
第三步:设置 Channel 通道是非阻塞模式
第四步:把 Channel 注册到 Socketor 选择器上,监听连接事件
第五步:调用 Selector 的 select 方法(循环调用),监测通道的就绪状况
第六步:调用 selectKeys 方法获取就绪 channel 集合
第七步:遍历就绪 channel 集合,判断就绪事件类型,实现具体的业务操作
第八步:根据业务,决定是否需要再次注册监听事件,重复执行第三步操作

服务端:

 /***  服务端代码*/@Testpublic void selectorServer() throws Exception{// 获取服务通道ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();// 切换为非阻塞模式serverSocketChannel.configureBlocking(false);// 创建buffer缓冲区ByteBuffer serverByteBuffer = ByteBuffer.allocate(1024);// 绑定端口号,做到监听的过程serverSocketChannel.bind(new InetSocketAddress("localhost",8080));// 获取selector选择器Selector selector = Selector.open();// 通道注册到选择器,进行监听serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 选择器进行轮询,查看那些通道是处于就行状态,进一步操作// while (selector.select()>0){while (selector.select()>0){Set<SelectionKey> selectionKeys = select99999or.selectedKeys();//遍历Iterator<SelectionKey> iterato9r = selectionKeys.iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();// 是否就绪接收状态if (key.isAcceptable()){// 获取连接SocketChannel accept = serverSocketChannel.accept();// 切换非阻塞模式accept.configureBlocking(false);// 注册accept.register(selector, SelectionKey.OP_READ);}else if (key.isReadable()){ // 是否就绪读取状态SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer=ByteBuffer.allocate(1024);// 读取数据int length=0;while ((length=socketChannel.read(serverByteBuffer))>0){serverByteBuffer.flip();System.out.println(new String(serverByteBuffer.array(),0,length));serverByteBuffer.clear();}}}iterator.remove();}}

客户端:

    public static void main(String[] args) throws Exception {// 获取通道SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost",8080));// 设置为非阻塞模式socketChannel.configureBlocking(false);// 创建buffer缓冲区 写入数据ByteBuffer buffer = ByteBuffer.allocate(1024);System.out.print("please input message:");Scanner scanner = new Scanner(System.in);while (scanner.hasNext()){System.out.print("please input message:");String input = scanner.next();buffer.put((new Date()+"-->"+input).getBytes());// 模式切换buffer.flip();// 写入通道socketChannel.write(buffer);// 关闭buffer.clear();}}

NIO的服务器和客户端双相数据交互例子:
服务端

package nio_demo_01;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;/*** @Author: yesiming* @Platform: Mac* @Date: 10:31 AM 2019/10/16** NIO Demo*/
public class B_Server implements Runnable {private Selector selector;private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);private ByteBuffer writeBuffer = ByteBuffer.allocate(1024);public B_Server(int port) {try {// 1. 打开多路复用this.selector = Selector.open();// 2. 打开服务器通道ServerSocketChannel ssc = ServerSocketChannel.open();// 3. 设置服务器通道为阻塞模式ssc.configureBlocking(false);// 4. 绑定端口ssc.bind(new InetSocketAddress(port));// 5. 把服务器channel注册到选择器中,监听阻塞事件ssc.register(this.selector, SelectionKey.OP_ACCEPT);System.out.println("注册服务端channel");} catch (IOException e) {e.printStackTrace();}}@Overridepublic void run() {while(true) {try {System.out.println("进入run 大轮询开始");// 1. 让选择器开始监听System.out.println("打开选择器监听事件");this.selector.select(); // 这里是阻塞的,返回至少一个已经准备好的channel// 2. 返回选择器结果集System.out.println("select阻塞结束");Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();System.out.println("keys:" + keys);// 3. 遍历while(keys.hasNext()) {// 4. 获取其中一个元素SelectionKey key = keys.next();// 5. 可以移除掉/*channel是注册在selector中的,在后面的轮询中,是先将已准备好的channel挑选出来,即selector.select(),再通过selectedKeys()生成的一个SelectionKey迭代器进行轮询的,一次轮询会将这个迭代器中的每个SelectionKey都遍历一遍,每次访问后都remove()相应的SelectionKey,但是移除了selectedKeys中的SelectionKey不代表移除了selector中的channel信息(这点很重要),注册过的channel信息会以SelectionKey的形式存储在selector.keys()中,也就是说每次select()后的selectedKeys迭代器中是不能还有成员的,但keys()中的成员是不会被删除的(以此来记录channel信息)。*/keys.remove();// 6. 判断有效性if(key.isValid()) {// 7. 监听【阻塞】状态的selectorKeyif(key.isAcceptable()) {System.out.println("监听到【阻塞(Accept)】key: " + key);this.accept(key);}// 8. 监听【可读】状态的selectorKeyif(key.isReadable()) {System.out.println("监听到【可读】key: " + key);this.read(key);}// 9. 监听【可写】状态的selectorKeyif(key.isWritable()) {System.out.println("监听到【可写】key: " + key);this.write(key);}}}} catch (IOException e) {e.printStackTrace();}}}private void accept(SelectionKey key) {try {System.out.println("进入accept()");// 1. 获取服务器通道ServerSocketChannel ssc = (ServerSocketChannel)key.channel();// 2. 执行阻塞方法SocketChannel sc = ssc.accept();// 3. 设置阻塞模式:非阻塞sc.configureBlocking(false);// 4. 将客户端通道注册到选择器上,并设置监听【可读】标实位/*** 如果客户端向服务端发送了数据,那么执行以下步骤* 4-1. 服务端的服务器操作系统内核空间接收客户端发来的数据* 4-2. 内核空间接收完所有的数据之后,将该客户端与服务端连接的socket的文件描述符设置为可读* 4-3. selector这时结束阻塞(我猜测:java的selector调用系统的socket()函数,select()函数判断有事件产生,就结束阻塞,返回给java的selector)*/sc.register(this.selector, SelectionKey.OP_READ);System.out.println("设置监听客户端channel的监听状态为可读状态");} catch (IOException e) {e.printStackTrace();}}private void read(SelectionKey key) {System.out.println("进入read()");try {// 1. 清空缓冲期数据// 如果不clear(),那么会导致buffer的position位置与limit位置相同,无法往buffer里写入数据// 导致下面的sc.read(this.byteBuffer)方法返回0this.byteBuffer.clear();// 2. 获取之前注册到选择器的socket通道SocketChannel sc = (SocketChannel)key.channel();// 3. 读取数据(对于butybuffer,是写入数据,所以下面从bytebuffer里读取数据之前,需要flip)/*** 接上accept()注释的 4-3* 这里从通道读取数据,应该是从系统内核空间把数据复制到jvm进程空间,也就是服务端*/int count = sc.read(this.byteBuffer); // int是读取到的字节个数, 如果没读到数据,那么就返回-1// 4. 如果没有数据if(count == -1) {key.channel().close();key.cancel();return;}// 5. 有数据就读取,读取之前需要进行flip(翻转bytebuffer)this.byteBuffer.flip();// 6. 根据缓冲区的数据长度创建相应大小的byte数组,接受缓冲区数据byte[] bytes = new byte[byteBuffer.remaining()];// 7. 接收缓冲区数据this.byteBuffer.get(bytes);// 8. 打印结果String body = new String(bytes).trim();System.out.println("Server: " + body);// 9. 可以写回给客户端数据sc.configureBlocking(false);sc.register(this.selector, SelectionKey.OP_WRITE);System.out.println("读取数据完毕,将当前客户端通道的监听事件修改为【可写】");} catch (IOException e) {e.printStackTrace();}}private void write(SelectionKey key) {System.out.println("进入write()");SocketChannel sc = (SocketChannel)key.channel();try {
//            sc.register(this.selector, SelectionKey.OP_WRITE);byte[] bytes = "呵呵".getBytes();writeBuffer.put(bytes);writeBuffer.flip();sc.write(writeBuffer);writeBuffer.clear();key.channel().close();key.cancel();
//            sc.register(this.selector, SelectionKey.OP_READ);} catch (ClosedChannelException e) {e.printStackTrace();} catch (IOException e) {}}public static void main(String[] args) {new Thread(new B_Server(8910)).start();}}

客户端代码

package socket_demo_01;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.SocketChannel;
import java.util.Iterator;/*** @Author: yesiming* @Platform: Mac* @Date: 11:39 AM 2019/10/16** 单向通信的客户端【方法分离】*/
public class B_Client implements Runnable {private SocketChannel sc = null;private Selector selector = null;public static void main(String[] args) {B_Client b_client = new B_Client();new Thread(b_client).start();b_client.write();}public B_Client() {InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8910);try {sc = SocketChannel.open();sc.connect(address);sc.configureBlocking(false); // NotYetConnectedExceptionthis.selector = Selector.open();sc.register(this.selector, SelectionKey.OP_READ);} catch (IOException e) {e.printStackTrace();}}private void write() {ByteBuffer byteBuffer = ByteBuffer.allocate(1024);try {while (true) {byte[] bytes = new byte[1024];System.in.read(bytes);byteBuffer.put(bytes);byteBuffer.flip();sc.write(byteBuffer);byteBuffer.clear();}} catch (IOException e) {}}private void read(SelectionKey key) throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocate(1024);try {// 2. 获取之前注册到选择器的socket通道SocketChannel sc = (SocketChannel)key.channel();// 3. 读取数据int count = sc.read(byteBuffer);// 4. 如果没有数据if(count == -1) {key.channel().close();key.cancel();return;}// 5. 有数据就读取,读取之前需要进行flip(把position 和limit进行固定)byteBuffer.flip();// 6. 根据缓冲区的数据长度创建相应大小的byte数组,接受缓冲区数据byte[] bytes = new byte[byteBuffer.remaining()];// 7. 接收缓冲区数据byteBuffer.get(bytes);// 8. 打印结果String body = new String(bytes).trim();System.out.println("Server: " + body);// 记住,一定要1. 关闭通道,2. 取消keykey.channel().close();key.cancel();} catch (IOException e) {e.printStackTrace();} finally {}}@Overridepublic void run() {while(true){try {this.selector.select();Iterator<SelectionKey> keys = this.selector.selectedKeys().iterator();while (keys.hasNext()) {SelectionKey key = keys.next();if(key.isValid()){if(key.isReadable()){this.read(key);}}keys.remove();}} catch (IOException e) {e.printStackTrace();}}}
}

java的io和nio例子相关推荐

  1. Java之IO,BIO,NIO,AIO知多少?

    开心一笑 [一女人:"我真不放心丈夫,他准备到湖中心水最深的地方把猫扔掉."邻居:"那有什么不放心的?"女人:"猫已回家一钟头了!"] 提出 ...

  2. Java之IO,BIO,NIO,AIO

    2019独角兽企业重金招聘Python工程师标准>>> 参考文献一 IO基础知识回顾 java的核心库java.io提供了全面的IO接口.包括:文件读写.标准设备输出等.Java中I ...

  3. Java中IO和NIO的本质和区别

    文章目录 简介 IO的本质 DMA和虚拟地址空间 IO的分类 IO和NIO的区别 总结 简介 终于要写到java中最最让人激动的部分了IO和NIO.IO的全称是input output,是java程序 ...

  4. java中io与nio复制文件性能对比

    2019独角兽企业重金招聘Python工程师标准>>> 1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read( ...

  5. 【Java网络编程与IO流】Java中IO流分为几种?字符流、字节流、缓冲流、输入流、输出流、节点流、处理流

    Java网络编程与IO流目录: [Java网络编程与IO流]Java中IO流分为几种?字符流.字节流.缓冲流.输入流.输出流.节点流.处理流 [Java网络编程与IO流]计算机网络常见面试题高频核心考 ...

  6. 网络编程——使用更简洁且性能高效的Okio库来做IO和NIO

    文章大纲 引言 一.Okio 概述 二.Okio的核心元素 1.[Okio的两种数据类型](https://square.github.io/okio/#bytestrings-and-buffers ...

  7. NIO详解(十三):Java IO 和NIO 总结

    1. 概述 下面总结了Java NIO和IO之间的主要差别 IO NIO 面向流 面向缓冲 阻塞IO 非阻塞IO 无 选择器 2. Java IO和 NIO的主要区别 2.1 面向流和面向缓冲区 Ja ...

  8. Java NIO:IO与NIO的区别

    一.概念 NIO即New IO,这个库是在JDK1.4中才引入的.NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多.在Java API中提供了两套N ...

  9. Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)

    本文会从传统的BIO到NIO再到AIO自浅至深介绍,并附上完整的代码讲解. 下面代码中会使用这样一个例子:客户端发送一段算式的字符串到服务器,服务器计算后返回结果到客户端. 代码的所有说明,都直接作为 ...

最新文章

  1. 技术 | 入门机器学习必须知道的6件事,你可未必都了然于心了
  2. Ajax[Mount]
  3. 数据结构 单链表 C
  4. python为什么这么火 知乎-没想到吧!Google 排名第一的编程语言,为什么会这么火?...
  5. 解决升级 Office 2010 之后 Outlook 提示“无法打开 Microsoft Outlook”
  6. tomcat(5)servlet容器
  7. 如何用Java编写最快的表达式评估器之一
  8. Linux下数据库(sqlite3)学习笔记
  9. jira使用教程pdf_jira项目管理系统使用指南.pdf
  10. android os自动安裝软件,[图]Bliss OS 12进入开发阶段:可在桌面设备上安装Android 10系统...
  11. 全新AI内参:量子位精心打磨,最新产业资讯、科研进展一文呈现
  12. P2922 [USACO08DEC]秘密消息Secret Message
  13. Xamarin iOS教程之使用按钮接接收用户输入
  14. java中this什么时候不能省略?this()的用法
  15. 计算机网络二进制转化为十进制,二进制如何转十进制?二进制转换十进制公式...
  16. 电脑的wifi天线原理_无线网络 WIFI天线原理 (DIY天线知识学习)上文
  17. window下编译ffmpeg--mys2下安装对应库编译ffmpeg
  18. linux服务器cpu/负载占用率100%怎么办?
  19. 富有组织性是通往成功的必要习惯
  20. 电竞达人最爱五款真无线蓝牙耳机,听声辨位低延迟TWS蓝牙耳机助你《夺冠》

热门文章

  1. ajax报错几种原因
  2. 如果你决定离职,请把这五样东西留下,要不然你必身败名裂
  3. 20190722华为OPPO小米夜间省电模式调研
  4. 哈工大软件构造期末复习(根据老师复习提纲整理)
  5. 知情人士:比特大陆创始人吴忌寒离开核心纯属谣言
  6. 【阿里云】在CentOS7系统上安装MySQL8
  7. UnityShader快速上手指南(四)
  8. STM32学习笔记--DAC
  9. 服务器主板性能检测,服务器的主板性能指标
  10. HTML5交互性是什么意思,HTML5的结构和语义(5):交互