操作系统上的网络IO基础

网络IO过程包含许多层面的内容,如计算机组成、网络通信、操作系统、应用程序API等。

本次只讨论操作系统层面以上的网络IO基础。

从操作系统层面看网络IO

socket

socket,主要包含五个要素, 通信协议、客户端ip、客户端port、服务端 ip、服务端port;可以理解为应用层到传输层的一层抽象,操作系统就为应用程序提供了许多socket相关的系统调用,以方便应用程序进行网络通信。

可以通过netstat命令查看网络连接

netstat -natp # 查看网络连接
复制代码

如果你进行过网络通信,如之前调用了curl www.baidu.com,再查看网络连接,将看到socket的五个要素,如下图:

image-20201019170605440.png

文件描述符

操作系统将socket连接映射成为 -> 文件描述符(file descriptor,简称fd),针对socket的读写转换为对fd的读写,进程的输入输出。说起来抽象,可以通过在linux下创建一个socket连接并通过fd读写更形象的理解:

# 与百度建立socket连接,并将其读写交给文件描述符8
exec 8<> /dev/tcp/www.baidu.com/80 # 8是文件描述符,<>代表输入输出流,由内核建立socket连接
复制代码
# 输出一段文本到上面的socket文件描述符,即发送tcp数据,在应用层向传输层发送了数据,socket是应用层到传输层的一个抽象
echo -e "GET / HTTP/1.1\n" 1>& 8 
复制代码
# 输入从文件描述符8处来
cat 0<& 8 
复制代码

执行完第三条命令后将得到百度首页的相应内容

image-20201023162844174.png

服务端与客户端

如下图C1,C2,C3为三个客户端,Server为一个服务端,它们建立连接并进行数据读写的过程如下:

  1. Server启动,创建了一个socket,绑定地址,得到一个S-fd服务端文件描述符
  2. 客户端通过Server的socket地址,进行TCP三次握手连接,成功后,客户端生成一个代表socket连接的文件描述符(图中客户端的c1-fd、c2-fd、c3-fd)
  3. 服务端S-fd读写进行三次握手,连接成功后,在服务端生成一个代表客户端socket连接的文件描述符(图中Server的c1-fd、c2-fd、c3-fd)
  4. 客户端、服务端通过socket连接(即c1-fd、c2-fd、c3-fd的读写)进行发送接收数据

image-20201023171411969.png

网络IO的阶段

image-20201023170853186.png

从CPU工作的角度来看,网络IO的读取过程大概分为两个阶段

  1. 数据从网卡读取到内核缓冲区;(需要发起IO请求后等待数据就绪
  2. 从内核缓冲区拷贝数据到用户空间

类似的,写入数据过程也可能因为图中标红的buffer memory,内核socket缓冲区被占满而需要等待就绪。

综上所述,网络IO与本地文件IO的不同在于,其读写过程中可能需要一个等待的过程,这有助于理解后面编写网络IO程序的阻塞概念。

从Java网络IO程序到系统调用

操作系统为应用程序提供了一系列系统调用用于实现建立socket连接、读写数据等操作。具体的包括这几类:

socket # 创建socket
bind # 服务端绑定地址
listen # 监听
accept # 服务端接收客户端连接
recvfrom / read  # 读取数据
复制代码

下面从几个Java应用程序,结合它们运行时所进行的系统调用理解网络IO的过程。

BIO

BIO,Blocking IO的简称,指的是阻塞IO,下面通过一个java程序的运行分析操作系统层面是如何实现一个BIO的Server的。

程序

// BIOServer.java
ServerSocket serverSocket = new ServerSocket(8081);
while (true){
  // 接受连接,阻塞至有连接
  final Socket socket = serverSocket.accept();
  // new Thread(()->{
    try {
      InputStream inputStream = socket.getInputStream();
      while (true){
        byte[] bytes = new byte[1024];
        // 从socket连接中读取数据,阻塞至有数据可读
        if (inputStream.read(bytes) > 0){
          System.out.println(String.format("got message: %s", new String(bytes)));
        }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  // }).start();
}
复制代码

启动

上面是一个简单的Java BIO程序,我们可以在执行它时同时使用strace命令查看程序运行时所进行的系统调用(Linux下)

strace -ff -o out java BIOServer # 查看进程的线程对内核进行了那些调用
复制代码

执行上面命令后,我们会得到几个out为前缀的文件,这些文

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值