Python Web 面试题
1 Web 相关
get 和 post 区别
-
get
:-
请求数据在
URL
末尾,URL
长度有限制 -
请求幂等,即无论请求多少次,服务器响应始终相同,这是因为
get
至少获取资源,而不修改资源 -
可以被浏览器缓存,以便以后的请求中更快地获取相同资源
-
可以在浏览器中输入或作为超链接点击
-
-
post
-
参数在请求体中,可以传递更多数据
-
不幂等,因为通常需要修改或创建资源
-
无法被浏览器缓存,因为可能会修改服务器资源
-
请求必须通过表单或
js
发送
-
cookies 和 session 的区别,cookie 的安全性
都是用于 web
开发中用于跟踪用户状态的技术,区别如下:
-
存储位置:
Cookie
存储在客户端浏览器小文件中,Session
是存储在服务器端的内存或数据库中 -
存储内容:
Cookie
中存储的是客户端的一些状态信息,例如用户的登录状态、语言偏好等;而Session
中存储的是服务器端保存的用户状态信息,例如用户 ID、购物车内容等。 -
安全性:
Cookie
因为存储在浏览器,容易被攻击,Session
存储在服务器端,安全性更高 -
有效期:
Cookie
可以设置一个过期时间,当超过这个时间后,Cookie
将自动失效,而Session
可以根据需要设置过期时间或者在用户关闭浏览器时自动过期
综上所述,Cookie 和 Session 都是用于跟踪用户状态的技术,但存储位置、存储内容、安全性和有效期等方面有所不同。在实际开发中,应该根据具体需求选择合适的技术。例如,对于需要跨多个页面和多个设备保存用户状态的应用程序,应该使用 Cookie;而对于需要保存更多用户状态信息和更高安全性要求的应用程序,应该使用 Session。
jwt token 和 session 的区别
JWT (JSON Web Token) 和 Session 都是用于认证和授权的技术,它们之间的区别如下:
-
数据存储:Session 是在服务器端存储用户状态信息的一种方式,通常使用内存或数据库来存储 Session 数据;而 JWT 是一种无状态的认证方式,Token 数据是通过编码后存储在客户端的 Cookie 或 LocalStorage 中,服务器不会存储 Token 数据。
-
跨域支持:由于 Session 是基于 Cookie 实现的,因此在跨域请求时需要特殊处理;而 JWT 则可以在不同域名之间共享,因为 Token 数据是存储在客户端的。
-
扩展性:JWT 可以扩展为不同的认证方式,例如基于 OAuth 的认证方式;而 Session 的扩展性相对较弱,需要在服务器端进行实现。
-
安全性:使用 Session 进行认证时,需要确保 Session ID 的安全,否则可能会被攻击者盗取;而使用 JWT 进行认证时,可以使用加密算法对 Token 数据进行加密,提高安全性。
-
无状态性:由于 JWT 是无状态的,因此可以避免服务器端的负载问题,特别适合于分布式系统和微服务架构。
总的来说,JWT 适合于分布式系统和无状态应用,而 Session 适合于传统的 Web 应用。使用哪种方式,取决于具体的需求和应用场景。
docker 镜像和容器的区别
Docker是一种用于构建和管理应用程序的开源平台。在Docker中,镜像和容器是两个重要的概念。
-
Docker镜像是应用程序的打包和分发方式。它包含了应用程序所需的所有文件、配置和依赖项,可以看作是一个轻量级的虚拟机模板。Docker镜像是只读的,一旦创建就不能更改。镜像通常是由Dockerfile文件定义的,Dockerfile文件是一种文本文件,用于指定构建Docker镜像所需的所有步骤。
-
Docker容器是Docker镜像的运行实例。容器是可运行的、独立的应用程序单元,可以在任何支持Docker的环境中运行。Docker容器是基于Docker镜像创建的,并且可以动态修改和更新容器中的应用程序和配置。容器可以启动、停止、暂停、恢复和删除。
可以将Docker镜像看作是应用程序的打包和分发方式,而Docker容器是应用程序的运行实例。在使用Docker时,通常首先构建一个Docker镜像,然后在Docker镜像的基础上创建多个Docker容器。这种分离的方式使得开发、测试、部署和运维过程更加高效、可靠和可重复。
输入一个网址,在浏览器的完整过程,越详细越好
-
解析URL(Uniform Resource Locator,统一资源定位符)。 URL是指定网络资源的地址。URL由多个部分组成,例如协议(例如http或https)、主机名(例如www.example.com)、端口号、路径和查询字符串。浏览器会解析URL以确定要访问的主机名和路径。
-
建立TCP连接。浏览器会使用HTTP(HyperText Transfer Protocol,超文本传输协议)或HTTPS(HTTP Secure,HTTPS是一种加密的HTTP)协议与Web服务器建立TCP连接。如果使用的是HTTPS,则还需要进行SSL/TLS握手以建立安全连接。
-
发送HTTP请求。一旦TCP连接建立,浏览器会向Web服务器发送HTTP请求。请求中包括HTTP方法(例如GET或POST)、资源路径、HTTP版本、请求标头和消息正文(对于某些请求,例如POST请求)。
-
接收HTTP响应。Web服务器将处理HTTP请求并生成HTTP响应。响应包括HTTP状态码、响应标头和响应正文。常见的HTTP状态码包括200 OK(请求成功)、404 Not Found(资源未找到)和500 Internal Server Error(服务器内部错误)。
-
处理响应。一旦浏览器接收到HTTP响应,它会解析响应并执行以下操作:
-
如果响应状态码为200,则解析响应正文并显示网页内容。
-
如果响应状态码为3xx,则浏览器将遵循响应中指定的重定向URL以获取重定向后的资源。
-
如果响应状态码为4xx或5xx,则浏览器将显示适当的错误页面。
-
-
渲染页面。浏览器会将HTML、CSS和JavaScript代码解析为可视化页面,并在屏幕上呈现出来。页面可能包括文本、图像、视频和其他媒体元素。
-
关闭TCP连接。一旦页面渲染完毕,浏览器会关闭TCP连接。
什么是 MVC?
模型、视图、控制器:解耦数据
-
M(Model)模型:负责业务对象和数据库的交互(ORM)
-
V(View)视图:负责与用户的交互展示
-
C(Control)控制器:接收请求参数调用模型和视图完成请求
ORM
ORM(Object Relational Mapping),对象关系映射,用于实现业务对象与数据表中的字段映射。比较出名的软件有:Sqlalchemy、Django ORM、Peewee
Web 框架对比 Django、flask、Fastapi、tornado
-
Django:高度集成的 Web 框架,适合开发复杂的 Web 应用程序,提供了大量的自带模块和插件,如:ORM、模板引擎、表单处理、认证等,非常适合快速搭建全功能的 Web 应用程序。但是自由度控制较少,有一定的学习曲线。
-
Flask:是一个轻量级的 Web 框架,比 Django 更为灵活,适合快速搭建小型的 Web 应用程序。提供了基本功能,如:路由、请求响应、模板引擎等,可通过插件来拓展功能。具有更高的自由度,但需要更多的手动配置和开发
-
FastAPI:FastAPI 是一个新兴的 Web 框架,以性能高和易用性强著称。FastAPI 基于 Python 3.7+ 的类型注解和异步编程模型,具有自动生成 API 文档、自动验证输入和输出数据等优秀特性。FastAPI 在处理高并发请求和异步 IO 方面表现出色,是构建高性能 Web 应用的不二之选。
-
Tornado:Tornado 是一个异步 Web 框架,特别适合处理大量的并发请求和实时流数据。Tornado 提供了基于协程的异步编程模型,其核心特性是非阻塞 IO 和事件驱动,比较适合构建长连接的 Web 应用程序,如聊天应用、实时推送等。
综上所述,选择哪个 Web 框架应该根据具体的应用场景和需求而定。如果需要开发高度集成、复杂的 Web 应用程序,可以选择 Django;如果需要开发轻量级、灵活的 Web 应用程序,可以选择 Flask;如果需要开发高性能的 Web 应用程序,可以选择 FastAPI 或 Tornado。
什么是 wsgi
WSGI(Web Server Gateway Interface)是 Python Web 应用程序和 Web 服务器之间的标准接口,它定义了 Web 服务器如何与 Python 应用程序进行通信。WSGI 规范主要包括两部分:服务器和应用程序之间的环境和通信协议。WSGI 的主要作用是为 Python Web 应用程序和 Web 服务器提供了一个标准的接口,让开发者可以使用不同的 Web 服务器来部署和运行他们的应用程序,同时也提高了 Web 应用程序的可移植性。
2 Django 框架
Django 中间件的使用场景是什么
是在请求和响应过程中位于视图函数前后的一系列可重用的组件,可用于拦截请求和响应,进行一些处理或修改,并将请求传递给下一个中间件或视图函数,应用场景:
-
认证和授权:验证是否登录或是否有权限访问特定页面
-
日志记录:记录请求和响应详细信息,包括请求时间、响应时间,请求方法、路径、响应状态码等,以便于调试和监控
-
缓存:提高网站性能和响应速度
-
异常处理:捕获视图函数中的异常,如发送邮件通知管理员等
Django 请求生命周期?
-
wsgi
: 获取解析HTTP
请求,并转发给Web
框架 -
中间件:对请求进行校验或在请求对象中添加其他相关数据,如:
csrf、request、session
-
路由匹配:根据请求
URL
去匹配不同的视图函数 -
视图函数:在视图函数中进行业务逻辑的出来,涉及到:
orm、templates => 渲染
-
中间件:对响应的数据进行处理
-
wsgi
:将响应的内容发送给浏览器
Django 内置组件?
-
Admin
:对model
中对应的数据表进行增删改查提供的组件 -
model
:负责操作数据库 -
form
组件:生成HTML
片段,对数据有效性进行校验 -
ModelForm
:用于数据库操作,也可以用于用户请求的验证
列举 Django
中间件的 5 个方法,及应用场景?
-
process_request
:请求进来时,权限验证 -
process_view
:路由匹配之后,能够得到视图函数 -
process_exception
:异常时执行 -
process_template_response
:模板渲染时执行 -
process_response
:请求有响应时执行
以上方法的返回值可以是 None
,或者是一个HttpResponse
对象,如果是 None
,则继续按照django定义的规则向后继续执行,如果是 HttpResponse
对象,则直接将改对象返回给用户。
Django 的 request
对象是在什么时候创建的?
class WSGIHandler(base.BaseHandler): request = self.request_class(environ)
请求到 WSGIHandler
类的时候,执行 __cell__
方法,将 environ
封装成 request
。
如何给 CBV
添加装饰器?
# 引入 method_decorator 模块 # 1. 直接给类加装饰器,所以请求方法(get、post 等) @method_decorator(test, name='dispatch') class LoginView(View): pass # 2. 单独给请求的函数添加装饰器 @method_decorator def post(self, request, *args, **kwargs):pass
Django 如何实现 csrf
?
只对 post
请求进行 csrf
防御,第一次请求时,会在服务端随机产生一个 token
,把这个 token
放到 cookie
中,然后每次 post
请求都会携带这个 token
,这样就能避免 csrf
攻击了。
Django 中如何实现单元测试?
使用 untest
模块,coverage
生成测试覆盖度报告。
简述 MVC
和 MTV
?
-
MVC
(Model View Controller)
:模型-视图-控制器,是一种Web
架构模式。特点:把业务逻辑、模型数据、用户界面分离开来,让开发者将数据与表现解耦-
Model
:模型,数据库层面 -
View
:视图,系统中选择显示什么和如何显示的部分 -
Controller
:系统中根据用户输入并视需要访问模型,以决定使用哪个视图的那部分
-
-
MTV
(Model Templates View
):模型-模板-视图-
Model
:数据存取层,处理与数据所以事务,操作数据模型,如何读取数据等 -
Templates
:表现层,将模型中的数据通过页面的形式展示给用户 -
View
:业务逻辑层,包含存取模型以及调取恰当模板的相关逻辑,如:从模板中获取用户输入,写入模型中;从模型中读取数据放到模板中显示灯。
-
简述什么是FBV和CBV?
FBV和CBV本质是一样的,基于函数的视图叫做FBV
,基于类的视图叫做CBV
,使用 CBV 的优点:
-
提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
-
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
Django本身提供了runserver,为什么不能用来部署?(runserver与uWSGI的区别)
-
runserver方法是调试 Django 时经常用到的运行方式,它使用Django自带的 WSGI Server 运行,主要在测试和开发中使用,并且 runserver 开启的方式也是单进程 。
-
uWSGI是一个Web服务器,它实现了WSGI协议、uwsgi、http 等协议。注意uwsgi是一种通信协议,而uWSGI是实现uwsgi协议和WSGI协议的 Web 服务器。uWSGI具有超快的性能、低内存占用和多app管理等优点,并且搭配着Nginx就是一个生产环境了,能够将用户访问请求与应用 app 隔离开,实现真正的部署 。相比来讲,支持的并发量更高,方便管理多进程,发挥多核的优势,提升性能。
Django如何实现websocket?
Django-channels 模块
Django 是 MVC 还是 MTV 模式
Django采用了类似于MVC的设计模式,但是由于模板层的引入,所以Django更常用的被称作MTV,即Model-Template-View。其中:
-
Model:负责业务对象和数据库的关系映射(ORM)。
-
Template:负责如何把页面展示给用户(html)。
-
View:负责业务逻辑,并在适当时候调用Model和Template。
3. Flask 框架
Flask 上下文
在 Flask 中,有两种上下文:应用上下文和请求上下文。应用上下文包含了应用的全局状态,而请求上下文则包含了关于当前请求的信息。
1、应用上下文:
在 Flask 中,应用上下文对象用于存储应用的全局状态,例如配置、插件、请求钩子等。应用上下文在应用启动时创建,并在应用关闭时销毁。在同一个线程中,应用上下文是共享的,可以使用 current_app
全局变量来访问应用上下文对象。
2、请求上下文:
在 Flask 中,请求上下文对象用于存储关于当前请求的信息,例如请求头、请求方法、URL 等。请求上下文对象在每个请求开始时创建,并在请求结束时销毁。在同一个线程中,请求上下文是独立的,可以使用 request
全局变量来访问请求上下文对象。
应用上下文和请求上下文可以使用 Flask 的 app_context()
和request_context()
函数进行手动创建,也可以使用 Flask 提供的 app 和 request 全局变量来隐式访问上下文对象。在使用 Flask 的扩展时,通常会隐式地使用上下文对象,因此开发者不需要手动创建。
下面是一个使用 Flask 上下文的简单示例:
from flask import Flask, request, current_app app = Flask(__name__) @app.route('/') def hello_world(): # 访问应用上下文对象 print(current_app.name) # 访问请求上下文对象 print(request.method) print(request.url) print(request.headers) return 'Hello, World!'
Flask 中请求钩子的理解和应用?
Flask 中的请求钩子是一种机制,用于在请求的不同阶段执行特定的操作。在 Flask 中,请求钩子主要有两种类型:before_request 和 after_request。
before_request 钩子会在每个请求处理之前被调用,可以用来执行一些全局操作,比如验证用户的登录状态、设置一些全局变量等。
-
在 before_request 钩子中,可以通过返回 None 或一个 Response 对象来控制请求的继续处理或提前结束。如果返回 None,则请求会继续向下执行,而如果返回一个 Response 对象,则会提前结束请求处理,并返回该 Response 对象。
-
after_request 钩子会在每个请求处理结束之后被调用,可以用来执行一些请求结束后的全局操作,比如关闭数据库连接、设置一些响应头等。在 after_request 钩子中,可以修改响应对象并返回它,也可以返回原始的响应对象或一个新的 Response 对象。
下面是一个简单的例子,展示了如何使用 before_request
钩子来验证用户的登录状态:
from flask import Flask, request, abort app = Flask(__name__) @app.before_request def check_login(): if request.path != '/login' and not request.args.get('token'): abort(401, 'Unauthorized') @app.route('/login') def login(): token = generate_token() # 生成登录 token return {'token': token} @app.route('/data') def get_data(): # 获取数据 return {'data': data} if __name__ == '__main__': app.run()
上面的每个请求都会先经过 check_login()
函数验证是否登录,你也可以给不用验证的请求加白名单。
对 Flask 蓝图(Blueprint)的理解?
Flask 蓝图(Blueprint)是一种组织 Flask 应用的方式,它可以将应用拆分为一系列小的应用组件,从而使应用更加模块化和易于管理。蓝图可以包含一组相关的路由、模板、静态文件、错误处理等应用组件,并可以被注册到 Flask 应用中。在一个 Flask 应用中可以同时注册多个蓝图,它们之间相互独立,可以共同组成一个完整的应用。
Flask 蓝图的主要作用有以下几点:
-
模块化应用:通过将应用拆分为多个蓝图,可以将不同功能的代码分开组织,从而提高应用的可读性和可维护性。
-
组合多个应用:多个蓝图可以在一个 Flask 应用中并存,从而形成一个完整的应用,这样可以更灵活地组合多个应用。
-
分离功能:蓝图可以让你将不同功能的代码分离开来,这样可以更好地重用代码、测试代码和独立部署不同功能的应用。
-
分离权限:蓝图可以用来隔离不同权限的用户,从而更好地控制用户访问不同的功能。
使用 Flask 蓝图的基本步骤如下:
-
创建一个蓝图对象:使用 Flask 的 Blueprint 类创建一个蓝图对象,可以指定蓝图的名称、导入名称、静态文件夹、模板文件夹等属性。
-
注册路由和视图函数:在蓝图对象上使用装饰器注册路由和视图函数,与 Flask 应用中的路由和视图函数注册方式相同。
-
注册蓝图:在 Flask 应用对象上使用 register_blueprint() 方法注册蓝图对象,可以指定蓝图对象的 URL 前缀、子域名等属性。
下面是一个简单的 Flask 蓝图的例子:
from flask import Blueprint bp = Blueprint('auth', __name__, url_prefix='/auth') @bp.route('/login') def login(): return 'Login page' @bp.route('/logout') def logout(): return 'Logout page'
Flask 常用第三方组件
-
Flask-WTF:表单处理组件,提供了 CSRF 保护、表单验证等功能;
-
Flask-Login:用户认证组件,提供了登录、注销等功能;
-
Flask-Mail:邮件发送组件,可以方便地集成到应用中发送邮件;
-
Flask-RESTful:RESTful API 开发组件,提供了路由、参数解析、错误处理等功能;
-
Flask-SQLAlchemy:数据库 ORM 组件,支持多种关系型数据库,提供了简单易用的查询接口;
-
Flask-Caching:缓存组件,支持多种缓存类型,提供了简单的接口;
-
Flask-Admin:后台管理组件,提供了 CRUD 操作、搜索、过滤等功能;
-
Flask-Uploads:文件上传组件,支持多种存储方式,提供了上传验证、文件处理等功能;
-
Flask-Migrate:数据库迁移组件,可以方便地管理数据库结构的变更。
Web 安全
sql 注入
通过特殊的输入参数传入 web 应用,导致后端执行了恶意的 sql 语句
-
通常由于程序员未对输入进行过滤,直接动态拼接 sql 产生
-
可以使用开源工具
sqlmap
、sqlninja
检测
如何防范?
-
永远不要相信用户的输入
-
对输入参数做好检查(类型和范围),过滤和转义字符
-
不要拼接 sql,使用占位符或者 ORM 可以大大降低 sql 注入的风险
-
数据层:做好权限管理配置,不要明文存储存储信息
# sql 语句拼接(不推荐) sql = "select * from users where name='"+ name +"' + "and password=md5('"+password+"')"" # SQL 语句注入,'---' 将后面的 SQL语句注释掉,从而到达不需要输入密码就能取出数据的目的 rose '--' # 使用占位符 sql = "select * from users where name=%s and password=md5(%s)"
xss 跨站脚本攻击
恶意用户将代码植入到提供给其他用户使用的页面中,未经转义的恶意代码输出到其他用户浏览器被执行
-
用户浏览的页面被恶意植入 JS 语句,其他用户访问同一个页面时,JS文件被执行,从而到达攻击用户的目的。
-
主要分为两类:反射型(非持久型:放 url 中,发给用户,诱惑用户点击),存储型(持久型)
-
危害:盗取用户 cookie,获取敏感信息
如何防范?
过滤用户输入的特殊字符,如:<script>、<、>
等
bs4 = BeautifulSoup(article_content, 'html.parser') # 过滤 script、link 标签 for tag in bs4.find_all(): if tag.name in ['script', 'link']: tag.decompose() # 删除
csrf 跨站请求伪造
CSRF(Cross-Site Request Forgery),也称为“跨站请求伪造”,是一种常见的网络攻击方式,攻击者利用用户在已登录的网站上的身份信息,发送一个伪造的请求,以达到攻击者的目的。
攻击者通常使用各种方式来诱导用户访问一个特定的网站或点击一个特定的链接,当用户在登录状态下访问该站点或点击该链接时,攻击者就可以利用用户的身份信息发送一个伪造的请求。这样,攻击者就可以在用户不知情的情况下执行一些非法操作,如修改用户信息、转账等。
为了避免 CSRF 攻击,可以采取以下措施:
-
验证来源站点:在服务端对请求的来源站点进行验证,只允许来自合法站点的请求。可以在请求中添加一个 token 来进行验证,防止伪造的请求被执行。
-
限制敏感操作:对于一些敏感操作,如修改用户信息、删除数据等,需要进行二次确认,或者设置必须使用 POST 方法才能执行的操作。
-
合理设置 Cookie:在 Cookie 中设置 HttpOnly 属性,防止 Cookie 被 JavaScript 获取。
-
使用验证码:在关键操作前添加验证码校验,确保操作是由人类进行的。
-
及时更新:定期更新软件和框架,并及时修复已知漏洞。
网络编程
TCP 和 UDP 区别
-
TCP是一种面向连接的协议,它在数据传输之前需要进行三次握手建立连接,建立连接后才能进行数据传输,而且在传输数据时需要保证数据的可靠性,即要保证数据的顺序和完整性,所以TCP具有高可靠性和安全性的特点,但是传输效率相对较低,适用于需要保证数据可靠性的场景,如文件传输、邮件传输等。
-
UDP是一种无连接的协议,它不需要进行连接的建立和断开,数据包可以直接发送到目的地址,但是由于不保证数据的可靠性和顺序,所以在传输数据时有可能丢失数据、数据包到达的顺序与发送时的不同等情况,但是传输效率相对较高,适用于需要高效传输数据的场景,如实时音视频传输、游戏等。
因此,TCP适用于需要保证数据可靠性的场景,UDP适用于需要高效传输数据的场景。
HTTP 常见状态码
-
1xx:服务器收到请求,需要请求者继续执行操作
-
2xx:操作被成功接收并处理
-
3xx:重定向,需要进一步操作完成请求
-
4xx(客户端错误):请求有语法错误或无法完成请求
-
5xx(服务端错误):服务器处理请求过程中发生错误
- 200:请求成功 - 301:资源被永久转义到其他 URL - 403:Forbidden - 404:请求资源不存在 - 500:内部服务器错误 - 504:充当网关或代理的服务器,未及时从远程服务器获取请求 - 400 Bad Request: /客户端请求有语法错误,不能被服务器所理解 - 503 Server Unavailable: 服务器当前不能处理客户端的请求,一段时间后可能恢复正常 - 401 Unauthorized: 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
http 和 https 区别
HTTP(HyperText Transfer Protocol)和HTTPS(HyperText Transfer Protocol Secure)都是应用层协议,用于在网络中传输数据。HTTP协议是不安全的,而HTTPS协议通过加密和身份验证来提供更高的安全性。
下面是HTTP和HTTPS之间的区别:
1.安全性:HTTP不提供数据加密,而HTTPS使用 SSL/TLS 协议对传输的数据进行加密,从而更加安全。
2.端口:HTTP使用端口80,而HTTPS使用端口443。
3.证书:HTTPS需要使用数字证书来验证网站的身份,确保网站是可信的,而HTTP不需要证书。
4.速度:由于加密和解密数据需要额外的计算资源,HTTPS比HTTP要慢一些。
5.使用场景:HTTPS适用于敏感数据的传输,如个人身份信息、支付信息等,而HTTP适用于不需要保密性的数据传输。
总的来说,HTTP适用于一些不需要保密性的场景,而HTTPS则更适用于需要保密性和数据安全的场景。
Unix 网络模型
-
阻塞 I/O:最基本的网络模型,在 I/O 时,进程会一直等待,直到 I/O 操作完成。等待过程中,进程无法进行其他操作,因此不能同时处理多个连接。适用于连接数较少的应用场景。
-
非阻塞 I/O:进程不会等待,而是立即返回。若操作未完成,进程会再次轮询。可以处理多个请求,但需要轮询技术,会导致 CPU 占用率较高。
-
I/O 多路复用:允许进程同时监听多个连接的 I/O 操作,它通过 select、poll 或 epoll 等系统调用实现。可以有效处理大量连接,而且 CPU 占用率比非阻塞 I/O 模型低。
-
信号驱动 I/O:通过信号函数来实现异步 I/O 操作,在进行 I/O 操作时,进程会发起一个系统调用,并注册一个信号处理函数,在 I/O 操作完成后,系统会向进程发送一个信号,进程就可以在信号处理函数中进行后续处理。优点是可以处理多个连接,而且 CPU 占用率也较低,但需要编写复杂的信号处理函数。
-
异步 I/O:最先进的网络模型,它通过 aio_read、aio_write 等异步 I/O 函数来实现。在进行 I/O 操作时,进程会发起一个异步 I/O 请求,并继续执行后续操作,当 I/O 操作完成时,系统会向进程发送一个信号或回调函数,进程可以在回调函数中进行后续处理。这种模型可以同时处理多个连接,而且 CPU 的占用率也很低,但需要操作系统支持异步 I/O 函数。
请介绍一下 三次握手和四次挥手
三次握手
-
客户端发送一个 SYN 数据包到服务器端口,进入 SYN_SEND 状态,等待服务器确认。
-
服务器收到 SYN 数据包,必须确认客户端的 SYN,同时也发送一个 SYN 数据包到客户端,进入 SYN_RECV 状态。
-
客户端收到服务器的 SYN 数据包,必须向服务器发送确认,此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。
四次挥手
-
客户端发送一个 FIN 数据包,表示客户端已经没有数据要发送了,进入 FIN_WAIT_1 状态。
-
服务器收到 FIN 数据包,返回一个 ACK 数据包,表示收到了客户端的关闭请求,进入 CLOSE_WAIT 状态,等待服务器端的数据发送完成。
-
服务器端数据发送完成,发送一个 FIN 数据包,请求关闭连接,进入 LAST_ACK 状态。
-
客户端收到服务器的 FIN 数据包,发送一个 ACK 数据包,表示已经收到服务器的关闭请求,进入 TIME_WAIT 状态。服务器收到 ACK 数据包后,进入 CLOSED 状态,连接关闭。
通过三次握手,保证了客户端和服务器都能够正常通信;四次挥手,则是保证了客户端和服务器都能够正确关闭连接,避免了数据丢失和重复发送等问题。
select、poll 和 epoll 的区别
select、poll、epoll 都是 I/O 多路复用的机制,用于实现同时监听多个文件描述符的可读/可写/异常事件,从而避免阻塞等待。
它们的主要区别如下:
-
select 和 poll 对文件描述符的管理采用的是线性扫描方式,每次调用都需要将全部的文件描述符从应用进程中复制到内核中,效率较低。而 epoll 采用基于事件驱动的方式,只有当描述符状态发生变化时,才会调用回调函数。
-
select 和 poll 的缺点在于,每次调用都需要重复地将描述符集合从用户态复制到内核态,而 epoll 利用了内核与用户空间共享内存的方式避免了这种复制开销。因此,当需要监听的文件描述符数量较大时,epoll 的效率会更高。
-
在 epoll 中,可以使用 ET(Edge Trigger)和 LT(Level Trigger)两种触发方式。LT 模式下,当一个文件描述符上有事件发生时,epoll_wait() 就会立即返回,应用程序可以进行相应的操作。ET 模式下,只有当文件描述符从无可读/写/异常状态转变为可读/写/异常状态时,才会通知应用程序。因此,在 ET 模式下需要注意一些细节问题,例如非阻塞 IO 和边缘触发模式的配合使用,避免遗漏事件等。
总之,epoll 是相比于 select 和 poll 更加高效的一种 I/O 多路复用机制,尤其在需要同时监听大量文件描述符的情况下表现更优秀。