web2:3次握手4次挥手及py返回浏览器需要的HTTP页面

3次握手4次挥手概述
返回浏览器需要的HTTP页面

3次握手4次挥手概述

1.回忆关于udp和tcp相应的部分知识点
2.三次握手
3.四次挥手

1.回忆关于udp和tcp相应的部分知识点
  • udp : 不稳定 面向无连接。 且主机发送一条数据,本身并不知道对方有没有收到
  • tcp: 稳定 面向有连接。 tcp是操作系统和操作系统之间打招呼,发送一条数据给对方,本身是知道对方有没有收到的

问题在于 这些特点的原理是什么?
原因是:
udp直接发送
tcp先要经过3次握手才会发送。

2.三次握手(打开连接,准备资源)
  • 3次握手(connect)的目的是让收发双方准备资源,保证收发不会丢失数据包

syn标记请求的数据
ack标记应答的数据

如何实现:
  • 第一次握手 客户端发送数据给服务器端(目的让服务器端准备好资源)属于syn

  • 第二次握手 服务器端做了两件事。
    1.给客户端回送数据(让客户端知道我服务器端准备好了)
    2.发送数据让客户端准备好数据--------->这里connect解堵塞
    1属于ack,回应上一步客户端发的syn
    2属于syn 发送给客户端数据
    这里是两步合在一起了

  • 第三次握手 客户端给服务器端发送数据(让服务器端知道我客户端准备好了)属于ack,回应上一步服务器端发的syn

得到结论:udp不稳定,数据可能丢失,因为udp属于直接发送,没有回应并不知道对方有没有接收到。比如断线了,系统故障了,导致没有发出去的话,并不知道对方有没有接收到。
tcp稳定,数据不太可能丢失,tcp在连接的时候是操作系统之间会有通信,因为有syn就有ack回应,当收到回应就明白对方已经接收到。
3.四次挥手(断开连接,释放资源)

先明白:套接字有两个通道, 一个收道 一个发道 关闭两个才是真的关闭

如何实现:
  • 第一次挥手客户端调用tcp_socket.close() 客户端告诉服务器不会再给服务器发送任何数据(自己关闭发道)属于syn

  • 第二,三次挥手 服务器做了两件事情
    第二次挥手:1.给客户端发送数据说我收到了
    第三次挥手:2.服务器端调用new_socket.close(),给客户端发送数据表示不会给你发数据,让客户端关收道。准备一个超时时间。
    1属于ack,回应上一步客户端发的syn
    2属于syn 发送给客户端数据
    这里是跟3次握手有区别,两步不能合在一起

  • 第四次挥手
    让双方把准备的资源都释放了属于ack,回应上一步服务器端发的syn

两个问题
  • 1.为什么客户端先调close()

原因在于最后,如何确认两边都收到了信息,可以在一个时间段都释放掉资源。
想象一个循环:
客户端给服务器端发送数据syn表示想结束聊天了,
服务器端为了让客户端知道自己收到了给客户端发送一个回应ack,
但是服务器端不知道客户端是否收到,你敢关闭资源吗?
比如说因为网络问题,客户端没有收到。你关闭了资源。
得,客户端停了老半天,以为你没收到,又发信息,结果你关闭资源了,这不坑人嘛。

所以客户端也得回服务器端一个消息表示自己收到了,然后问题来了,客户端没收到回应敢关资源吗?万一服务器端也突然断下网呢,
同样的道理,服务器端又发送,哦 我收到了。 客户端, 哦 我收到了 ,服务器端,客户端…
就这样陷入死循环了。。。

所以怎么解决这个问题?

谁先调close() 在收到对方传来的syn的时候,谁的资源就会保存2-5分钟。

客户端先调用资源,
所以在第三次挥手服务器端调用close()发送一个syn的时候,当客户端收到这个syn请求数据,会执行第四次挥手回应,
但服务器端怎么知道客户端收到了呢,
这时候服务器端会设置一个超时时间,比如4s,超过这个时间4s,我就再发,超过再发,直到接收到回应就关闭资源。
然后客户端在收到这个syn请求的时候发送一个ack,然后停留2-5分钟后再关闭资源。

谁最后调用close会有超时时间
如果超时了的话,会再次发送数据

服务器端是绑定的固定的ip和端口,你不释放资源,停留2分钟及以上,你还工不工作了。
客户端则不同,我端口是随时可以变的啊,我一个端口不释放资源停留,我换一个就好了。

所以这就是为什么客户端先调close()
  • 2.二次挥手和三次挥手为什么不像3次握手一样中间合在一起。

因为第三次挥手是调用close(),我不调用也可以啊,我不调用,你能合在一起使用吗,肯定不行啊。


返回浏览器需要的HTTP页面

1.创建套接字
2.绑定端口
3.转为接听模式
4.客户端连接
5.接收数据
6.发送数据
7.关闭套接字

1–4过程

	# 打开套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    # 绑定端口
    localaddr = ("",8080)
    tcp_server_socket.bind(localaddr)

    # 转为接听模式
    tcp_server_socket.listen(128)

    # 客户端连接
    new_socket,client_addr = tcp_server_socket.accept()

5过程中接收到的数据是

GET /1.html HTTP/1.1
Host: 192.168.79.1:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1551868678
Upgrade-Insecure-Requests: 1

利用正则表达式将 /1.html 提取出来
先用splitlines将接收到的代码切割成很多部分

['GET /1.html HTTP/1.1', 'Host: 192.168.79.1:8080', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', 'Accept-Encoding: gzip, deflate', 'Connection: keep-alive', 'Cookie: Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1551868678', 'Upgrade-Insecure-Requests: 1', '']

再用正则match将第一组内的 /1.html 提取

ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
    if ret:
        html_name = ret.group(1)
        print (html_name)
然后发送数据分为两部分

第一部分
打开本地的/1.html文件的代码 复制下来用一个html_content来存储

第二部分
用一个response来存储"HTTP /1.1 200 OK\r\n"
response +="\r\n"

注意html是二进制,response是字符串
所以应该分别发送:

new_socket.send(response.encode("utf-8"))
new_socket.send(html_content)
总代码:
import socket
import threading
import re

def client_socket(new_socket):
    recv_data = new_socket.recv(1024).decode("utf-8")
    print (recv_data)
    request_lines = recv_data.splitlines()
    print(">"*20)
    print (request_lines)
    # 用正则表达式把recv_data的值提取出来
    ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
    if ret:
        html_name = ret.group(1)
        print (html_name)

    response = "HTTP/1.1 200 OK\r\n"
    response += "\r\n"

    try:
        f = open("." + html_name,"rb")
    except:
        response = "HTTP/1.1 404 NOT found\r\n"
        response += "\r\n"
        response += "-----file not found----"
        new_socket.send(response.encode("utf-8"))
    else:
        html_content = f.read()
        f.close()
        new_socket.send(response.encode("utf-8"))
        new_socket.send(html_content)

    new_socket.close()

def main():
    # 打开套接字
    tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

    # 绑定端口
    localaddr = ("",8080)
    tcp_server_socket.bind(localaddr)

    # 转为接听模式
    tcp_server_socket.listen(128)

    # 客户端连接
    new_socket,client_addr = tcp_server_socket.accept()

    # 打开一个线程给客户端
    t1 = threading.Thread(target=client_socket,args=(new_socket,))
    t1.start()


if __name__ == '__main__':
    main()

先执行py程序,然后打开网页进行连接就可以了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值