Linux-socket详解

news2024/9/21 12:30:40

前言

OSI七层模型和TCP/IP四层模型在这里就不说了。

套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将IO插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

Socket与TCP/IP应用层的关系:

  1. Socket是一种编程接口,使应用程序能够使用TCP/IP协议进行网络通信。
  2. 应用层协议是构建在Socket和TCP/IP之上的,用于实现不同应用程序之间的通信规则。应用层利用Socket接口与传输层通信,最终使用TCP/IP协议在网络上传输数据。

下图是应用层通过Socket接口与传输层通信,网络包的收发过程:
在这里插入图片描述

最基本的Socket模型

TCP协议的Socket程序的调用过程:

  1. 服务端调用socket()函数,创建网络协议为 IPv4,以及传输协议为 TCP 的 Socket
  2. 服务端调用bind()函数,给这个 Socket 绑定一个 IP 地址和端口。
  3. 服务端调用listen()函数进行监听。
  4. 客户端在创建好 Socket 后,调用connect()函数发起连接。
  5. 客户端调用accept()函数,来从内核获取已经完成的客户端的连接。
  6. 客户端和服务端可以开始相互传输数据了,双方都可以通过read()write()函数来读写数据。

在connect()函数发起的时候就是三次握手的开始。

在TCP连接的过程中,内核是维护了两个队列:

  1. 一个是还没完全建立连接的队列,称为TCP半连接队列(图中的syn队列),这个队列都是没有完成三次握手的连接,此时服务端处于syn_rcvd的状态。
  2. 一个是已经建立连接的队列,称为TCP全连接队列(图中的accept队列),这个队列都是完成了三次握手的连接,此时服务端处于established状态;

在这里插入图片描述

如何让更多的客户端连接同时使用?

如果按照上面的TCP socket进行连接,那当一个客户端连接有网络IO或者发生阻塞时,那么其他的客户端就无法使用了。

题外话:

  • 四元组:本机IP,本机端口,对端IP,对端端口
  • 五元组:协议,本机IP,本机端口,对端IP,对端端口

那么,服务器单机理论最大TCP客户端连接数 = 客户端IP数 × 客户端端口数。

对于IPv4来说,客户端的IP数最多为2的32次方,客户端的端口数最多为2的16次方,也就是服务端单机最大TCP客户端连接数约为2的48次方

上面只是理论值,实际上服务器的TCP连接数会受两个方面影响:

  1. 文件描述符,Socket实际上是一个文件,也就会对应一个文件描述符。在Linux下,单个进程打开的文件描述符数是有限制的,没有经过修改的值一般都是1024,不过我们可以通过ulimit增大文件描述符的数目。
  2. 系统内存,每个TCP连接在内核中都有对应的数据结构,意味着每个连接都是会占用一定内存的。
经典的C10k和C1000k的问题  
C是Client单词首字母缩写,C10K就是单机同时处理1万个请求的问题,C1000K就是单机同时处理100万个请求的问题。

C10k
从物理资源上来说,对2GB内存和千兆网卡的服务器来说,同时处理1万个请求,只要每个请求处理占用不到200KB(2GB/10000)的内存和100Kbit(1000Mbit/10000)的网络带宽就可以。所以,物理资源很容易满足,其实就是网络IO模型的问题。

C1000k
从物理资源使用上来说,100万个请求需要大量的系统资源。每个请求还是按照占用内存200KB和占用带宽100Kbit来算,那就需要200Gb和100000Mbit带宽才行。而且大量的连接还会带来大量的中断请求、文件描述符大量占用等等这些软件上的问题。虽然还是可以在网络IO模型上优化,但是C1000k更关键的是借助硬件资源了。

网络IO模型

多进程模型

为每个客户端分配一个进程来处理请求。

服务器的主进程负责监听客户的连接,一旦与客户端连接完成。accept()函数就会返回一个已连接Socket,这时就通过fork()函数创建一个子进程,实际上就把父进程所有相关的东西都复制一份,包括文件描述符、内存地址空间、程序计数器、执行的代码等。

在这里插入图片描述
注意:

  1. 当子进程退出时,实际上内核里还会保留该进程的一些信息,也是会占用内存的,如果不做好回收工作,就会变成僵尸进程,随着僵尸进程越多,会慢慢耗尽我们的系统资源。
  2. 客户端达到一万以上,多进程模型会扛不住,因为每个进程都会占用一定的系统资源,并且进程之间的上下文切换也非常消耗系统资源。

多线程模型

既然进程间的上下文切换消耗系统资源大,那么轻量级的多线程模型就出来了。

线程是运行在进程中的一个逻辑流,单进程中可以运行多个线程,同进程里的线程可以共享进程的部分资源,比如文件描述符列表、进程空间、代码、全局数据、堆、共享库等。

当服务器与客户端TCP完成连接后,通过pthread_create()函数创建线程,然后将已连接Socket的文件描述符传递给线程函数,接着在线程里和客户端进行通信,从而达到并发处理的目的。

但是如果来一个连接就创建一个线程,那线程完成之后的销毁还是得靠操作系统,频繁的创建和销毁也是非常消耗资源的,所以多线程模型就采用了以线程池的方式来避免频繁的创建和销毁线程,把已连接的socket放到队列里,让提前创建好的线程池里的线程从队列里取出已连接的socket去处理。

在这里插入图片描述

注意:

  1. 这个队列是全局的,每个线程都会操作,为了避免多线程竞争,线程在操作这个队列前都要加锁。
  2. 虽然说改善了上下文切换的资源消耗,但是还是如果服务器需要维护一万个进程或者线程,肯定是扛不住的。

IO多路复用

既然给每个请求都分配一个进程或者线程操作系统扛不住,那么使用一个进程维护多个socket的IO多路复用就出来了。

一个进程也是在任一时刻只能处理一个请求,但是处理每个请求的事件时,耗时控制在 1 毫秒以内,这样 1 秒内就可以处理上千个请求,把时间拉长来看,多个请求复用了一个进程,这就是多路复用,这种思想很类似一个 CPU 并发多个进程,所以也叫做时分多路复用。

内核有三种IO多路复用接口,分别是:

  • select
  • poll
  • epoll
select和poll

select和poll实现多路复用的过程是:

  1. 用户态进程将已连接的 Socket 都放到一个文件描述符集合里。
  2. 用户态进程调用 select 函数或者 poll 函数将文件描述符集合拷贝到内核空间里。
  3. 内核遍历这个文件描述符集合,并把有IO事件发生的 Socket 标记为可读或者可写。
  4. 内核把文件描述符集合拷贝回用户空间。
  5. 用户态进程再次遍历这个文件描述符集合找到可读可写的Socket,然后进行处理(也就是通过read函数或者write函数去把内核准备好的内核空间数据拷贝到用户空间)。

注意:这里只要发生IO事件,无论这个IO事件是否已经在内核里把数据准备好了,内核都会在文件描述符集合里把对应的socket标记上。

select和poll的区别是:

  1. select使用固定长度的BitsMap来维护文件描述符集合,一般是内核中的FD_SETSIZE限制, 默认最大值为1024,也就是只能监听0到1023的文件描述符。
  2. poll使用的是动态数组,以链式的结构来维护文件描述符集合,从而打破select中对文件描述符的限制。

在这里插入图片描述

epoll

select和poll需要2次遍历文件描述符集合和2次拷贝文件描述符集合。在并发数大的时候,是非常影响性能的。

epoll用了两方面去解决select/poll的问题:

  1. epoll在内核里使用红黑树来跟踪所有的文件描述符。所以每次都只需要从用户空间把待检测的socket传到内核空间即可,不需要像select/poll一样每次都要把整个socket集合拷贝到内核里。减少了内核空间和用户空间来回拷贝集合的消耗

红黑树是一个高效的数据结构,增删改查的时间效率肯定比集合的高。

  1. epoll使用了事件驱动的机制。内核里维护了一个链表来记录就绪事件,当监控的红黑树里某个socket有事件发生并完成准备好数据时,通过回调函数内核会将其加入到这个就绪事件列表中,然后用户进程只会从链表中获取有事件发生的文件描述符,不需要像select/poll那样遍历整个socket集合,减少了需要遍历整个集合的时间

epoll实现多路复用的过程是:

  1. 使用epoll的用户进程首先会通过epoll_create函数在内核生成并维护一个监听socket文件描述符的红黑树。
  2. 当出现需要监听的socket时,用户进程通过epoll_ctl函数,把需要监听的socket文件描述符传到内核的红黑树中。同时也会针对这个socket注册一个回调函数。
  3. 当内核里监听socket文件描述符的红黑树有IO事件发生时,会通过回调函数,把这个socket的文件描述符复制到链表里。
  4. 用户进程调用epoll_wait函数把这个链表从内核空间拷贝到用户空间,用户进程遍历获取到里面可读可写的socket,然后进行处理(也就是通过read函数或者write函数去把内核准备好的内核空间数据拷贝到用户空间)。

注意:

  1. 这里也是只要发生IO事件,无论这个IO事件是否已经在内核里把数据准备好了,内核都会在文件描述符集合里把对应的socket标记上。
  2. 用户进程调用epoll_wait函数是阻塞的,它得等待内核返回IO事件发生的通知。

在这里插入图片描述

epoll 支持两种事件触发模式:

  1. 边缘触发:当被监控的Socket文件描述符上有可读写事件发生时,内核只会从epoll_wait中苏醒(返回)一次。因此我们程序要保证一次性将内核缓冲区的数据读取完。只要边缘触发模式下的epoll_wait函数返回了,那么意味着至少有一个socket文件描述符是有IO事件触发的。
  2. 水平触发:当被监控的Socket文件描述符上有可读事件发生时,内核会不断地从epoll_wait中苏醒(返回),直到内核缓冲区数据被read函数读完才结束,目的是告诉我们有数据需要读取。

注意:因为边缘触发模式IO事件发生时只返回一次,所以需要循环从socket文件描述符中读写数据。如果有一个socket文件描述符是阻塞的,也就是内核还没准备好数据,那进程就阻塞在这里了。所以边缘触发模式一般和非阻塞IO搭配使用

epoll支持边缘触发和水平触发的方式,默认是水平触发。而select/poll只支持水平触发。一般而言,边缘触发模式会比水平触发模式的效率高。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1943671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nest.js 实战 (四):利用 Pipe 管道实现数据验证和转换

什么是管道(Pipe)? 在 Nest.js 中,管道(Pipelines) 是一种强大的功能,用于预处理进入控制器方法的请求数据,如请求体、查询参数、路径参数等。管道允许开发者在数据到达控制器方法之…

使用GoAccess进行Web日志可视化

运行网站的挑战之一是了解您的 Web 服务器正在做什么。虽然各种监控应用程序可以在您的服务器以高负载或页面响应缓慢运行时提醒您,但要完全了解正在发生的事情,唯一的方法是查看 Web 日志。阅读日志数据页面并了解正在发生的事情可能需要花费大量时间。…

慎用 readFileSync 读取大文件, 教你一招如何优雅处理大文件读取

我们在编写 nodejs 服务的时候,有时候需要使用 fs.readFileSync api 去读取文件,但是使用 fs.readFileSync 会将文件读取在内存中,如果遇到了文件很大时,fs.readFileSync 会占据服务器大量的内存,即使读取的文件比较小…

代发考试战报:7月16号武汉参加HCIP-Transmission传输 H31-341考试通过

代发考试战报:7月16号武汉参加HCIP-Transmission传输 H31-341考试通过,,有2个题好像没见到过,其他都是题库里的原题,题库很准,这个题库也不是一直不变的,也没规律可循什么时候变题,哪…

【TAROT学习日记】韦特体系塔罗牌学习(6)——教皇 THE HIEROPHANT

韦特体系塔罗牌学习(6)——教皇 THE HIEROPHANT 目录 韦特体系塔罗牌学习(6)——教皇 THE HIEROPHANT牌面分析1. 基础信息2. 图片元素 正位牌意1. 关键词/句2.爱情婚姻3. 学业事业4. 人际财富5. 其他象征意 逆位牌意1. 关键词/句2…

PostgreSQL 中如何实现数据的批量插入和更新?

🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!📚领书:PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何实现数据的批量插入和更新?一、批量插入数据1. 使用 INSERT INTO 语句结…

PSINS工具箱函数介绍——r2d

介绍工具箱里面r2d这个小函数的作用。 程序源码 function deg r2d(rad) % Convert angle unit from radian to degree % % Prototype: deg r2d(rad) % Input: rad - angle in radian(s) % Output: deg - angle in degree(s) % % See also r2dm, r2dms, d2r, dm2r, dms2r% …

运维锅总详解VLAN

本文介绍了VLAN作用、公司多个部门VLAN举例、VLAN间路由、VLAN协议控制字段解释及工作流程、VLAN历史演进等方面对VLAN技术进行详细分析。希望对您理解VLAN有所帮助! 一、VLAN作用 VLAN(Virtual Local Area Network,虚拟局域网)…

FreeSWITCH 1.10.10 简单图形化界面26-在网页上播放SIP设备视频

​ FreeSWITCH 1.10.10 简单图形化界面26-在网页上播放SIP设备视频 1、前言2、大概流程3、测试环境4、安装流媒体服务器5、设置流媒体服务器接口6、简单写个web接口7、测试一下1、web播放在线播放器1在线播放器2本地video控件 2、vlc播放vlc播放rtmpvlc播放rtsp 8、总结 1、前…

简过网:公务员公示后是不是就没有问题了?

A:请问,公务员录用考试公示期过后是不是说明就正式录用了? Q:公务员已经公示录用,就说明前期政审已经过关,档案在前期的审查工作中没有发现问题,在入职前,档案会调入组织部&#xf…

10.发布确认

解决消息不丢失的一个重要环节。 前面说过消息持久化,可能出现一种情况就是: 尽管它告诉rabbitmq将消息保存到磁盘,但是依然存在当消息刚准备存储到磁盘的时候,但是还没有存储完,消息还在缓存的一个间隔点。此时消息…

充电桩--交流充电桩硬件原理以及竞品方案

聚焦光伏领域、深耕储能市场、探究充电技术 微信公众号 小Q下午茶 聚焦光伏领域,深耕储能市场,探究充电技术 47篇原创内容 公众号 一、交流充电桩系统介绍 为了实现能源安全和“双碳”目标的达成,充电桩是需要智能电网支持,…

Linux 各目录

Linux 是一个非常严谨的操作系统,每个目录都有自己的作用,这些作用是固定的,没有特殊情况,应严格执行; Linux 中所有东西以文件形式存储和管理,命令也不例外; 以下四个 bin 是二进制文件&…

linux C++ onnxruntime yolov8 视频检测Demo

linux C onnxruntime yolov8 视频检测Demo 目录 项目目录 效果 ​编辑CMakeLists.txt 代码 下载 项目目录 效果 ./yolov8_demo --help ./yolov8_demo -c2 -ptrue ./yolov8_demo -c1 -strue CMakeLists.txt # cmake needs this line cmake_minimum_required(VERSION 3…

力扣最热一百题——3.最长连续序列

目录 题目链接:128. 最长连续序列 - 力扣(LeetCode) 题目描述 示例 提示 解法一:排序双指针剪枝 思路 1. 获取数组长度并进行特判 2. 对数组进行排序 3. 初始化变量 4. 遍历数组并寻找最长连续子序列 5. 返回结果 总结…

Linux笔记-对.a静态库的进一步理解(2024-04-09)

过程 问: Linux中生成.a库时候,如果代码里面调用了一些只引用未定义的函数,gcc不报错,但能生成对应的.a文件,这是为什么?再写一个执行程序去调用.a库时,链接时就会报这个.a库未定义的引用&…

列举excel中调整行高列宽的五种方法

列举excel中调整行高列宽的五种方法 在Excel中调整行高列宽的方法有以下五种: 使用鼠标手动调整行高列宽:将鼠标悬停在行或列的边界上,光标会变成双向箭头,此时按住鼠标左键并拖动边界即可调整行高或列宽。 使用快捷键调整行高列…

node和npm安装;electron、 electron-builder安装

1、node和npm安装 参考: https://blog.csdn.net/sw150811426/article/details/137147783 下载: https://nodejs.org/dist/v20.15.1/ 安装: 点击下载msi直接运行安装 安装完直接cmd打开可以,默认安装就已经添加了环境变量&…

vue3通过html2canvas dom转图片复制到剪贴板和dom转图片并下载

代码实现 <template><div class"page"><div id"to-img-dom"><strong>我是图片标题</strong><p>我是内容&#xff0c;我是内容&#xff0c;我是内容&#xff0c;我是内容&#xff0c;我是内容&#xff0c;我是内容&am…

Jupyter notebook 快速入门

1、什么是jupyter notebook Jupyter Notebook是一个交互式笔记本环境&#xff0c;可以在其中同时编写和运行代码&#xff0c;以及进行数据分析和可视化。它支持多种编程语言&#xff08;如Python、R、Julia等&#xff09;&#xff0c;并提供了丰富的功能和工具供用户使用。Jup…