IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 网络编程 -- 套接字 socket 和 tcp 连接过程 -> 正文阅读

[系统运维]网络编程 -- 套接字 socket 和 tcp 连接过程

一、socket 和 fd(file descriptor)是什么?

Unix/Linux 基本哲学之一就是"一切皆文件",即一切都可以用 "open -> read/write -> close" 来操作,socket 也可以理解成是一种特殊的文件。

fd(file descriptor):文件描述符,非负整数,是内核为了高效的管理已经被打开的文件所创建的索引,内核(kernel)利用文件描述符来访问文件。

需要明确的是,每个 tcp 连接的两端都会关联一个套接字和该套接字指向的文件描述符。

二、tcp 连接过程

要通过 TCP 连接发送出去的数据都先拷贝到 send buffer,可能是从用户空间进程的 app buffer 拷入的,也可能是从内核的 kernel buffer 拷入的,拷入的过程是通过 send() 函数完成的,由于也可以使用 write() 函数写入数据,所以也把这个过程称为写数据,相应的send buffer 也就有了别称 write buffer。

最终数据是通过网卡流出去的,所以 send buffer 中的数据需要拷贝到网卡中。由于一端是内存,一端是网卡设备,可以直接使用 DMA 的方式进行拷贝,无需 CPU 的参与。也就是说,send buffer 中的数据通过 DMA 的方式拷贝到网卡中并通过网络传输给 TCP 连接的另一端。

当通过 TCP 连接接收数据时,数据肯定是先通过网卡流入的,然后同样通过 DMA 的方式拷贝到 recv buffer 中,再通过 recv() 函数将数据从 recv buffer 拷入到用户空间进程的 app buffer 中。

三、tcp 连接细节

?总体流程如下:服务端使用 socket 函数创建套接字,调用 bind、listen 函数进入等待状态。客户端通过调用 connect 函数发起连接请求。需要注意的是,客户端只能等到服务器端调用 listen 函数后才能调 connect 函数。同时要清楚,客户端调用 connect 函数之前,服务器端有可能率先调用 accept 函数。当然,此时服务器端在调用 accept 函数是进入阻塞状态,知道客户端调用 connect 函数为止。

下面我们要逐一对这些步骤进行解释。

  1. socket() 函数

    socket 函数的作用就是生成一个用于通信的套接字文件描述符 sockfd,这个文件描述符可以作为稍后 bind() 函数的绑定对象
  2. bind() 函数

    服务程序通过分许配置文件,从中解析出象牙监听的地址和端口,再加上可以通过 socket() 函数生成的套接字 sockfd,就可以使用 bind() 函数将这个套接字绑定到要监听的地址和端口组合 "addr:port" 上,绑定了端口号的套接字可以作为 listen() 函数的监听对象。
  3. listen() 函数

    listen() 函数就是监听已经通过 bind() 绑定了 "addr:port" 的套接字的。监听之后,套接字就从 CLOSE 状态转变为 LISTEN 状态,于是这个套接字就可以对外提供 TCP 连接的窗口了。

    listen() 函数维护了两个队列:连接未完成队列 (syn queue) 和连接已完成队列 (accept queue),用来配合内核完成 TCP 三次握手和四次挥手过程(注意,这时还不涉及用户线程),当监听的 sockfd? 接收到某个客户端发来的 SYN 并回复了 SYN + ACK 之后,就会在连接未完成队列(syn queue)的尾部创建一个关于这个客户端的条目,并设置他的状态为 SYN_RECV,显然,这个条目中必须包含客户端的地址和端口相关信息,当监听的该条目再次受到这个客户端发送的 ACK 信息之后,就会把这个条目移入到连接已完成队列(accept queue),并设置它的状态为 ESTABLISHED。
  4. connect() 函数

    connect() 函数是用于向某个已监听的套接字发起连接请求,也就是发起 TCP 三次握手过程。可以看出,连接请求方(如客户端)才会使用 connect 函数,当然,在发起connect之前,连接发起方也需要生成一个 sockfd,且使用的很可能是绑定了随机端口的套接字。既然 connect 函数是向某个套接字发起连接的,自然在使用 connect 函数时需要带上连接的目的地,即目标地址和目标端口,这正是服务端的监听套接字上绑定的地址和端口。同时,它还要带上自己的地址和端口,对于服务端来说,这就是连接请求的源地址和源端口。于真,TCP 连接的两端的套接字都已经成了五元组的完整格式。
  5. accept() 函数

    listen 函数的连接已完成队列中维护着已经完成三次握手的连接,accept 函数的作用是读取已经完成连接队列的第一项(读完就从队列中移除),并对此项生成一个用于后续连接的套接字描述符(暂且用connfd来表示),有了新的连接套接字,用户进程/线程就可以通过这个连接套接字和客户端进行数据传输,而前文所说的监听套接字(sockfd)则仍然被监听者监听。

    accept 函数是由用户空间进程发起,由内核空间操作,只要经过 accept 过的连接,连接将从已完成队列中移除,也就表示 TCP 已经建立完成了,两端的用户空间进程可以通过这个连接进行真正的数据传输了。

    经过accept函数后,tcp连接的套接字从 sockfd 变成了 connfd,也就是说,经过 accept 之后,这个连接和 sockfd 套接字已经没有任何关系了。
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-07-20 19:20:33  更:2022-07-20 19:21:31 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 18:36:07-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码