数据包的奇妙旅程:从网卡到你的屏幕

本文详细介绍了UTF-8编码方式,包括其变长字节的特点、如何存储不同字符集的字符,以及计算机如何读取并解析UTF-8编码的数据。

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

文章摘要

本文通过生活化比喻和技术解析,形象地讲解了网络数据从硬件到用户态的传输过程以及阻塞IO与非阻塞IO的区别。数据接收分为两大阶段:硬件接口到内核态(如快递分拣中心处理包裹),再由内核态到用户态(如快递派送到家)。阻塞IO会一直等待数据就绪,而非阻塞IO则立即返回并允许轮询检查。文章还分析了两种IO模型的优缺点,介绍了事件驱动IO的高效性,并提供了伪代码示例。最后总结出"数据进电脑先进内核仓"的口诀,以及高性能服务器的IO模型选择策略。


一、比喻:快递包裹的旅程

想象你要给远方的朋友寄一个快递包裹,这个过程就像一条网络数据包的旅程。

1. 第一阶段:硬件接口到内核态(快递员把包裹送到快递公司分拣中心)

  • 硬件接口:就是你家门口的快递员(网卡)。
  • 内核态:就是快递公司的分拣中心(操作系统内核)。
  • 过程:快递员(网卡)收到包裹(数据),把包裹送到分拣中心(内核),分拣中心负责后续处理。

技术上:

  • 网卡收到数据后,通过DMA(直接内存访问)或中断,把数据交给操作系统内核的网络协议栈。
  • 这时数据还在内核空间,用户程序还拿不到。

2. 第二阶段:内核态到用户态(分拣中心把包裹交给收件人)

  • 内核态:分拣中心(操作系统内核)已经收到包裹。
  • 用户态:收件人(你的应用程序)在家等着收快递。
  • 过程:分拣中心(内核)把包裹(数据)派送到你家(用户空间),你(应用程序)才能真正拿到包裹。

技术上:

  • 应用程序通过read()recv()等系统调用,从内核空间把数据拷贝到用户空间,应用程序才能处理数据。

二、流程图(收包裹的两大阶段)

[网线/无线] 
    ↓
[网卡](硬件接口) 
    ↓
[内核态](操作系统网络协议栈) 
    ↓
[用户态](应用程序)
  • 第一阶段:网卡 → 内核态(硬件到内核)
  • 第二阶段:内核态 → 用户态(内核到应用)

三、再举个生活例子

  • 第一阶段:邮递员把信件送到小区门卫(网卡到内核)。
  • 第二阶段:门卫把信件送到你家门口(内核到用户态)。

四、技术细节补充

  • 第一阶段(硬件到内核):
    • 由网卡驱动和内核协议栈负责,数据还在内核空间,用户程序看不到。
  • 第二阶段(内核到用户):
    • 由系统调用(如recvread)完成数据拷贝,用户程序才能访问。

五、总结口诀

数据进电脑,先进内核仓,
内核再递送,用户才开箱。


我们继续用生动形象的比喻+技术原理+常见问题来讲解网络数据的阻塞IO和非阻塞IO。


一、什么是阻塞IO和非阻塞IO?(形象比喻)

1. 阻塞IO(Blocking IO)

比喻:
你去银行排队取钱,轮到你时,柜员说“请稍等,我去金库取钱”。你只能站在柜台前等着,啥也干不了,直到柜员把钱拿来。

技术上:

  • 当你调用read()recv()等IO操作时,如果数据还没准备好,程序就会一直“卡住”在这里,直到数据到来。
  • 期间,这个线程啥也干不了,只能等。

2. 非阻塞IO(Non-blocking IO)

比喻:
你去银行取钱,柜员说“钱还没准备好,你先去喝杯咖啡,等会儿再来问”。你可以随时回来问“钱好了吗?”,如果还没好,你就继续去干别的。

技术上:

  • 当你调用read()recv()时,如果数据没准备好,系统会立刻返回一个错误(如EAGAIN),告诉你“现在没数据”,你可以去做别的事,过会儿再来试。

二、阻塞IO和非阻塞IO的底层原理

1. 阻塞IO

  • 系统调用(如read)会让线程挂起,直到数据准备好。
  • 适合简单场景,但如果有很多连接,线程会被大量阻塞,资源浪费。

2. 非阻塞IO

  • 系统调用会立刻返回,不会让线程挂起。
  • 需要你不断轮询(反复问“有数据了吗?”),或者配合事件通知机制(如selectpollepoll)来高效处理。

三、常见问题与场景

1. 阻塞IO的缺点

  • 线程被卡住:一个线程只能处理一个连接,遇到慢连接就被拖死。
  • 资源浪费:高并发时,需要大量线程,系统开销大。

2. 非阻塞IO的缺点

  • 轮询消耗CPU:如果你一直while循环去问“有数据吗?”,会浪费CPU。
  • 编程复杂:需要自己管理状态,代码复杂度高。

3. 事件驱动IO(select/poll/epoll)

  • 结合非阻塞IO,操作系统帮你“监听”哪些连接有数据了,通知你去处理。
  • 这样一个线程可以高效处理成千上万个连接(如Nginx、Node.js、Netty等)。

四、代码示例(伪代码)

阻塞IO

// 阻塞等待数据
int n = recv(socket, buf, len, 0);
// 如果没数据,线程会一直卡在这里

非阻塞IO

// 设置socket为非阻塞
fcntl(socket, F_SETFL, O_NONBLOCK);

int n = recv(socket, buf, len, 0);
if (n == -1 && errno == EAGAIN) {
    // 没有数据,立刻返回,可以去做别的事
}

五、总结口诀

阻塞IO,等到天荒地老;
非阻塞IO,随时来问一遭;
事件驱动最高效,通知来了再动手!


六、延伸:高性能服务器的IO模型

  • 阻塞IO:简单,适合小并发。
  • 非阻塞IO+多线程:适合中等并发。
  • 非阻塞IO+事件驱动(epoll等):适合高并发,现代服务器主流方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值