BIO网络编程

前言

之前我们已经了解了网络编程它是有操作系统统一的API的,每种语言又有不同的实现,所以本文主要讲述java最开始的一个Socket版本,使用java怎么去做一个BIO的网络编程,一步步开发根据现有问题升级改造,直到服务端能处理多个请求且能够支持HTTP网络协议

服务端-单线程处理

服务端代码

/**

* @author pangbohuan

* @date 2020/6/17 0017 17:15

* @description BIOSocket服务端

*/

publicclassBioSocketServer{

publicstaticvoidmain(String[] args)throws IOException {

ServerSocket serverSocket = new ServerSocket(8080);

System.out.println("服务启动成功");

while (!serverSocket.isClosed()) {

// TODO 会阻塞等待客户端连接

Socket request = serverSocket.accept();

System.out.println("收到新的连接:" + request.toString());

// 读取连接的数据打印出来

BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));

String requestMsg;

// TODO 没读取到数据会阻塞

while ((requestMsg = reader.readLine()) != null) {

if (requestMsg.length() != 0) {

System.out.println(requestMsg);

}

}

System.out.println("收到数据,来自于:" + request.toString());

}

}

}

客户端代码

/**

* @author pangbohuan

* @date 2020/6/17 0017 17:21

* @description BIOSocket客户端

*/

publicclassBioSocketClient{

publicstaticvoidmain(String[] args)throws IOException {

Socket socket = new Socket("127.0.0.1", 8080);

OutputStream outputStream = socket.getOutputStream();

Scanner scanner = new Scanner(System.in);

System.out.println("请输入要发送的内容:");

String msg = scanner.nextLine();

outputStream.write(msg.getBytes("UTF-8"));

outputStream.close();

scanner.close();

socket.close();

}

}

执行步骤和运行结果

1.先执行服务端成功后再执行客户端

2.服务端打印收到客户端的连接

3.客户端控制台输入你好啊

4.服务端打印出客户端发送的你好啊

服务端和客户端运行结果如下图

服务端正常收到客户端发送的消息了,那么服务端单线程处理会有什么问题呢?

期望

产品经理:我们得处理多个用户的请求,提高系统的处理速度

后端开发:好的!我试一下

互联网应用的服务端是需要处理多个客户请求的,但是我们编写的服务端是只有一个线程在处理用户的请求.如果说有两个用户进行访问,那么现在的服务端只能一个个进行处理而且同时只能处理一个.这样做肯定是不行的我们不可能只服务一个客户端

那既然单线程不行我们就升级使用多线程来处理多个客户端连接的问题,一个线程分配一个连接

服务端-升级为多线程处理

服务端代码

/**

* @author pangbohuan

* @date 2020/6/17 0017 17:15

* @description BIOSocket服务端

*/

publicclassBioSocketServer{

publicstaticvoidmain(String[] args)throws IOException {

// 缓存线程池,最大线程数为200,60秒回收线程

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, 200,

60L, TimeUnit.SECONDS,

new SynchronousQueue<>());

ServerSocket serverSocket = new ServerSocket(8080);

System.out.println("服务启动成功");

while (!serverSocket.isClosed()) {

// TODO 会阻塞等待客户端连接

Socket request = serverSocket.accept();

System.out.println("收到新的连接:" + request.toString());

// 为了满足同时能够处理多个客户端连接的需求,专门分配一个线程来处理客户的请求.但是最大只能处理200个任务,超过200个任务还是会阻塞

threadPoolExecutor.execute(() -> {

try {

// 读取连接的数据打印出来

BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));

String requestMsg;

// TODO 没读取到数据会阻塞

while ((requestMsg = reader.readLine()) != null) {

if (requestMsg.length() != 0) {

System.out.println(requestMsg);

}

}

System.out.println("收到数据,来自于:" + request.toString());

} catch (IOException e) {

e.printStackTrace();

}

});

}

}

}

客户端代码

/**

* @author pangbohuan

* @date 2020/6/17 0017 17:21

* @description BIOSocket客户端

*/

publicclassBioSocketClient{

publicstaticvoidmain(String[] args)throws IOException {

Socket socket = new Socket("127.0.0.1", 8080);

OutputStream outputStream = socket.getOutputStream();

Scanner scanner = new Scanner(System.in);

System.out.println("请输入要发送的内容:");

String msg = scanner.nextLine();

outputStream.write(msg.getBytes("UTF-8"));

outputStream.close();

scanner.close();

socket.close();

}

}

执行步骤和运行结果

1.先执行服务端成功后再执行两遍客户端

2.服务端打印收到两个客户端的请求连接

3.第一个客户端输入123发送给服务端

4.第二个客户端输入456发送给服务端

5.服务端打印出两个客户端分别发送的123和456

两个客户端和服务端结果如下图

期望

产品经理:目前我们想做一个web网站,需要服务端这边支持http协议,你改下吧

后端开发:好的,我试一下

服务端-升级为HTTP协议

HTTP协议

后端开发:先了解一下http协议

请求数据包解析

由于我们这个例子没有请求数据,所以没看到第三部分和第四部分

响应数据包解析

响应状态码

后端开发:了解得差不多了,开始动手吧

服务端代码

/**

* @author pangbohuan

* @date 2020/6/17 0017 17:15

* @description BIOSocket服务端

*/

publicclassBioSocketServer{

publicstaticvoidmain(String[] args)throws IOException {

// 缓存线程池,最大线程数为200,60秒回收线程

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(0, 200,

60L, TimeUnit.SECONDS,

new SynchronousQueue<>());

ServerSocket serverSocket = new ServerSocket(8080);

System.out.println("服务启动成功");

while (!serverSocket.isClosed()) {

// TODO 会阻塞等待客户端连接

Socket request = serverSocket.accept();

System.out.println("收到新的连接:" + request.toString());

// 为了满足同时能够处理多个客户端连接的需求,专门分配一个线程来处理客户的请求.但是最大只能处理200个任务,超过200个任务还是会阻塞

threadPoolExecutor.execute(() -> {

try {

// 读取连接的数据打印出来

BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));

String requestMsg;

// TODO 没读取到数据会阻塞

while ((requestMsg = reader.readLine()) != null) {

if (requestMsg.length() != 0) {

System.out.println(requestMsg);

} else {

break;

}

}

System.out.println("收到数据,来自于:" + request.toString());

//响应客户端

OutputStream outputStream = request.getOutputStream();

outputStream.write("HTTP/1.1 200 OKrn".getBytes());

outputStream.write("Content-Length 11rnrn".getBytes());

outputStream.write("Hello World".getBytes());

outputStream.flush();

outputStream.close();

} catch (IOException e) {

e.printStackTrace();

}

});

}

}

}

执行步骤和运行结果

1.启动服务端程序

2.打开浏览器输入http://127.0.0.1:8080/servlet-demo-1.0.0/index 访问服务端

浏览器和服务端结果如下图

这是一个HTTP协议开发的过程,也就是说如果我们想要在Socket之上支撑更多的功能需要在tcp之上运用更多协议

总结

协议含义

BIO网络编程改造升级到这基本可以告一段落了, 在这期间我们发现java对于网络编程的一些封装还是比较友好的,操作比较便捷

同时也发现如果我们要支持一些应用级别的开发和更强大的一些功能直接使用Socket它是不能支持的,我们需要运用一些高级的协议也就是应用层协议,所谓的协议就是客户端和服务端之间进行交互时需要遵循特定的规范,比如说http的状态码 URL路径这些都是HTTP协议的规范.请求数据也好响应数据也好全部要基于这个协议进行开发

BIO-阻塞IO的含义

IO含义
阻塞 (blocking) IO资源不可用时,IO请求一直阻塞,直到反馈结果(有数据或超时)
非阻塞 (non-blocking) IO资源不可用时,IO请求离开返回,返回数据标识资源不可用
同步 (synchronous) IO应用阻塞在发送或接收数据的状态,直到数据成功传输或返回失败
异步(asynchronous) IO应用发送或接收数据后立刻返回,实际处理是异步执行的

注意事项

阻塞和非阻塞是获取资源的方式而同步和异步是程序如何处理资源的逻辑设计

简单来说阻塞或非阻塞IO 和 同步或异步IO之间是不冲突的

阻塞不阻塞是获取资源的方式

同步不同步是拿到资源如何处理的方式

设计者可以做成同步阻塞IO、异步阻塞IO、异步阻塞IO、异步非阻塞IO

代码中使用的API:ServerSocket#accept、InputStream#read都是阻塞的API

操作系统底层API中,默认Socket操作都是Blocking型,send/recv等接口都是阻塞的

BIO的弊端

阻塞导致在处理网络I/O时,一个线程只能处理一个网络连接,这就浪费了宝贵的线程资源

一个线程好不容易从操作系统中申请下来,JVM也为它创建了一个对象把宝贵的内存分给他一部分,结果它只是去等待

这个弊端也是NIO问世的主要原因

以上是 BIO网络编程 的全部内容, 来源链接: utcz.com/a/28236.html

回到顶部