本文共 3929 字,大约阅读时间需要 13 分钟。
前提知识:想要彻底的了解IO模型及其原理和演变,你需要了解一下操作系统底层相关的一些系统调用知识,我上篇文章介绍过了,参考学习,有错误的望指出。
想必大家都知道IO操作肯定需要系统调用的,因为网络传输势必会用到网卡硬件,这就需要内核程序的参与,接下来详细介绍下整个BIO实现以及原理模型。
大家先有个简单网络模型:
客户端和服务器端建立链接请求的时候肯定是调用了系统调用,那么系统调用又是怎么做的呢?
一下主要讲解服务器端 先抓包看下内核: 以上3和系统调用就是我们写建立连接的时候系统调用做的事。 系统调用通过这三个函数帮我们建立起服务器链接,并开启监听,监听客户端链接事件。并通过系统调用函数 accept() 方法接受客户端链接,因为一般需要链接多个客户端,所以需要用 while 无线循环去接受客户端。 但是accept方法是阻塞的,所以必须要等有至少一个客户端链接进来的时候才会继续下一步。(重点) 当有客户端链接进来的时候,服务器端需要对连接进行轮询去获取客户端发来的信息,通过 recv函数获取客户端数据。以上是我们在服务器端建立链接,我们所需要的系统调用函数。
因为accpet 和 recv 都是阻塞函数,所以会产生一个问题: 如果客户端一直不发信息过来,系统会一直阻塞在那,遇到多个客户端链接时就会一直阻塞等待了,因为你的代码就一直阻塞在那了。 那么怎么解决呢,所以在我们java程序中我们会为每一个链接创建一个新的线程,去专门处理自己的链接发送的消息。代码演示:
服务器端:package com.socket;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.ServerSocket;import java.net.Socket;/** * 基于TCP协议 网络通讯编程(网络中客户端于服务器io流传输) * Created by Administrator on 2021/5/16. * Socket 服务器端 * 1. */public class SocketServerC { public static void main(String[] args) throws IOException { // 1. 创建server服务器,指定端口号,形成ip+端口构成唯一能识别的标识符套接字 ServerSocket serverSocket =new ServerSocket(5154); System.out.println("============等待连接============"); // 监听客户端连接,该方法线程阻塞,如果没有客户端连接,就阻塞一直等待。 // 优化:1.让服务器一直处理链接状态不要,链接到了就执行完关闭链接while循环 // 2. 因为下面很多地方线程阻塞,多个线程链接服务器时,产生效率问题,链接成功之后:使用多线程处理每一个链接 while(true) { final Socket accept = serverSocket.accept(); // 阻塞, 系统调用accept函数 System.out.println("============成功连接了============" + accept.getInetAddress()); new Thread(new Runnable() { public void run() { try { // 连接成功之后,读取客户端传来的网络IO对象,输入流 InputStream is = accept.getInputStream(); // 读取数据流,打印数据 // 定义一个缓存数组 byte[] bytes = new byte[1024]; int len = 0; // 系统调用 while (-1 != (len =is.read(bytes))){ // 阻塞--一直读取流通道,获取流数据,直到发现结束符才会跳出(File文件操作不会堵塞,因为读到最后会遇到EOF的) // 打印客户端发送过来的信息 System.out.println("客户端:" + new String(bytes, 0, len)); // 设置跳出条件,len长度小于bytes长度,这样就没办法持续读取流通道数据了,因为跳出了 if(len
客户端:
package com.socket;import java.io.BufferedOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;/** * * 基于TCP协议 网络通讯编程(网络中客户端于服务器io流传输) * Created by Administrator on 2021/5/16. * Socket 客户端 */public class SocketClientC { public static void SocketClientCC(Socket socket) throws IOException, InterruptedException { // 通过ip+端口号 套接字 建立tcp链接 System.out.println("连接成功"); // 向服务器端发送信息 OutputStream os = socket.getOutputStream(); for (int i = 0; i < 10; i++) { Thread.sleep(1000); os.write(("服务我来链接你了..."+i+"").getBytes()); } //获取服务器端发送过来的io输入流 InputStream is = socket.getInputStream(); byte[] bytes = new byte[1024]; int len = is.read(bytes); // 打印 System.out.println("服务器端:"+new String(bytes,0,len)); //socket.close(); } public static void main(String[] args) throws IOException, InterruptedException { Socket socket = new Socket("127.0.0.1",5154); SocketClientCC(socket); }}
以上的实现方式就是传统的IO模式 即 BIO,同步阻塞IO模型.
总结:
缺点:
如果线程数很大时,需要创建很多的线程去实现读写。既然问题找到了,就肯定需要解决,既然链接多了需要很多线程不行,原因就是因为你阻塞了,那要是accept 和 read不阻塞了,那是不是就一个线程就行了。这样问题是不是就解决了。
上述的解决思想就是NIO ( NONBLOCKING IO ) 的实现思想,切记此处说的NIO 是站在操作系统层面的非阻塞同步IO模型(不是用多路复用器),而不是jdk提供的NEW NIO,此包用Selector对多路复用器的封装。下一篇讲解!
转载地址:http://rjhof.baihongyu.com/