很长一段时间内,大多数网络通信方式都是阻塞模式,即:
· 客户端 向服务器端发出请求后,客户端会一直处于等待状态(不会再做其他事情),直到服务器端返回结果或者网络出现问题 。
· 服务器端同样如此,当在处理某个客户端 A 发来的请求时,另 一个客户端 B 发来的请求会等待,直到服务器端的处理线程完成上一个请求的处理。

Java 对阻塞模式的支持,就是由 java. net 包中的 Socket 套接字功能完成的 。 这里要说明 一下 , Socket 套接字是 TCP/IP 等传输层协议在高级编程语言中的具体体现 。 例如客户端使用
TCP 协议连接这台服务器的时候,当 TCP 三次握手成功后,应用程序就会创建一个 Socket 套接字对象(注意,这时还没有进行数据内容的传输),当这个 TCP 连接出现数据传输时,Socket 套接字就会把数据传输的表现告诉程序员 。

客户端代码

package testBlockSocket;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.CountDownLatch; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class SocketClientDaemon {
public static void main(String[] args) throws Exception {
Integer clientNumber = 20;
CountDownLatch countDownLatch = new CountDownLatch(clientNumber);
// 分别开始启动这 20 个客户端
for (int index = 0; index < clientNumber; index++, countDownLatch.countDown()) {
SocketClientRequestThread client = new SocketClientRequestThread(countDownLatch, index);
Thread thread = new Thread(client);
thread.start();
}
// 这个同步锁不涉及具体的实验逻辑,只是保证守护线程在启动所有线程后,不会退出
synchronized (SocketClientDaemon.class) {
SocketClientDaemon.class.wait();
}
}
} class SocketClientRequestThread implements Runnable { private final static Logger LOGGER = LoggerFactory.getLogger(SocketClientRequestThread.class); private CountDownLatch countDownLatch;
// 这个线程的编号
private Integer clientindex; // countDownLatch 是 Java 提供的线程同步计数器。
// 当计数器数值减为 0 时,所有受其影响而阻塞的线程将会被激活。尽可能模拟并发请求的真实性(但实际上也并不是完全并发的)
public SocketClientRequestThread(CountDownLatch countDownLatch, Integer clientindex) {
this.countDownLatch = countDownLatch;
this.clientindex = clientindex;
} @Override
public void run() {
Socket socket = null;
OutputStream clientRequest = null;
InputStream clientResponse = null;
try {
socket = new Socket("localhost", 8888);
clientRequest = socket.getOutputStream();
clientResponse = socket.getInputStream();
// 阻塞,直到 SocketClientDaemon 完成所有线程的启动,然后所有线程一起发送请求
this.countDownLatch.await(); // 发送请求信息
clientRequest.write((" 这是第" + this.clientindex + " 个客户端的请求。").getBytes());
clientRequest.flush();
clientRequest.write((" 这是第" + this.clientindex + " 个客户端的 over ").getBytes());
clientRequest.flush(); // 在这里等待 , 直到服务器返回信息
SocketClientRequestThread.LOGGER.info("第" + this.clientindex + "个客户端的请求发送完成, 等待服务器返回信息");
int maxLen = 1024;
byte[] contextBytes = new byte[maxLen];
int realLen;
String message = "";
// 程序执行到这里 , 会一直等待服务器返回信息
// (注意,前键是 in 和 out 都不能关闭,如果关闭了就收不到)
while ((realLen = clientResponse.read(contextBytes, 0, maxLen)) != -1) {
message += new String(contextBytes, 0, realLen);
SocketClientRequestThread.LOGGER.info("接收 到 来自服务器的信息 :" + message);
}
} catch (Exception e) {
SocketClientRequestThread.LOGGER.error(e.getMessage(), e);
} finally {
// 记得关闭连接
try {
clientRequest.close();
clientResponse.close();
socket.close();
} catch (Exception e) {
SocketClientRequestThread.LOGGER.error(e.getMessage(), e);
}
}
}
}

服务端代码

package testBlockSocket;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class SocketServer1 { private final static Logger LOGGER = LoggerFactory.getLogger(SocketServer1.class); public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8888);
try {
while (true) {
// 这里 Java 通过 JNI 请求操作系统,并等待操作系统返回结果或出错
Socket socket = serverSocket.accept();
// 下面我们收取信息(这里还是阻塞式的, 一直等待 ,直到有数据可以接收 )
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
Integer sourcePort = socket.getPort();
int maxLen = 2048;
byte[] contextBytes = new byte[maxLen];
int realLen;
StringBuffer message = new StringBuffer();
// 试图读数据的时候,程序也会被阻塞,直到操作系统把网络传来的数据准备好 。
while ((realLen = in.read(contextBytes, 0, maxLen)) != -1) {
message.append(new String(contextBytes, 0, realLen));
// 我们假设读取到"over"关键字表示一段内容传输完成
if (message.indexOf("over") != -1) {
break;
}
}
// 下面打印信息
LOGGER.info("服务器收到来自于端口 : " + sourcePort + "的信息:" + message);
// 下面开始发送信息
out.write("回发响应信息 !".getBytes());
// 关闭
out.close();
in.close();
socket.close();
}
} catch (Exception e) {
SocketServer1.LOGGER.error(e.getMessage(), e);
} finally {
if (serverSocket != null) {
serverSocket.close();
}
}
}
}

java -classpath ./;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\slf4j-api-1.7.25.jar;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\log4j-api-2.10.0.jar;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\logback-core-1.2.3.jar;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\logback-classic-1.2.3.jar testBlockSocket.SocketServerl

网络I/O模型--01阻塞模式(普通)的更多相关文章

  1. 网络I/O模型--02阻塞模式(多线程)

    当服务器收到客户端 X 的请求后(读取到所有请求数据后),将这个请求送入一个独立线程进行处理,然后主线程继续接收客户端 Y 的请求. 客户端一侧也可以使用一个子线程和服务器端进行通信.这样客户端主线程 ...

  2. Socket 阻塞模式和非阻塞模式

    阻塞I/O模型: 简介:进程会一直阻塞,直到数据拷贝 完成 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好. 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返 ...

  3. 简明网络I/O模型---同步异步阻塞非阻塞之惑

    转自:http://www.jianshu.com/p/55eb83d60ab1 网络I/O模型 人多了,就会有问题.web刚出现的时候,光顾的人很少.近年来网络应用规模逐渐扩大,应用的架构也需要随之 ...

  4. 网络I/O模型---同步异步阻塞非阻塞之惑

    网络I/O模型 人多了,就会有问题.web刚出现的时候,光顾的人很少.近年来网络应用规模逐渐扩大,应用的架构也需要随之改变.C10k的问题,让工程师们需要思考服务的性能与应用的并发能力. 网络应用需要 ...

  5. UNIX网络编程读书笔记:I/O模型(阻塞、非阻塞、I/O复用、信号驱动、异步)

    I/O模型 UNIX下可用的5种I/O模型: (1)阻塞I/O (2)非阻塞I/O (3)I/O复用(select和poll) (4)信号驱动I/O(SIGIO) (5)异步I/O 对于一个套接口上的 ...

  6. 网络IO模型 非阻塞IO模型

    网络IO模型 非阻塞IO模型 同步 一件事做完后再做另一件事情 异步 同时做多件事情 相对论 多线程 多进程 协程 异步的程序 宏观角度:异步 并发聊天 阻塞IO 阻塞IO的问题 一旦阻塞就不能做其他 ...

  7. 网络IO-阻塞、非阻塞、IO复用、异步

    网络socket输入操作分为两个阶段:等待网络数据到达和将到达内核的数据复制到应用进程缓冲区.对这两个阶段不同的处理方式将网络IO分为不同的模型:IO阻塞模型.非阻塞模型.多路复用和异步IO. 一 阻 ...

  8. 转:PHP中实现非阻塞模式

    原文来自于:http://blog.csdn.net/linvo/article/details/5466046 程序非阻塞模式,这里也可以理解成并发.而并发又暂且可以分为网络请求并发 和本地并发 . ...

  9. Java I/O演进与Linux网络I/O模型

    参考文章: 简书-浅谈Linux五种IO:http://www.jianshu.com/p/486b0965c296 一.linux基础概念 1.1 内存空间 linux系统中的使用的是虚拟存储器,即 ...

随机推荐

  1. 初次学习Vue,输出Hello Vue!

    Vue.js作为目前比较流行的js框架,而我却迟迟没有接触,深感不安! 使用vue之前先要下载vue.js文件,然后在html里面导入vue.js文件,下面试着输出"Hello Vue!&q ...

  2. 【Spring】Spring MVC文件上传--整合bootstrap-fileinput和jQuery-File-Upload

    前言 这里分享两个使用Spring MVC进行文件上传的简单示例, 分别整合bootstrap-fileinput 和 Jquery File Upload , 代码十分简单, 都是入门的示例,因此这 ...

  3. 【Java并发编程】:加锁和volatile变量

    加锁和volatile变量两者之间的区别: 1.volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比syn ...

  4. 09 - JavaSE之线程

    线程 线程的基本概念 线程是一个程序里面不同的执行路径. 进程与线程的区别 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换开销大. 线程可以看作轻量级的进程,同一类线程共享代码和数据空间 ...

  5. 设置nginx反向代理将80端口转发到9999端口

    关于linux系统不允许个人账户使用80端口,导致无法将express默认的bin/www内3000端口改为80端口一个问题,可以使用sudo来强制启动,当然,这样显然不合适,这时候,可以使用ngin ...

  6. JDK自带工具介绍

    JDK工具一览表 工具名称 功能描述 appletviewer.exe 用于运行并浏览applet小程序. apt.exe 注解处理工具(Annotation Processing Tool),主要用 ...

  7. tomcat启动(一)startup.bat|catalina.bat分析

    环境:windows X64位 Tomcat8.0.47 bootstrap.jar是tomcat的内核 开始位置 startup.bat 查看文本 具体的批处理脚本语法可以查看我整理的文章 http ...

  8. ubuntu16.04安装cuda,无法定位软件包问题

    为了学习深度学习,这几天在安装深度学习框架,cuda安装时却出现无法定位软件包的问题.cuda官网下载时提供了deb和run格式,今天只讲deb格式的安装包安装过程的问题. 按照官方教程,下载好cud ...

  9. Windows下整合apache和Tomcat

    1.前言: 引用:http://www.cnblogs.com/liaokailin/p/3963603.html 引用的博客里面有比较详细的说明,并且结合图片,非常生动: 我这里只做简单的说明和整合 ...

  10. mac 上安装 redis

    1.从http://redis.io 下载redis包,这里选择了redis-3.2.3 2.将下载的 redis-3.2.3.tar.gz 包拷贝到 /usr/local 目录 3.执行 sudo ...