HTTP #web服务器 #三次握手四次挥手 #多进程 #多线程 #多协程 #非阻塞 #epoll #网络通信

一、HTTP是什么

  • http就是超文本传输协议 ,它基于TCP。是浏览器和服务器之间用于传输的一种规定,请求的时候按照什么什么发,回的时候按照什么什么发,它们都是基于TCP发送的二进制数据。现今,HTTP语句运用到了不在浏览器的方面。
  • 浏览器一定是客户端
    在这里插入图片描述
  • 这一坨绿油油的就是HTTP协议。具体解释如下:
GET /happy.html HTTP/1.1  # 请求方式Get,请求文件happy.html,HTTP1.1版本
# 下面的东西,类似于字典(其实是字符串)
Host: 127.0.0.1:8080  # 主机端口
Connection: keep-alive  # 长链接
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)   AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36  # 客户端浏览器版本
Sec-Fetch-User: ?1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3  # 浏览器能接收的格式
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Accept-Encoding: gzip, deflate, br  # 能接收的压缩格式
Accept-Language: zh-CN,zh;q=0.9  # 能接收的语言
  • 查看HTTP协议
  1. 如图操作
    在这里插入图片描述
  2. 点击请求和应答后面的View Source
  • 这就是浏览器发送的请求
    在这里插入图片描述
  • 这就是浏览器接收的应答
    在这里插入图片描述
    一去一回就是HTTP协议。
    服务器返回的东西中,上面那个应答的部分,被称为应答头head。HTTP/1.1 200 OK是其中必不可少的。后面紧跟传输的内容body,是浏览器真正传输的东西。第一个空行上方是head,下方是body。
  • 我们用网络调试助手,充当服务器给浏览器法信息。(需要有一个返回头,和一串信息。)
HTTP/1.1 200 OK

<h1>hello world</h1>

在这里插入图片描述

二、Web服务器

1.写代码框架

  • 既然网络调试助手可以当服务器,那么之前写的python程序也可以
  • 准备一个html文件
  • 代码如下:
    在这里插入图片描述
  • 效果如下:
    在这里插入图片描述

2.TCP的三次握手、四次挥手

2.1三次握手

  • 三次握手就是双方准备资源
  • connect默认堵塞,在三次握手成功时,解堵塞。也就是说,握手的起点是connect。
  1. 客户端:你准备好了吗?

客户端给服务器发送一个数字。
为了显示是第一次发送的请求,前面用syn标记

  1. 服务器:我好了,你呢?

服务器将这个数字+1返回,用ack标记
并返回一个新的值,用syn标记

  1. 客户端:愉快地开始玩耍吧

客户端将这个新的值+1返回,用ack标记
在这里插入图片描述

2.2四次挥手

  • 四次挥手就是双方释放资源
  • TCP是全双工的,必须得收发都关了,才是真正的关闭。
  • 挥手的起点是close。
  1. 客户端:我他娘地以后不再跟你讲一句话。(客户端关闭发送)

客户端不和服务器说话时,服务器的 client_socket.recv()会堵塞。可是这次客户端如此决绝,服务器就解堵塞。

recv_data = client_socket.recv()

if recv_data:
   """来欣赏妾身的舞姿"""
else:
   """滚吧,渣男"""
   client_socket.close()
  1. 服务器:好吧,随你(服务器确认接收到客户端的信息,客户端关闭接收)
  2. 服务器:我也不理你了,滚吧,渣男(服务器关闭对他的发送)
  3. 客户端:fine o( ̄︶ ̄)o(客户端关闭接收)
    在这里插入图片描述

a. 客户端调用close(),客户端关闭发送
b. 服务器给客户端回应自己收到了。
c. 服务器的client_socket.recv()还在堵塞。如果加上一个判断其的条件语句,当收到空时,也就是客户端执行了close()操作时,服务器解堵塞。服务器关闭对中国客户端的发送。
e. 客户端知道服务器不给自己发数据时,客户端就关闭了收数据。并且向服务器发送一个确认的包。

  • 但问题是: 客户端怎么知道服务器收到了?答,返回一个确认包。怎么确认对方收到了返回的确认包?对方再返回一个确认确认的确认包…这不就是死循环吗?
  • 解决办法: 规定,谁先调用的最后一次Close,谁就等待一个时间,如果超过这个时间没收到确认包,就再发一个。于是!接收包的这方(在这里是C),回复一个确认包后,要等待两份的这个时间,记做2MSL,如果对方没收到确认包,它需要再次接收一次确认包。
  • 注意: 谁先发起close,谁就要等待。所以要客户端先发。不然服务器强制关闭后,会出现某某端口被占用。(客户端端口不固定换就完事,服务器是固定端口,所以会出问题)
  • 添加这一行代码,就可以在调用close后,立马结束进程
    在这里插入图片描述

3.应用户需求打开页面

  1. 获取用户需求的页面名
  1. 切割发送的请求
  2. 用re模块,获取页面名
  1. 为了防止页面名为空,设定一个主页index.html
  2. 打开相应文件
  • 代码:
    在这里插入图片描述

4.完善代码

  • 有一个小BUG:输入不存在的文件,应该返回404
    在这里插入图片描述

三、并发HTTP服务器

1.多进程实现

在这里插入图片描述
在这里插入图片描述

  • 关于为什么要对客户端套接字调用两次close:

主进程创建套接字后,由于子进程会复制一模一样的资源,所以对这个套接字就会有两个硬链接。
主进程调用close(),并不会把这个套接字释放,只会减少一个硬链接。子进程中的代码执行完时,调用的close(),才会真正四次挥手。

2.多线程实现

  • 对刚才多进程文件

:%s/multiprocessing/threading/g
:%s/Process/Thread/g

  • 删除主进程里的close()

多线程并不存在有两个硬链接的问题,当主进程调用close时,就已经被关闭了。

在这里插入图片描述
在这里插入图片描述

3.多协程实现

在这里插入图片描述
在这里插入图片描述

4.单进程单线程非阻塞

  • 套接字有个.setblocking(False)可以设置为非堵塞。那么哪里会阻塞呢?没有新客户端接入和未收到信息会阻塞。
  • 由于非堵塞时,未接受到一定会报错,所以使用try结构。下面我们用一种取巧的方式验证(没有就输入0)
while True:
    try:
        new_socket = int(input("请输入一个新的套接字"))
        zero_s = 5/new_socket
    except Exception as ret:
        print(">>>>>>>没有新的客户端<<<<<<<")
    else:
        print("-------新客户端-------")
        try:
            recv = int(input("请输入接收的消息"))
            zero_c = 3/recv
        except Exception as ret:
            print("]]]]]]]没有接收的消息[[[[[[[")
        else:
            print("=======新消息=======")
  • 结果验证:

在这里插入图片描述
在这个套接字未接收到消息后,是不会继续服务它的。所以放在外部。可以用个列表存放套接字,在需要时取用。

  • 最终代码:
    在这里插入图片描述
  • 如果发消息过快,会发现消息有可能会在同一行内,说明?

说明电脑有一个缓存区,消息是放在缓存区里的。需要时再取用。进程每隔一段时间来看有没有它的数据,没有的话,要么阻塞要么异常。

5.长链接&短链接

  • HTTP1.0时期是短链接,现在的HTTP1.1是长链接
  • 为了获取一个对象的三个数据可以有两种方式
  • 短链接
  1. 三次握手,接收第一个数据,四次挥手
  2. 三次握手,接收第二个数据,四次挥手
  3. 三次握手,接收第三个数据,四次挥手
  • 长链接:
  1. 三次握手,接收第一个数据
  2. 接收第二个数据
  3. 接收第三个数据,四次挥手

5.2非阻塞实现长链接

  • close关闭服务套接字,那不就相当于短链接了吗?
  • 注释掉close,浏览器就会一直转圈圈。(因为不知道接收完数据了)
  • 可以用Content-Length请求头,并获取文件体长度,只要长度达到了,浏览器就知道接收完数据了。
    在这里插入图片描述
    在这里插入图片描述
  • 代码实现:
    在这里插入图片描述
    在这里插入图片描述

6.epoll

  • epoll是当今Linux中采用的方式。比如gevent底层的实现就是用epoll。
  • nginx和apache都是用的epoll。
  • 这部分了解怎么用就行。
  • epoll是单进程单线程的。
  • 操作系统有内存,应用程序也有内存。两部分内存相互独立。
  • 单线程非阻塞,是在应用程序内部有一个服务套接字列表。每当for循环(轮询)到某一个套接字时,会复制这个对象的fd(文件索引),然后给操作系统内存。操作系统就会查看是否有属于它的数据,没有就异常或阻塞。因为存在复制过程,列表越大,性能就会显著降低。
  • epoll可以让应用程序和操作系统内核共用某一块内存。并且不遍历了,以事件通知的方式。

6.2epoll代码实现

  1. 导入select,创建一个共享内存,并将监听套接字写入
    在这里插入图片描述
  2. 返回触发事件的列表
    在这里插入图片描述
  3. 当新客户端连接时,生成一个新套接字
    在这里插入图片描述
  4. 当服务套接字接收到信息时、断开时
    在这里插入图片描述
  • 最终代码:
    在这里插入图片描述
    在这里插入图片描述

四、网络通信

1.TCP/IP协议

  • tcp-ip协议是一类协议的简称:
    在这里插入图片描述
  • 链路层→网路层→传输层→应用层
  • 应用层: 包括应用自己规定的协议,浏览器之类的还有HTTP协议
  • 传输层: 一个应用可以既走TCP又走UDP,并且端口可以一致。(TCP内部不能存在两个8090端口,UDP同理。但是可以TCP8090端口,一个UDP8090端口)
  • ICMP:比如Ping,判断对方是否在线
  • 原始套接字:可以直接从应用到IP。(传输层会检查IP,若使用此种方式,可以伪造IP)
  • 以浏览器发送消息为例:
  1. 应用层POST /xxx.html HTTP/1.1\r\n\r\nHello World
  2. 传输层在前面加tcp端口、源端口、目的端口
  3. 网络层在前面加源IP、目的IP
  4. 链路层在前后加MAC.地址
  5. 然后层层拆解。不匹配就扔。

1.2另一套标准OSI

在这里插入图片描述

  • OSI只是理论标准

2.Wireshark抓包工具

  • 下面我们来通过抓包工具抓取数据包,然后进行分析
  • 这个工具是在发送接收网络数据,收不到或者发不出时,来判断原因的。
  • 原理是利用原始套接字,穿到操作系统最核心的部分,把所有数据拷贝了一份。
  • 以Windows系统为例(Linux和Mac版安装较复杂)

2.1安装

Wireshark抓包工具

  1. 下载工具,一路Next
  2. 记得勾选(这个工具只是作展示用,而勾选才可以抓包)

2.2抓包

  • 点开一个
  • 红框里点击一个,蓝框里会出现详细信息
  • 黄框里是数据在内存中的表现(看不懂是因为加密了)
    在这里插入图片描述
SourceDestinationProtocalLengthinfo
源IP目的IP协议数据长度简单信息描述

蓝色的为Mac地址在这里插入图片描述

蓝色的为IP地址在这里插入图片描述

蓝色的显示为UDP方式
在这里插入图片描述

蓝色为应用程序的数据在这里插入图片描述

2.3过滤

  • 看图识字吧:
  • 只看TCP在这里插入图片描述
  • 只看目的IP为192.168.1.105在这里插入图片描述
  • ip.src可以指定源IP
  • 可以组合条件,用 and 连接(not和or也能用)
  • udp.port指定UDP端口(同理)

3.电脑通信&网络掩码

1.1子网掩码

  • 两台电脑通过网线连接,需要设置IP地址和网络掩码
  • 子网掩码可以确定网络号是谁,主机号是谁。
  • 把IP地址转换为2进制。然后执行按位与操作。
  • 按位与操作: 都为1 ,才为1,只要有1个0,那么就是0
  • 比如192为1100 0000。255为1111 1111。两者按位与结果为:1100 0000。
  • 也就是说,子网掩码255所在位为网络号,0所在位为主机号。

1.2集线器&交换机

1.2.1集线器Hub
  • 网络里传输的是数据的信号,而不是电流
  • 集线器发出的数据是以广播的形式。容易卡,所以被淘汰了。
  • 扩展坞也是一种Hub。
1.2.2交换机switch
  • 该广播广播,该单播单播
  • 要发送数据,必须得知道Mac地址
  • ARP协议可以根据IP地址找到Mac地址(Windows的cmd下输入arp -a可以查看)
  • ARP查找的过程:
  1. 电脑广播一个数据包
  2. 所有的网卡,默认还接受另一个Mac地址(广播Mac地址):FF.FF.FF.FF.FF.FF
  3. 对应的IP地址会接包,并单播回送一个数据。然后电脑接收到Mac地址,会根据IP储存起来。
1.2.3arp攻击
  1. 假如A要给B发信息。
  2. C主动给A发自己的Mac地址,然后说是B的。给B发自己的Mac地址,然后说是A的。这样A给B发信息,C可以修改、劫持、监听

1.3路由器Router

  • 交换机连接多台电脑进一个网络。路由器连接多个网络。
  • 路由器至少有两个网卡。每个网卡与其对应的交换机处于同一个小网络内。路由器内的多张网卡可以发生数据通信。
  • 一台电脑要给另一个网络的电脑发送消息,需要通过默认网关。默认网关一般就是路由器。
  • 网关: 收到了数据,并把数据发出去的代理,就叫网关。
  • 两个不同网络内的电脑在通信过程中IP地址不发生任何变化,Mac地址会发生变化(通过网关代理)(类似在课堂上两个相距甚远的情侣传纸条)
  • Mac地址仅仅是收发双方的,逐级变化。IP是最终希望通信的两方之间的,所以不会变。IP是一个逻辑上的地址。
  • 路由器之间有一个路由发现协议。可以知道彼此的MAC、IP地址

1.4复杂通信过程

  1. 解析域名
  1. 发送数据给默认网关
  2. 数据转手给其它网关,直到到达DNS服务器网络内
  3. 数据发送给DNS服务器,得到IP地址
  4. 再一路往回传
  1. 向目标服务器发送tcp的三次握手
  2. 发送HTTP请求数据,等待服务器应答
  3. 发送TCP四次挥手
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值