记住NIO在jdk1.7版本之前是同步非阻塞的,以前的inputsream是同步阻塞的,上面学习完成了Buffer现在我们来学习channel

channel书双向的,以前阻塞的io的inputstream都是单向的

channel有四种连接状态 connect accept  read  write ,nio的核心基础就是selector

package bhz.nio;

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.util.Iterator; public class Server implements Runnable{
//1 多路复用器(管理所有的通道)
private Selector seletor;
//2 建立缓冲区
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
//3
private ByteBuffer writeBuf = ByteBuffer.allocate(1024);
public Server(int port){
try {
//1 打开路复用器
this.seletor = Selector.open();
//2 打开服务器通道
ServerSocketChannel ssc = ServerSocketChannel.open();
//3 设置服务器通道为非阻塞模式
ssc.configureBlocking(false);
//4 绑定地址
ssc.bind(new InetSocketAddress(port));
//5 把服务器通道注册到多路复用器上,并且监听阻塞事件
ssc.register(this.seletor, SelectionKey.OP_ACCEPT); System.out.println("Server start, port :" + port); } catch (IOException e) {
e.printStackTrace();
}
} @Override
public void run() {
while(true){
try {
//1 必须要让多路复用器开始监听
this.seletor.select();
//2 返回多路复用器已经选择的结果集
Iterator<SelectionKey> keys = this.seletor.selectedKeys().iterator();
//3 进行遍历
while(keys.hasNext()){
//4 获取一个选择的元素
SelectionKey key = keys.next();
//5 直接从容器中移除就可以了
keys.remove();
//6 如果是有效的
if(key.isValid()){
//7 如果为阻塞状态
if(key.isAcceptable()){
this.accept(key);
}
//8 如果为可读状态
if(key.isReadable()){
this.read(key);
}
//9 写数据
if(key.isWritable()){
//this.write(key); //ssc
}
} }
} catch (IOException e) {
e.printStackTrace();
}
}
} private void write(SelectionKey key){
//ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//ssc.register(this.seletor, SelectionKey.OP_WRITE);
} private void read(SelectionKey key) {
try {
//1 清空缓冲区旧的数据
this.readBuf.clear();
//2 获取之前注册的socket通道对象
SocketChannel sc = (SocketChannel) key.channel();
//3 读取数据
int count = sc.read(this.readBuf);
//4 如果没有数据
if(count == -1){
key.channel().close();
key.cancel();
return;
}
//5 有数据则进行读取 读取之前需要进行复位方法(把position 和limit进行复位)
this.readBuf.flip();
//6 根据缓冲区的数据长度创建相应大小的byte数组,接收缓冲区的数据
byte[] bytes = new byte[this.readBuf.remaining()];
//7 接收缓冲区数据
this.readBuf.get(bytes);
//8 打印结果
String body = new String(bytes).trim();
System.out.println("Server : " + body); // 9..可以写回给客户端数据 } catch (IOException e) {
e.printStackTrace();
} } /**
* channel处于阻塞状态,获得channel肯定是服务器端的channel
ssc.register(this.seletor, SelectionKey.OP_ACCEPT);
*/
private void accept(SelectionKey key) {
try {
//1 获取服务通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//2 执行阻塞方法
SocketChannel sc = ssc.accept();
//3 设置阻塞模式,sc是客户端对应的channel
sc.configureBlocking(false);
//4 客户端的通道注册到多路复用器上,并设置读取标识
sc.register(this.seletor, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) { new Thread(new Server(8765)).start();;
} }

server必须实现Runnable接口,开启一个线程让selector去轮询channel的状态

server读写数据都需要缓冲区

分析下流程:

1、第一步需要打开多路复用器;

2、第二步将服务器端的channel注册到seletor中,模式设置成非阻塞的模式,模式设置成accept模式,因为服务端的代码先运行,所以selector首先监听到的肯定是服务器注册的accept

3、获得客户端的channel,将客户端的chanel注册到selector上面,模式设置成read模式,模式也设置成非阻塞模式

记住:

//2 执行阻塞方法
SocketChannel sc = ssc.accept();

这个方法是阻塞的,用来监听客服端连接的channel,返回客户端SocketChannel

客户端的代码:

package bhz.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel; public class Client { //需要一个Selector
public static void main(String[] args) { //创建连接的地址
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8765); //声明连接通道
SocketChannel sc = null; //建立缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); try {
//打开通道
sc = SocketChannel.open();
//进行连接
sc.connect(address); while(true){
//定义一个字节数组,然后使用系统录入功能:
byte[] bytes = new byte[1024];
System.in.read(bytes); //把数据放到缓冲区中
buf.put(bytes);
//对缓冲区进行复位
buf.flip();
//写出数据
sc.write(buf);
//清空缓冲区数据
buf.clear();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(sc != null){
try {
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} } }

强度:

首先看 select() 方法,该方法会一直阻塞下去,直到选择器中的通道关注的事件就绪:

selector.select();
  参数5000是5秒,参数以毫秒为单位。这个方法会一直阻塞5秒,5秒之内如果没有通道事件就绪的话程序会往下运行:

selector.select(5000);
  selectNow()其实就是非阻塞,无论有无通道事件就绪,程序都会向下执行:

selector.selectNow();
  这三个方法的本质区别无非是选择器阻塞或者等待一个或多个通道的事件就绪有多长时间。

所以select()方法是线程阻塞的,必须有channel注册成功到seletor中,否则服务器线程会被一直阻塞,这在面试中经常被问到

java scoket Blocking 阻塞IO socket通信四的更多相关文章

  1. java scoket Blocking 阻塞IO socket通信二

    在上面一节中,服务端收到客户端的连接之后,都是new一个新的线程来处理客户端发送的请求,每次new 一个线程比较耗费系统资源,如果100万个客户端,我们就要创建100万个线程,相当的 耗费系统的资源, ...

  2. java scoket Blocking 阻塞IO socket通信一

    package bhz.bio; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; p ...

  3. java scoket Blocking 阻塞IO socket通信三

    在NIO同步非阻塞的场景中和原来同步阻塞最大的却别就是引入了上面的Buffer对象,现在我们来学校上面的BUffer对象 我们来看看程序的代码: package bhz.nio.test; impor ...

  4. Java与C之间的socket通信

    最近正在开发一个基于指纹的音乐检索应用,算法部分已经完成,所以尝试做一个Android App.Android与服务器通信通常采用HTTP通信方式和Socket通信方式.由于对web服务器编程了解较少 ...

  5. java多线程实现多客户端socket通信

    一.服务端 package com.czhappy.hello.socket; import java.io.IOException; import java.net.InetAddress; imp ...

  6. Delphi和JAVA用UTF-8编码进行Socket通信例子

    最近的项目(Delphi开发),需要经常和java语言开发的系统进行数据交互(Socket通信方式),数据编码约定采用UTF-8编码. 令我无语的是:JAVA系统那边反映说,Delphi发的数据他们收 ...

  7. java与c/c++进行socket通信

    比如Server端只接收一个结构Employee,定义如下: struct UserInfo {   char UserName[20];   int UserId; }; struct Employ ...

  8. java nio--采用Selector实现Socket通信

    server: /** * 选择器服务端 * Created by ascend on 2017/6/9 9:30. */ public class SelectorServer { // publi ...

  9. java centos7 gcc编码 解决socket通信 汉字乱码

    1.把 Java eclipes 设置编码成utf-8 windows->preference->workspace 2.centos7 gcc 默认为utf-8

随机推荐

  1. 插入与读取Blob类型数据

    BlobTest package com.aff.PreparedStatement; import java.io.File; import java.io.FileInputStream; imp ...

  2. SpringBoot--SpringMVC自动配置

    SpringMVC自动配置 1.SpringBoot官方文档对SpringMVC的默认配置: Inclusion of ContentNegotiatingViewResolver and BeanN ...

  3. CVE-2017-9993 FFMpeg漏洞利用

    漏洞原理: 更改连接直接发送请求,造成ssrf 漏洞利用: 脚本地址:https://github.com/neex/ffmpeg-avi-m3u-xbin 用法: 生成一个读取/etc/passwd ...

  4. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  5. css背景图定位和浮动

    网站图标引入:<link rel="shortcut icon" href="ico图标地址"> 背景图片  background-image: u ...

  6. Java实现高效便捷还容易懂的排序算法

    PS:我现在越来越认为排序大法是,很深的算法了,就是简单的几个步骤,网上的大佬们能给你玩出花来(ง •_•)ง public class zimuzhenlie2 { public static vo ...

  7. Java实现 LeetCode 169 多数元素

    169. 多数元素 给定一个大小为 n 的数组,找到其中的多数元素.多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素. 你可以假设数组是非空的,并且给定的数组总是存在多数元素. 示例 1: 输 ...

  8. 第五届蓝桥杯C++B组国(决)赛真题

    解题代码部分来自网友,如果有不对的地方,欢迎各位大佬评论 题目1.年龄巧合 小明和他的表弟一起去看电影,有人问他们的年龄.小明说:今年是我们的幸运年啊.我出生年份的四位数字加起来刚好是我的年龄.表弟的 ...

  9. java实现测量到的工程数据

    [12,127,85,66,27,34,15,344,156,344,29,47,-] 这是某设备测量到的工程数据. 因工程要求,需要找出最大的 5 个值. 一般的想法是对它排序,输出前 5 个.但当 ...

  10. java实现猜生日

    ** 猜生日** 今年的植树节(2012年3月12日),小明和他的叔叔还有小伙伴们一起去植树.休息的时候,小明的同学问他叔叔多大年纪,他叔叔说:"我说个题目,看你们谁先猜出来!" ...