Java BIO模型

本文详细解析了基于BIO的网络服务端启动、客户端连接的过程,通过strace跟踪系统调用,展示了如何建立通信、监听、接受连接以及业务处理的细节。在服务端,从创建socket、监听到接受客户端连接,再到子线程处理数据读取。在客户端,通过nc命令建立连接,服务端通过accept分配新描述符进行数据传输。整个过程揭示了socket编程的基本原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前面我们简单介绍了网络之间是service和client是如何建立通信的,这次我们来看下两方是如何交换数据的。
使用的service和client代码和前一章一样。

启动service

首先我们先来看下基础的网络BIO模型是如何运作的,为了讲解清楚过程,我们还是以实际运行结果展示。

我们这次在service运行时,追踪一下service进程的系统调用情况:

在这里插入图片描述
我们通过命"strace -ff -o out java Service "令,将"java Service"命令的系统调用执行情况输出到out文件夹。
现在我们已经将Serice启动了,绑定的端口号是9092。

此时我们来观察下网络情况:
在这里插入图片描述
我们可以看到此时针对9092端口,已经有了一个线程在进行Listen,只是还没有客户端连接进来。

我们再来看下文件描述符的使用情况:lsof -p 1982
在这里插入图片描述
当前service端1982线程下分配了一个描述符17来进行端口9092的监听。

现在我们再来看下文件情况:
在这里插入图片描述
前面我们有讲,我们会将每个线程的系统调用追踪情况打印到以out开头的文件中,现在发现多出了很多out文件,这说明当我们在启动service之后,实际上是创建了很多的线程在一同执行。

下面我们进入主线程9184看下系统调用情况,通过vim查找日志,我们发现有一个write的系统调用:
在这里插入图片描述
通过这几个write我们便可以在控制台里面看到我们输出的那两行信息,同时线程通过System.in.read()调用系统read()方法阻塞在这边(read的FD就是0,标准输入),等待输入。

我们前面有查看进程1982有分配一个FD(17)用于监听9092端口,这是怎么实现的呢?我们继续查看系统调用:
在这里插入图片描述
通过这几行日志,我们便可以很清晰的看到,当我们代码中new ServerSocket(9092)之后,在OS层面,是帮我们创建了一个socket,分配了描述符17,同时将17与9092进行绑定,最后再进行监听。

启动client

再下面我们让service开始接收客户端的连接,并通过命令“nc 127.0.0.1 9092"与service建立一个连接,我们看下service端的情况:
在这里插入图片描述
然后我们来看下线程1982的文件描述:
在这里插入图片描述
我们可以看到,针对线程1982,OS已经分配了描述符FD(18),关联的端口号是9092,对应的client的IP是localhost,端口号是55342,状态是已经建立连接,可以传输数据。

那当我们启动nc建立连接之后,发生了什么事情呢?我们一起来看下系统调用的追踪情况:
在这里插入图片描述
我们可以看到在poll中遍历到 FD=17的描述符后,accept了 一个ip是127.0.0.1,端口号是55342的连接,同时分配了描述符FD(18),后面再是系统调用write(1),打印"service accept client request …",对应的servie代码如下:
在这里插入图片描述
其中关于poll相关的知识点,我们会在后续的NIO中进行讲解。
如此我们便了解了socket.accept()在OS层面执行了哪些步骤,同时也印证了我们上一章节见过的socket的四元组概念。

业务处理

我们再看一下上面贴的代码,当我们accept了一个client之后,创建了一个线程来读取数据。具体在OS层是怎么实现的呢?我们还是来追踪系统调用情况:

在这里插入图片描述
我们可以看到有触发一次clone的系统的调用,从当前主线程中clone出一个子线程,flags中规定了子线程持有父线程的FD,在同一个内存空间等信息,同时对clone出的子线程指定线程唯一编号2387。

当代码的new Thread().start()之后,子线程便开始运行了,现在我们来看下目录文件:
在这里插入图片描述
此时我们可以看到os已经创建好并运行了编号为2387的线程,strace追踪线程也已经将2387线程的系统调用情况记录进文件了。

我们来看下2387线程的系统调用做了啥,还是一样的,我们进入out.2387文件一探究竟:
在这里插入图片描述
一目了然,我们可以看到2387线程,阻塞在这边,一直在等待从FD(18)中读取数据。还记得FD(18)是什么吗?没错,就是前面介绍的service在accept到client之后分配的描述符FD(18)。

网络IO模型

我们现在来梳理一下Service的过程:

  1. 主线程启动,调用OS的Socket,创建出一个Socket,绑定9092端口,分配FD(17)进行监听
  2. 创建出socket之后,执行accept来接收客户端的连接
  3. 客户端接入之后,OS得到完整的四元组socket信息,分配FD(18)进行数据传输
  4. 主线程clone出一个子线程,不停的尝试从FD(18)中读取数据

PS:linux下的追踪命令:strace
Mac os下追踪命令:dtruss,但是显示的信息没有linux全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值