图解 | 当我们在读写 Socket 时,我们究竟在读写什么?

Posted 芋道源码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图解 | 当我们在读写 Socket 时,我们究竟在读写什么?相关的知识,希望对你有一定的参考价值。

技术文章第一时间送达!

源码精品专栏

 
  •  69 篇

  •  61 篇



来源:http://t.cn/Rru8Rk7

简单过程细节过程


套接字socket是大多数程序员都非常熟悉的概念,它是计算机网络编程的基础,TCP/UDP收发消息都靠它。我们熟悉的web服务器底层依赖它,我们用到的mysql关系数据库、Redis内存数据库底层依赖它。我们用微信和别人聊天也依赖它,我们玩网络游戏时依赖它,读者们能够阅读这篇文章也是因为有它在背后默默地支持着网络通信。

简单过程

当客户端和服务器使用TCP协议进行通信时,客户端封装一个请求对象req,将请求对象req序列化成字节数组,然后通过套接字socket将字节数组发送到服务器,服务器通过套接字socket读取到字节数组,再反序列化成请求对象req,进行处理,处理完毕后,生成一个响应对应res,将响应对象res序列化成字节数组,然后通过套接字将自己数组发送给客户端,客户端通过套接字socket读取到自己数组,再反序列化成响应对象。

img

通信框架往往可以将序列化的过程隐藏起来,我们所看到的现象就是上图所示,请求对象req和响应对象res在客户端和服务器之间跑来跑去。

也许你觉得这个过程还是挺简单的,很好理解,但是实际上背后发生的一系列事件超出了你们中大多数人的想象。通信的真实过程要比上面的这张图复杂太多。你也许会问,我们需要了解的那么深入么,直接拿来用不就可以了么?

在互联网技术服务行业工作多年的经验告诉我,如果你对底层机制不了解,你就会不明白为什么对套接字socket的读写会出现各种奇奇乖乖的问题,为什么有时会阻塞,有时又不阻塞,有时候还报错,为什么会有粘包半包问题,NIO具体又是什么,它是什么特别新鲜的技术么?对于这些问题的理解都需要你了解底层机制。

细节过程

为了方便大家对通信底层的理解,我花了些时间做了下面这个动画,它并不能完全覆盖底层细节的全貌,但是对于理解套接字的工作机制已经足够了。请读者仔细观察这个动画,后面的讲解将围绕着这个动画展开。

img

我们平时用到的套接字其实只是一个引用(一个对象ID),这个套接字对象实际上是放在操作系统内核中。这个套接字对象内部有两个重要的缓冲结构,一个是读缓冲(read buffer),一个是写缓冲(write buffer),它们都是有限大小的数组结构。

当我们对客户端的socket写入字节数组时(序列化后的请求消息对象req),是将字节数组拷贝到内核区套接字对象的write buffer中,内核网络模块会有单独的线程负责不停地将write buffer的数据拷贝到网卡硬件,网卡硬件再将数据送到网线,经过一些列路由器交换机,最终送达服务器的网卡硬件中。

同样,服务器内核的网络模块也会有单独的线程不停地将收到的数据拷贝到套接字的read buffer中等待用户层来读取。最终服务器的用户进程通过socket引用的read方法将read buffer中的数据拷贝到用户程序内存中进行反序列化成请求对象进行处理。然后服务器将处理后的响应对象走一个相反的流程发送给客户端,这里就不再具体描述。

阻塞

我们注意到write buffer空间都是有限的,所以如果应用程序往套接字里写的太快,这个空间是会满的。一旦满了,写操作就会阻塞,直到这个空间有足够的位置腾出来。不过有了NIO(非阻塞IO),写操作也可以不阻塞,能写多少是多少,通过返回值来确定到底写进去多少,那些没有写进去的内容用户程序会缓存起来,后续会继续重试写入。

同样我们也注意到read buffer的内容可能会是空的。这样套接字的读操作(一般是读一个定长的字节数组)也会阻塞,直到read buffer中有了足够的内容(填充满字节数组)才会返回。有了NIO,就可以有多少读多少,无须阻塞了。读不够的,后续会继续尝试读取。

ack

那上面这张图就展现了套接字的全部过程么?显然不是,数据的确认过程(ack)就完全没有展现。比如当写缓冲的内容拷贝到网卡后,是不会立即从写缓冲中将这些拷贝的内容移除的,而要等待对方的ack过来之后才会移除。如果网络状况不好,ack迟迟不过来,写缓冲很快就会满的。

包头

速率

还有个问题那就是如果读缓冲满了怎么办,网卡收到了对方的消息要怎么处理?一般的做法就是丢弃掉不给对方ack,对方如果发现ack迟迟没有来,就会重发消息。那缓冲为什么会满?是因为消息接收方处理的慢而发送方生产的消息太快了,这时候tcp协议就会有个动态窗口调整算法来限制发送方的发送速率,使得收发效率趋于匹配。如果是udp协议的话,消息一丢那就彻底丢了。




已在知识星球更新源码解析如下:

  • 《精尽 Dubbo 源码解析系列》69 篇。

  • 《精尽 Netty 源码解析系列》61 篇。

  • 《精尽 Spring 源码解析系列》35 篇。

  • 《精尽 MyBatis 源码解析系列》34 篇。

  • 《数据库实体设计》17 篇。

  • 《精尽 Spring MVC 源码解析系列》15 篇。


目前在知识星球更新了《Dubbo 源码解析》目录如下:

01. 调试环境搭建
02. 项目结构一览
03. 配置 Configuration
04. 核心流程一览

05. 拓展机制 SPI

06. 线程池

07. 服务暴露 Export

08. 服务引用 Refer

09. 注册中心 Registry

10. 动态编译 Compile

11. 动态代理 Proxy

12. 服务调用 Invoke

13. 调用特性 

14. 过滤器 Filter

15. NIO 服务器

16. P2P 服务器

17. HTTP 服务器

18. 序列化 Serialization

19. 集群容错 Cluster

20. 优雅停机

21. 日志适配

22. 状态检查

23. 监控中心 Monitor

24. 管理中心 Admin

25. 运维命令 QOS

26. 链路追踪 Tracing

... 一共 69+ 篇

目前在知识星球更新了《Netty 源码解析》目录如下:

01. 调试环境搭建
02. NIO 基础
03. Netty 简介
04. 启动 Bootstrap

05. 事件轮询 EventLoop

06. 通道管道 ChannelPipeline

07. 通道 Channel

08. 字节缓冲区 ByteBuf

09. 通道处理器 ChannelHandler

10. 编解码 Codec

11. 工具类 Util

... 一共 61+ 篇


目前在知识星球更新了《数据库实体设计》目录如下:


01. 商品模块
02. 交易模块
03. 营销模块
04. 公用模块

... 一共 17+ 篇


目前在知识星球更新了《Spring 源码解析》目录如下:


01. 调试环境搭建
02. IoC Resource 定位
03. IoC BeanDefinition 载入

04. IoC BeanDefinition 注册

05. IoC Bean 获取

06. IoC Bean 生命周期

... 一共 35+ 篇


目前在知识星球更新了《MyBatis 源码解析》目录如下:


01. 调试环境搭建
02. 项目结构一览
03. MyBatis 面试题合集

04. MyBatis 学习资料合集

05. MyBatis 初始化

06. SQL 初始化

07. SQL 执行

08. 插件体系

09. Spring 集成

... 一共 34+ 篇


源码不易↓↓↓↓

点赞支持老艿艿↓↓

以上是关于图解 | 当我们在读写 Socket 时,我们究竟在读写什么?的主要内容,如果未能解决你的问题,请参考以下文章

[转帖]脑残式网络编程入门:我们在读写Socket时,究竟在读写什么?

动画图解 socket 缓冲区的那些事儿

使用read write 读写socket

icu诊室当我们谈论数据挖掘时,究竟是在说什么?

socket返回值为0的问题

浏览器的工作原理你真的全知道嘛?史上最全图解其原理