一篇文章帮你拿下面试八股文之网络三次握手四次挥手, HTTP超文本传输协议重点理论刨析到实现简单的HTTP服务, 思考着图解着学习网络 (咱不死记硬背)

简介: 一篇文章帮你拿下面试八股文之网络三次握手四次挥手, HTTP超文本传输协议重点理论刨析到实现简单的HTTP服务, 思考着图解着学习网络 (咱不死记硬背)

一. 三次握手  (TCP连接建立)

服务器端在一开始就调用listen() 函数进入监听状态, 阻塞监听着来自客户端的连接请求


客户端调用  connect()  函数发起一个连接请求, 进入 SYN_SENT 状态


服务端接收到这个连接请求, 将其放到SYN队列中, 应答客户端并且向客户端发起连接请求, 服务器进入SYN_RECVD状态


客户端接收到服务端发送过来的数据包, 客户端确定连接建立, connect返回, 应答服务器且进入ESTABLISHED状态


服务器接收到应答包, accept返回, 服务端确立连接建立, accept返回其实是从accept队列中拿取一个节点的.


注意, 细节问题, 在上述过程中其实还存在两个队列, 一个叫做  SYN队列,也叫做等待队列, 是服务端接收到来自客户端的SYN请求之后都会将这个请求挂在SYN 队列下面,  还有一个队列叫做accept队列, 这个队列中都是完成了三次握手的请求, 也就是刚刚SYN队列中的对应节点直接转移过来的.   accept其实就是从 accept队列中拿取的一个节点分析返回connfd的


细节疑惑刨析


SYN  ACK  都是标志位   SYN: 标志发起一个连接请求   ACK : 标识确认号是否有效, 确认刚刚发起的连接请求是否收到, 标识应答.


seqnum  arcnum 都是序号,     seqnum是初始化序号,  是arcnum是确认序号(应答序号)


上述此处仅暂做简单理解, 后序分析各种协议报文的时候会详解.

面试灵魂拷问, 为什么是三次握手,而不是二次握手也不是四次握手?

很多面经上面的的统一回答方向:  为了确立客户端和服务端双放的收发数据是否正常


上述回答肯定是没有问题的,  毕竟三次握手就是为了在最小的资源浪费条件下让CS双方建立一个稳定的通信的连接通道


第一二次握手, 客户端确定服务端是可以正常收到我的数据包的, 也是可以正常发送数据包的, 但是服务端并不知晓客户端是否可以正常接收到它的应答请求包, 它仅仅知道客户端发送是OK的


第一三次握手,  服务端确定客户端是可以正常收发数据的


四次五次乃至更多次握手不是不可以达到要求, 而是三次握手就足以建立CS双方稳定的连接, 所以没有必要再进行更多的资源浪费...  


上述回答结束OK吗? 当然是OK的, 但是其实存在诸多细节没有刨析


从连接已失效的历史请求报文段的方向来思考为什么必须要三次握手而不是两次握手.

上述就是两次连接会造成的经典问题, 连接历史包, 在拥塞的网络环境下, 可能刚开始一个客户端发送的历史包被网络阻塞住了, 新的客户端发送的连接还没有到来之前的历史失效报文段阻塞消失了, 并且 历史包还体现到达了服务端 , 两次握手, 服务端此时向客户端发出应答报文段完成连接建立, 误以为新的连接已经成功建立了, 就会傻等着这个客户端发送数据过来, 但是当然正在连接请求的客户端根本不认识这个server的确认连接的数据包, 不会理睬这个server的确认连接的信息,  重点来了, 这个时候没有第三次连接呀, 客户端倒是知道了这个数据包不对, 但是服务端 已经是确立连接的状态了, 就会一直等着服务这个客户端, 但是事实是人家不鸟他, 但是三次握手才让服务端确立连接就可以避免服务端的空白连接, 浪费服务端的资源

二. 四次挥手 (TCP的连接断开)

第一次挥手, 客户端发送一个 FIN(结束), 用来关闭自己到服务器的连接,  注意: 是断开的客户端到服务器的单向连接, 仅仅只是客户端不可以再向服务端发送数据了...  客户端进入FIN_wait1 状态


第二次挥手, 服务端接收到这这个 FIN , 先回一个 ACK, 我知道了,至此半关闭状态完成, 服务端进入 CLOSE_WAIT状态, 这个 CLOSE_WAIT的原因是因为, 服务端还需要将手头上没有发送完的数据包发送完, 因为一旦发送了 FIN(结束)就无法在发送数据了。。。   同时客户端收到ACK应答进入FIN_WAIT2状态


第三次挥手,  服务端发送一个FIN(结束)到客户端, 关闭服务端到客户端的连接, 服务端至此进入LAST_ACK状态, 等待客户端确认


第四次挥手, 客户端发送ACK 报文确定, 同时在等待  2MSL 之后完成CLOSE.


2MSL  :   等待网络中残存的数据包丢失, 不然下一次复用同一个端口的时候可能会收到这些遗留的数据包, 造成错误


MSL :  (max segment live) 报文最大生存时间


FIN : 结束标志位        SYN : 同步连接标志位      ACK: 确认标志位    (常见标志位)  

面试灵魂拷问2,为什么是四次挥手, 而不是三次

我们类比着三次握手来解释,  三次握手之所以是三次。 是因为第二次的时候是 SYN (同步序号)和ACK(确认应答) 合在了一起, 放在一个报文中发送给了客户端  (此处不懂请回溯)


But  :  四次挥手的时候   FIN 和 ACK  不可以放在同一个报文发送给客户端, 这个是为什么?


因为对方(Client)发送FIN 过来 仅仅代表客户端没有数据需要再发送给你服务端了, 但是你服务端可能还有数据需要发送响应给客户端, 所以你服务端暂时不能直接回FIN, 因为一旦回了FIN 就不可以再发送数据了, 所以只能先ACK, 等服务端把当前还需要发送的数据发送完再发送FIN 请求关闭连接.  ------   故此 FIN 和 ACK 需要分开

三. HTTP超文本传输协议

啥叫作超文本, URL, DNS?

我听过超链接, 也听过文本, 但是木有听过超文本, 其实就是含有超链接的文本文件彼此之间链接起来, 形成网状,是不是迷糊了, 其实还有个别称: 网页.这些链接使用的都是URL, 不清楚这个的在写博文的时候点一下你的链接, 上面就有...


啥是URL?   网页资源在服务器上的什么位置问题的解决,    网页是放在在服务器上的

DNS:域名系统     (解决IP地址不好记忆的问题), 记IP你能记住几个?? 根部记不住,

原理:  


一个组织的系统管理机构, 系统内的每个主机的IP和主机名的对应关系.


如果新计算机接入网络, 将这个信息注册到数据库中;


用户输入域名的时候, 会自动查询DNS服务器, 由DNS服务器检索数据库, 得到对应的IP地址.


DNS系统: 是一整套从域名映射到IP的系统

超文本传输协议HTTP(正式刨析)

  • 超文本传输协议  :    通过URL的指示, 将服务器上的超文本文档(网页) 传输到 客户端(浏览器)上的一种应用层协议....
  • HTTP协议是基于TCP/IP的一个应用层协议
  • HTTP的工作原理图解如下

上述图解分析的仅仅只是最为简单的最初版本的HTTP请求, 一次请求之后一个流程走完, 请求网页, 响应网页之后立马断开...   这个叫做  非持久性连接,  


问题  :   每一次请求网页都必须建立和断开一次TCP连接, 效率着实低下


然后 HTTP  1.2 版本叫做持久性连接  :    正如其名:  它支持了一个连接中, 可以多次进行网页的请求和响应, 咱商量好哈, 咱不那么费, 访问的人多, 咱把这个连接的时间定的短一点, 不造成网络拥堵, 访问的人少, 咱把连接的时间定的长一点, 这个连接保持的时间可以应需求而定  


无状态性  :  就是说每一次访问都是新的一次访问, 服务器不知道你曾经是否访问过, HTTP的无状态性简化了服务器的设计, 使其更容易支持大量并发的HTTP请求   ( 说实话这句话是网上抄的, 自己不是特别理解, 很朦胧, 要是大佬看见希望评论区留下您的理解, 大家有自己的见解都可以讨论, 万分感谢)


最后下面附上报文的各种请求方法截图一张:   (主要也就是  GET  和  POST)   一个是从服务器申请网页, 另外个是往服务器上 POST 网页  

这个状态码感兴趣的看一看就是  咱经常访问哪些  (非法网站的时候) 会弹出  40几呀   50几呀, 懂得都懂3

然后简单的写一个HTTP服务器 (最简易版本)

  • 网页上打印一个  hello hhtp,   如下,  其实几乎和  TCP  差不多, 只是我们需要按照HHTP的规则来构造数据, 以及访问URL
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <string.h>
#define ERR_EXIT(m) \
  do { perror(m); exit(EXIT_FAILURE); } while (0)
typedef struct sockaddr SA;
int main(int argc, char* argv[]) {
  int connfd, listenfd;
  socklen_t buffLen;
  char dst[256] = {0};
  struct sockaddr_in clientAdd, serveAdd;
  if (argc != 2) {
    fprintf(stderr, "Usage: %s <ip>\n", argv[0]);
    exit(EXIT_FAILURE);
  }
  //域(协议家族), 服务类型, 弃用
  if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
    ERR_EXIT("socket error");
  }
  //确定协议地址簇
  serveAdd.sin_family = AF_INET;//协议家族
  serveAdd.sin_addr.s_addr = htonl(INADDR_ANY);//确定ip, 通配ip
  serveAdd.sin_port = htons(atoi(argv[1]));//确定绑定端口号
  //bind绑定固定端口号便于接收连接请求
  if (bind(listenfd, (SA*)&serveAdd, sizeof(serveAdd)) == -1) {
    ERR_EXIT("bind error");
  }
  //开始监听
  if (listen(listenfd, 3) == -1) {
    ERR_EXIT("listen error");
  }
  fprintf(stdout, "wait accept:\n");
  for ( ;; ) {//循环接收客户端的请求
    buffLen = sizeof(clientAdd);
    if ((connfd = accept(listenfd, (SA*)&clientAdd, &buffLen)) == -1) {
      ERR_EXIT("accept error");
    }
    printf("accept connection from ip is %s and port is %d \n", 
      inet_ntop(AF_INET, &clientAdd.sin_addr, dst, sizeof(dst)), 
      ntohs(clientAdd.sin_port));
    char input_buff[1024 * 10] = {0}; //开足够大的空间写入数据
    ssize_t read_size = read(connfd,input_buff, sizeof(input_buff) - 1);//留一个结束字符
    if (read_size == -1) {
      ERR_EXIT("read error");
    } 
    printf("[Request] %s", input_buff);
    char  buff[1024] = {0};
    const char* hello = "<h1>hello hhtp</h1>";
    sprintf(buff, "HHTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);//按照hhtp规矩写入
    write(connfd, buff, sizeof(buff));
  }
  exit(EXIT_SUCCESS);
}

备注: 此处我们使用 9090 端口号启动了HTTP服务器. 虽然HTTP服务器一般使用80端口,


但这只是一个通用的习惯. 并不是说HTTP服务器就不能使用其他的端口号.


使用chrome测试我们的服务器时, 可以看到服务器打出的请求中还有一个 GET /favicon.ico HTTP/1.1 这 样的请求.

四. 总结

首先本文描述了三次握手, 核心关键在于  为什么是三次不是二次也不是四次?


简单解释是:  为了使得确定客户端服务端双放的收发数据的正常, 确立双方稳定的数据通信的连接,   从避免历史包的接收建立服务端的无效连接浪费服务端资源的角度再理解了一下为啥两次连接不行, 四次乃至更多次的连接不是不可以而是没有必要, 浪费资源


四次挥手重点, 为啥是四次挥手不是三次?  核心点在于  这个  FIN + ACK为啥不能何在一起? (此处你应该能自己回答出来)不能请回溯


最后介绍HHTP 应用层协议: 超文本传输协议, 其实还有 FTP:文件传输协议等等 都是再应用层上的协议, HHTP : 超文本传输协议, 和我们平时使用的网页网站强相关, 所谓网页就是多个超链接连接一次构成的网状结构.  网站可以理解为存储多个网页的服务端.


DNS  域名系统, 为了解决IP地址不好记忆的问题, 搞出来了IP映射的这样一个域名系统, 可以利用域名映射IP地址  +  端口号访问服务器,


URL  : 服务类型  域名(IP)  端口号  文件路劲     资源定位


GET    从网站中获取网页       POST: 向网站中post网页


HTTP  是建立的一次TCP连接的基于TCP在其之上的应用层协议


HTTP  多次不断的进行TCP的连接和断开连接的效率低下方面的解决措施:  搞出来了持久连接  (连接保持一段时间)


无状态性  :  不会记录之前是否访问, 简化服务器设计, 更容易支持大量并发



相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
数据采集 算法 数据挖掘
模块化控制协议(MCP)在网络中增强智能体执行效率的研究
随着Web3技术的迅速发展,去中心化应用和智能体在各种领域的应用逐渐增多。MCP(Modularized Control Protocol,模块化控制协议)作为一种增强智能体执行能力的关键技术,为Web3场景中的智能体提供了更强的灵活性和可扩展性。本文将探讨如何利用MCP技术提升智能体在Web3场景中的执行能力,并通过实例代码展示其实现路径。
1229 22
|
10月前
|
监控 负载均衡 安全
WebSocket网络编程深度实践:从协议原理到生产级应用
蒋星熠Jaxonic,技术宇宙中的星际旅人,以代码为舟、算法为帆,探索实时通信的无限可能。本文深入解析WebSocket协议原理、工程实践与架构设计,涵盖握手机制、心跳保活、集群部署、安全防护等核心内容,结合代码示例与架构图,助你构建稳定高效的实时应用,在二进制星河中谱写极客诗篇。
WebSocket网络编程深度实践:从协议原理到生产级应用
|
JSON 中间件 Go
Go 网络编程:HTTP服务与客户端开发
Go 语言的 `net/http` 包功能强大,可快速构建高并发 HTTP 服务。本文从创建简单 HTTP 服务入手,逐步讲解请求与响应对象、URL 参数处理、自定义路由、JSON 接口、静态文件服务、中间件编写及 HTTPS 配置等内容。通过示例代码展示如何使用 `http.HandleFunc`、`http.ServeMux`、`http.Client` 等工具实现常见功能,帮助开发者掌握构建高效 Web 应用的核心技能。
591 61
|
11月前
|
存储 网络协议 算法
从HPACK到多路复用,揭秘HTTP/2如何终结网络拥堵
HTTP/2通过HPACK压缩头部冗余信息,提升传输效率;并利用多路复用技术,在单个TCP连接上并行处理多个请求,避免队头阻塞,显著提升性能。同时支持服务器推送和流优先级设置,优化资源加载体验。
647 7
|
11月前
|
运维 架构师 安全
二层协议透明传输:让跨域二层协议“无感穿越”多服务商网络
简介:本文详解二层协议透明传输技术,适用于企业网工、运营商及架构师,解决LLDP/LACP/BPDU跨运营商传输难题,实现端到端协议透传,提升网络韧性与运维效率。
|
负载均衡 架构师 Cloud Native
阿里面试:服务与发现 ,该选 CP 还是 AP?为什么?
阿里面试:服务与发现 ,该选 CP 还是 AP?为什么?
阿里面试:服务与发现 ,该选  CP 还是 AP?为什么?
|
安全 网络协议 Linux
Linux网络应用层协议展示:HTTP与HTTPS
此外,必须注意,从HTTP迁移到HTTPS是一项重要且必要的任务,因为这不仅关乎用户信息的安全,也有利于你的网站评级和粉丝的信心。在网络世界中,信息的安全就是一切,选择HTTPS,让您的网站更加安全,使您的用户满意,也使您感到满意。
436 19
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
网络安全 开发者
如何解决HTTPS协议在WordPress升级后对网站不兼容的问题
以上就是解决WordPress升级后HTTPS协议对网站的不兼容问题的方法。希望能把这个棘手的问题看成是学校的管理问题一样来应对,将复杂的技术问题变得更加有趣和形象,并寻觅出解决问题的方式。希望你的网站能在新的学期得到更好的发展!
361 19
|
JSON 安全 网络协议
HTTP/HTTPS协议(请求响应模型、状态码)
本文简要介绍了HTTP与HTTPS协议的基础知识。HTTP是一种无状态的超文本传输协议,基于TCP/IP,常用80端口,通过请求-响应模型实现客户端与服务器间的通信;HTTPS为HTTP的安全版本,基于SSL/TLS加密技术,使用443端口,确保数据传输的安全性。文中还详细描述了HTTP请求方法(如GET、POST)、请求与响应头字段、状态码分类及意义,并对比了两者在请求-响应模型中的安全性差异。
1189 20

热门文章

最新文章