【Java】同步阻塞式(BIO)TCP通信
TCP BIO
背景
网络编程的基本模型是Clien/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过socket进行通信。socket相当于通信的媒介,用来传输数据。
Java处理TCP的类主要有Socket和ServerSocket,基于传统同步阻塞模型(Blocking IO)开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
模型
下图是一个BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接受到客户端连接请求后为每一个客户端创建一个新的线程进行链路处理,处理完之后通过流应答给客户端,线程销毁。
这种模型的缺点十分明显,当客户端大量请求到达时,由于线程笨重、占用资源大等特点,服务端无法承受巨大的开销,易导致服务宕机。
代码
首先是客户端代码,在代码中可以看到,客户端向127.0.0.1:20006的地址发送连接请求,当连接建立时,通过IO流输出字符串到socket中,并读取socket返回的信息。代码比较简单,就没写注释了_
Client.java:
public class Client {
public static final int PORT = 20006;
public static final String ADDR = "127.0.0.1";
public static void main(String[] args) {
Socket socket = null;
BufferedReader in = null;
PrintStream out = null;
try {
socket = new Socket(ADDR, PORT);
socket.setSoTimeout(2000);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintStream(socket.getOutputStream());
out.println("1:Hello server!");
out.println("2:Hello server!");
out.println("bye");
String line = in.readLine();
System.out.println("Receive from server: " + line);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (SocketTimeoutException e) {
System.out.println("server time out, exit!");
}
catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务端代码如下,
服务端类首先创建ServerSocket对象,绑定要监听的端口,进行监听循环,当有客户端请求达到时,会触发accept事件并返回一个客户端socket对象,这时,就有多种处理方法了,我在Server.java中用注释写到三种处理客户端socket的方式:
1.单线程处理,即服务端只用一个线程处理客户端请求,一次只能处理一个请求,其他请求来了就在后面阻塞着吧。代码如注释中。
2.多线程处理,为每一个客户端请求分配一个线程去处理,处理完线程销毁,处理线程是Handler.java。
3.线程池处理,这个方法跟第二种方法区别主要在于分配了固定数量的线程,当一个线程处理完一个客户端请求时不会销毁而是回到线程池中。线程池类是HandlerExecutor.java。
Server.java
public class Server {
public static final int PORT = 20006;
public static void main(String[] args) throws IOException {
ServerSocket server = null;
try {
server = new ServerSocket(PORT);
Socket client = null;
System.out.println("Server is listening on " + PORT);
while (true) {
client = server.accept();
//单线程
// BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
// System.out.println(in.readLine());
// PrintWriter out = new PrintWriter(client.getOutputStream(), true);
// out.println("hello client");
// in.close();
// out.close();
//多线程
// new Thread(new Handler(client)).start();
//线程池
HandlerExecutor executor = new HandlerExecutor(10, 100);
executor.execute(new Handler(client));
// ExecutorService executor = Executors.newScheduledThreadPool(10);
// executor.submit(new Handler(client));
}
} finally {
if (server != null) {
System.out.println("server is closed!");
server.close();
}
}
}
}
处理类Handler.java
public class Handler implements Runnable {
private Socket socket;
public Handler (Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader in = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
while (true) {
String s = in.readLine();
if (s == null || "bye".equals(s)) {
break;
}
System.out.println(s);
}
PrintWriter out = new PrintWriter(this.socket.getOutputStream(), true);
out.println("hello client");
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
线程池类:HandlerExecutor.java
public class HandlerExecutor {
private ExecutorService executor;
public HandlerExecutor(int maxPoolSize, int queueSize) {
executor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), maxPoolSize, 120L,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(queueSize));
}
public void execute(Runnable task) {
executor.submit(task);
}
}
参考文献
- 《Netty权威指南》2.1章节
【Java】同步阻塞式(BIO)TCP通信的更多相关文章
- IO通信模型(一)同步阻塞模式BIO(Blocking IO)
几个概念 阻塞IO 和非阻塞IO 这两个概念是程序级别的.主要描述的是程序请求操作系统IO操作后,如果IO资源没有准备好,那么程序该如何处理的问题:前者等待:后者继续执行(但是使用线程一直轮询,直到有 ...
- java网络通信:同步阻塞式I/O模型(BIO)
缺点:一个线程只能处理一个客户端连接 服务端: public class TimeServer { public static void main(String[] args) throws IOEx ...
- Java NIO阻塞式通信
package com.nio.t; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.By ...
- Java IO(2)阻塞式输入输出(BIO)的字节流与字符流
在上文中<Java IO(1)基础知识——字节与字符>了解到了什么是字节和字符,主要是为了对Java IO中有关字节流和字符流有一个更好的了解. 本文所述的输出输出指的是Java中传统的I ...
- Java IO(2)阻塞式输入输出(BIO)
在上文中<Java IO(1)基础知识——字节与字符>了解到了什么是字节和字符,主要是为了对Java IO中有关字节流和字符流有一个更好的了解. 本文所述的输出输出指的是Java中传统的I ...
- IO同步阻塞与同步非阻塞
BIO.NIO.AIO IO(BIO)和NIO区别:其本质就是阻塞和非阻塞的区别 阻塞概念:应用程序在获取网络数据的时候,如果网络传输数据很慢,就会一直等待,直到传输完毕为止. 非阻塞概念:应用程序直 ...
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
- java的nio之:java的bio流下实现的socket服务器同步阻塞模型和socket的伪异步的socket服务器的通信模型
同步I/O模型的弊端===>每一个线程的创建都会消耗服务端内存,当大量请求进来,会耗尽内存,导致服务宕机 伪异步I/O的弊端分析===>当对Socket的输入流进行读取操作的时候,它会一直 ...
- Java IO------------------BIO(同步阻塞)、NIO1.0(多路复用)、NIO2.0(AIO,非阻塞)
1. BIO JDK5之前, JDK的IO模式只有BIO(同步阻塞)问题: 因为阻塞的存在, 需对每个请求开启一个线程. 过多的线程切换影响操作系统性能解决: 使用线程池, 处理不过来的放入队列, 再 ...
随机推荐
- JS结合a标签的使用
a标签可以当作按钮使用,也可以当作连接. <a href=javascript:test(5)>弹出5</a> 会直接调用JS函数(注意中间没引号) <a href ...
- 解决Centos下yum无法更新
问题: http://mirrors.cloud.aliyuncs.com/epel/6/x86_64/repodata/repomd.xml: [Errno 14] PYCURL ERROR 6 - ...
- 编译时bad substitution的解决办法
由于使用的使用的编译器不同导致, 需要使用shell为 #!/bin/bash 即可.
- zabbix3.0使用ss命令对tcp连接数和状态的监控性能优化
zabbix3.0对tcp连接数及状态的监控优化 之前对tcp的监控采用netstat命令,发现在服务器繁忙的时候效果不理想,这个命令占用大量的cpu有时候高达90%以上,可能会导致业务的不稳定,所以 ...
- 转载:2.2.5 在配置中使用变量《深入理解Nginx》(陶辉)
原文:https://book.2cto.com/201304/19630.html 有些模块允许在配置项中使用变量,如在日志记录部分,具体示例如下.log_format main '$remot ...
- vue-router两种模式,到底什么情况下用hash,什么情况下用history模式呢?
转:https://segmentfault.com/q/1010000010340823/a-1020000010598395 为什么要有 hash 和 history 对于 Vue 这类渐进式前端 ...
- php 中使用include、require、include_once、require_once的区别
在PHP中,我们经常会通过include.require.include_once.require_once来引用文件,都可以达到引用文件的目的,但他们之间又有哪些区别呢,接一下我们详细的介绍一下 i ...
- 【mysql】MySQLdb返回字典方法
来源:http://blog.csdn.net/zgl_dm/article/details/8710371 默认mysqldb返回的是元组,这样对使用者不太友好,也不利于维护下面是解决方法 impo ...
- 如何将Request对象中的参数列表打印出来
Map<String, String[]> map = request.getParameterMap(); Set<Map.Entry<String, String[]> ...
- php三种常用的加密解密算法
方法一: /** * @param $string 要加密/解密的字符串 * @param string $operation 类型,ENCODE 加密:DECODE 解密 * @param stri ...