HTTP协议
HTTP就是跑在TCP上面的应用层协议,专门规定浏览器和服务器怎么传文本、图片这些网页资源。这里分两种模型:C/S和B/S。
C/S 客户端/服务器模型
例子:QQ、英雄联盟这种,你电脑上得单独装一个软件客户端。
✅优点:资源放你本地电脑,打开飞快;就算断网连不上服务器,很多功能照样能用。
❌缺点:
-
占你硬盘存储空间
-
Windows、Mac系统不一样,开发要单独写两套代码,麻烦
-
服务器更新内容,你本地软件还得手动下载升级、打补丁
B/S 浏览器/服务器模型
就是咱们平时用的网页,不用装软件,打开浏览器就能访问网站。
特点:
-
所有图片文字资源全部存在远端服务器,不在你电脑
-
网站上新内容,不用你更新任何东西,服务器一改,所有人直接看到新页面
-
每次点开网页,都是临时从服务器下载资源到本地临时看
-
只要设备有浏览器就能访问,代码通用,不用单独开发客户端,省老多事
URL(统一资源定位符,就是网址)
格式:<协议>://<主机>:<端口>/<路径>
• 协议:用啥方式拿资源,常见http或者https
• 主机:服务器IP,平时咱们写域名,DNS会自动把域名翻译成IP地址
• 端口:服务器程序的门牌号!HTTP默认80或者8080,HTTPS是443
• 路径:资源在服务器硬盘里放的位置
HTTP完整传输流程(你在浏览器敲网址,背后发生啥)
-
浏览器输入URL网址
-
浏览器跟服务器IP+端口,先建立TCP连接(三次握手那套)
-
浏览器发送HTTP请求报文,告诉服务器我想要啥东西
-
服务器收到,返回HTTP响应报文,把网页数据发回来
-
数据传完,断开TCP连接(四次挥手)
HTTP请求报文长啥样
分三块:请求行、首部行、实体主体
- 请求行:方法 + URL + HTTP版本,空格隔开,结尾回车换行
◦ GET:拿服务器上的资源(比如打开网页)
◦ POST/PUT:往服务器提交数据(登录、上传表单)
-
首部行:一堆键值对,比如浏览器型号、编码格式,一行一组,末尾回车换行
-
空一行CRLF隔开,最后实体主体,GET请求一般这块是空的,POST才会放提交的数据
HTTP响应报文
同样三块:状态行、首部行、实体主体
- 状态行:版本 + 状态码 + 描述短语
状态码分类记这个:
1xx:通知信息,收到请求了,正在处理
2xx:成功!请求搞定
3xx:重定向,要跳转别的地址
4xx:客户端的锅,你这边请求写错了(比如404找不到页面)
5xx:服务器炸了,服务器这边出问题
-
首部行:和请求差不多,一堆键值对,告诉客户端文件类型、长度
-
实体主体:真正返回给你的网页、图片数据
TCP并发服务器模型
咱们写TCP服务器,要同时接待好多客户端,就叫并发。
- 多线程模型
思路:来一个客户端连接,就新开一个线程专门伺候这个客户。
优点:能实现多个人同时连服务器。
缺点:每个线程都要占独立内存栈,能开的线程数量有上限。如果几千上万客户端一起连,线程太多,系统直接崩掉。
一般搭配线程池用,提前开好一批线程,来了连接直接复用,不用反复创建销毁。
四种IO模型
- 阻塞IO
最省事。没有数据的时候,CPU直接把这个任务挂起歇着,完全不占CPU资源。等数据来了再唤醒。
- 非阻塞IO
比较浪费CPU。就算没数据,程序也不停循环、反复去问内核:"数据来了没?来了没?",疯狂轮询,CPU一直干活。
- 信号驱动IO(异步IO)
先告诉内核:帮我盯着这个文件描述符。一旦有事件来了,内核给我发信号通知我。我收到信号之后再来处理数据。平时程序该干嘛干嘛,不用一直等。
- 多路复用IO
一句话:一个线程,同时监视一堆文件描述符,哪个有事件就处理哪个。代表:select、epoll。
select
一次性放一堆fd(文件描述符)让内核帮忙盯着。只要集合里任意一个fd有读写事件,select就不再卡住,把有事件的fd留在集合里,没事件的删掉。
配套4个宏函数:
FD_ZERO:清空集合
FD_SET:把fd加入集合
FD_CLR:把fd从集合删掉
FD_ISSET:判断fd在不在集合里面
select函数参数:
nfds:所有fd里面最大的那个数字 +1
readfds:读事件集合
writefds:写事件集合
exceptfds:异常事件集合
timeout:超时时间。填NULL代表死等,一直阻塞。
返回值:成功返回有事件的fd数量;超时没事件返回0;出错返回-1。
select短板:
-
监听fd的数量有上限,数组大小卡死
-
fd集合放在用户层,事件来了内核要把数据拷贝到用户空间,开销大
-
只能水平触发
-
事件来了之后,得自己循环遍历全部fd,才能找到哪个有数据,很麻烦
epoll(Linux专属,性能比select强很多!)
epoll分3个核心函数:
-
epoll_create:创建内核里面的事件表,返回一个epoll的fd。size参数现在基本忽略。
-
epoll_ctl:操作epoll事件表,可以增、改、删监听的fd事件
EPOLL_CTL_ADD:添加fd进事件表
EPOLL_CTL_MOD:修改已经监听的事件
EPOLL_CTL_DEL:删掉fd
事件类型:EPOLL_IN 可读、EPOLL_OUT可写、EPOLLET开启边沿触发
- epoll_wait:蹲事件,阻塞等待,有事件就把触发事件的fd打包返回给用户。
timeout=-1就是一直死等。返回值是触发事件的fd个数,失败返回-1,超时返回0。
epoll牛逼在哪:
-
监听fd数量几乎没有上限,百万连接都扛得住
-
事件表放在内核里面,不用来回拷贝大量数据,省资源
-
支持水平触发,还支持高速的边沿触发EPOLLET
-
内核直接把发生事件的fd挑好给你,不用自己遍历全部fd,速度起飞