TCP协议三握四挥、socket模块

传输层之TCP与UDP协议

用于应用程序之间的通信

TCP与UDP都是用来规定通信方式的

​ 通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊

​ 随性所欲的聊:文字 图片 视频 小油腻话

​ 遵循一些协议:开头代尊称 首行空两格 只准用官腔 不能打情骂俏

PS:不遵循上述也可以通信 只不过遵循了更合规合法合理!

一、TCP协议(重要)

​ 三次握手建链接

1.TCP协议也称为不可靠协议(数据不容易丢失)

​ 造成数据不容易丢失的原因不是因为有双向通道 而是因为有反馈机制

​ 给对方发小新之后会保留一个副本 直到对方回应消息收到了才会删除 否则会在一定时间内反复发送

2.洪水攻击

​ 同一时间有大量的客户端请求建立链接 会导致服务端一直处于SYN_RCVD状态

3.服务端如何区分客户端建立链接的请求

​ 可以对请求做唯一标识

image

我们先分析一下上图的英文都是啥东西。
SYN:  代表连接请求/连接接受   ACK: 确认   ack: 确认号   seq:序号

客户机下面有三个框里面分别是: CLOSED  SYN-SENT  ESTAB-LISHED
这代表主机的三个状态。
服务器下面有四个框分别是: CLOSED LISTEN   SYN-RCVD  ESTAB-LISHED
这代表服务器的四个状态。 

CLOSED: 表示初始状态
CLOSED大家应该都知道是关闭的意思。
其实这里的CLOSED是关闭上次握手的东西。所以说CLOSED也就是初始化的意思。

LISTEN: 监听状态。表示服务器端的某个SOCKET处于监听状态。
所以说客户机是没有监听状态的,因为客户机是主动的一方。

SYN-SENT(客户机):  
客户端调用连接,发送一个SYN请求建立一个连接,在发送连接请求后等待匹配的连接请求,此时状态为SYN_SENT。
简言之: 就是我(客户机)给你(服务器)发个消息说咱俩连接吧! 等待你确认和我连接的状态。

SYN-RCVD(服务器):
在收到客户机发来的连接请求和自己发送一个可以连接,等待对方对连接请求的确认的状态。
简言之:(服务器)同意和你(客户机)连接,我(服务器)等你(客户机)确认和我(服务器)说话的过程。

ESTAB-LISHED:  连接已经建立,两台机器正在传输数据的状态

4.握手具体流程:

第一次握手:

客户机发送SYN=1, seq=x(随机的序号)  这时客户机进入SYN-SENT状态。
简言之:(客户机)发送一个连接请求,并说我这个报文的序号是x。

第二次握手:
服务器在收到客户机发来的SYN(连接请求)后,进入了SYN-RCVD状态。
服务器并发送:SYN=1(接受连接),ACK=1(确认),ack=x+1(客户机下次需要发送的报文序号) 和seq=y(服务器发送报文序号)
简言之:(服务器)答应连你,确认收到你的消息了。
下次你(客户机)给我发上次发送的报文(x)的后一个(即x+1.因为这个我有了 我要下一个)。我发送报文序号是y。

第三次握手:
客户机发送ACK=1,seq=x+1,ack=y+1
这时候客户机和服务器就开始进入传输数据(ESTAB-LISHED)的状态。

​ 四次挥手断链接

1.四次为啥不能合并为三次

​ 因为中间需要确认消息是否发完(TIME_WAIT)

image

ESTAB-LISHED 连接建立  CLOSED 是关闭连接。

FIN-WAIT-1(客户机): FIN_WAIT_1状态是当客户机在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,
		    此时该客户机即 进入到FIN_WAIT_1状态.这时双方的连接都还没有断。
		    
FIN-WAIT-2(客户机):	当对方(服务器)回应ACK报文后(即对方确认可以断开后),则进入到FIN_WAIT_2状态。
			这时候是半连接状态。即客户机要求关闭连接,客户机不发送数据了
			但服务器暂时还有点数据需要传送给客户机,稍后再关闭连接。
			
TIME_WAIT(客户机): 表示收到了服务器的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。
				  如果FIN_WAIT_1状态下,收到了对方同时带 FIN标志和ACK标志的报文时,
				  可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。  
简言之:  收到了对方(服务器)的断开标志(FIN)并确认(ACK)后,这时说明服务器确认断开连接且没有数据要传了,
		客户机可以直接从FIN-WAIT-1到TIME-WAIT状态。	

CLOSE-WAIT(服务器):  等待客户机发来的连接中断请求 ,服务器收到FIN后,
					就发出ACK以回应FIN请求,服务器进入CLOSE_WAIT状态。 		  

LAST_ACK(服务器): 服务器在发送FIN报文后,最后等待客户机的ACK报文。这个状态就是LAST_ACK。
				 当收到ACK报文后,服务器进入到CLOSED可用状态了。 

2.挥手具体流程:

第一次挥手:  客户机发送FIN=1,seq=u 之后进入了FIN-WAIT-1状态。
简言之: 
客户机发送了一个断开连接的标志(FIN=1)。
并说这个报文的序号是u。接着客户机就进入了等待服务器确认的状态(FIN_WAIT-1)。

第二次挥手: 服务器发送ACK=1,seq=v,ack=u+1 后服务器进入了CLOSE-WAIT状态。客户机进入FIN-WAIT-2状态。
简言之:
服务器说我确认收到你的断开请求了(ACK=1),我发送的报文序号是v,
下次你(客户机)给我(服务器)发送u+1这个序号的报文。
服务器开始处理剩余的一些数据(CLOSE-WAIT)。客户机等服务器处理完剩余的数据给自己发送确认信号(FIN-WAIT-2)

第三次挥手:  服务器发送 FIN=1,ACK=1,seq=w,ack=u+1 随后服务器进入LAST-ACK状态。

简言之:  服务器给客户机发送了一个断开连接的标志(FIN=1),确认收到你的断开请求了(ACK=1),
我的发送的报文序号是w,你(客户机)下次给我(服务器)发送u+1这个序号的报文。
因为在此之前客户机并没有再次的给服务器发送报文。所以服务器给客户机要的报文序号还是u+1。
服务器等待客户机给自己发送确认收到的报文(LAST-ACK)。

第四次挥手:  客户机发送ACK=1,seq=u+1,ack=w+1,服务器随后进入TIME-WAIT状态。
	    	服务器收到后进入CLOSED状态。等2MSL时间后客户机也进入CLOSED状态。
简言之:  
客户机给服务器说我知道你可以断开了(ACK=1),我(客户机)发送的报文的序号是u+1,(服务器)下次给我(客户机)发送的报文序号是w+1。服务器收到客户机的确认后没啥可说的,就关闭连接了。
客户机等了2MSL后服务器没有动静了就也关闭连接了。

​ 3.三次握手和四次挥手也可以看成是小情侣谈恋爱的过程

三次握手:表白在一起

image

四次挥手:决裂要分手

image

二、UDP协议

​ 也称之为数据协议、不可靠协议

​ 早期的QQ使用的是纯生的(没有加任何额外的功能)UDP协议

​ 现在的QQ自己添加了很多技术和功能

​ 使用UDP的原因就是因为很简单 快捷 粗暴 只要指定对方的地址就可以发消息了

TCP我们可以看成是打电话:可靠的传输 TCP传输数据前需要三次握手建立连接

UDP我们可以看成是发短信:只要发了就行 不管对方看不看 不可靠传输 直接发送数据包 不关心对方是否接收成功

应用层

​ 应用层相当于是程序员自己写的应用程序 里面的协议非常的多

​ 常见的有:HTTP、HTTPS、FTP

socket模块

​ 如果我们需要编写基于网络进行数据交互的程序 意味着我们需要自己通过代码来控制我们之前所学习的OSI七层(很繁琐 很复杂 类似于我们自己编写操作系统)

​ socket也叫套接字 套接字就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象 完成两个应用程序之间的数据传输

​ AF_UNIX 基于文件类型的套接字家族(单机)

​ AF_INET 基于网络类型的接字家族(联网)

socket代码简介

import socket

# 1.产生一个socket对象并指定采用的通信版本和协议(TCP)
server = socket.socket() # 括号内不写参数 默认就是TCP协议 famliy=AF_INET基于网络的套接字 type=SOCK_STREAM流式协议即TCP
# 2.绑定一个固定的地址(服务端必备的条)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1为本地回环地址 只有自己的电脑可以访问
# 3.设立半连接池
server.listen(5)
# 4.等待接客
sock, addr = server.accept()  # return sock, addr  三次握手
print(sock, addr) # sock就是双向通道 addr就是客户端地址
# 5.服务客人
data = sock.recv(1024)  # 接收客户端发送过来的消息 1024字节
print(data.decode('utf8'))
sock.send('客人您好 欢迎光临呀~'.encode('utf8'))  # 给客户端发送消息 且消息必须是bytes类型
# 6.关闭双向通道
sock.close()  # 四次挥手
# 7.关闭服务端
server.close()  # 关店了 倒闭了


import socket

# 1.生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务段的地址链接服务端
client.connect(('127.0.0.1', 8080))
# 3.直接给服务端发送消息
client.send('小二 上菜 把你家最好的的呈上来'.encode('utf8'))
# 4.接收服务端发送过来的消息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
client.close()

代码优化

  1. 聊天的内容自定义
    针对消息采用input获取
  2. 让聊天循环起来
    将聊天的部分循环包起来
  3. 用户输入的消息不能为空
    本质其实是两边不能都是recv或者send 一定是一方收一方发
  4. 服务端多次重启可能会报错
    address already in use
    主要是mac电脑会报
    改端口号
#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)   #接收客户端信息
print(ret)              #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)
	
  1. 当客户端异常断开的情况下 如何让服务端继续服务其他人客人
    windows服务端会直接报错
    msc服务端会有一段时间反复接收空消息延迟报错
    异常处理、空消息判断

客户端

import socket

client = socket.socket()
client.connect(('127.0.0.1', 8081))
while True:
    msg = input('请输入您想要发送给服务端的消息>>>:').strip()
    # 把发送的信息设置成自定义
    if len(msg) == 0:
        print('不能发送空消息')
        continue
    client.send(msg.encode('utf8'))
    data = client.recv(1024)
    print('来自于服务端发送过来的消息>>>:', data.decode('utf8'))

服务端

import socket
from socket import SOL_SOCKET, SO_REUSEADDR

server = socket.socket()
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
server.bind(('127.0.0.1', 8081))
server.listen(5)

while True:  # 链接循环
    sock, addr = server.accept()
    while True:  # 通信循环
        try:
            # 当客户端输入为空的时候通过循环跳过后续代码,防止程序报错
            data = sock.recv(1024)
            if len(data) == 0:
                break
            print(f'来自于客户端{addr}的消息>>>:', data.decode('utf8'))
            msg = input('请输入发送给客户端的消息(不能发空消息)>>>:').strip()
            # 返回的信息设置成自定义
            sock.send(msg.encode('utf8'))
        except BaseException:
            # 当客户端直接退出的时候服务端这边会报错,我们用异常处理让程序跳过这些会报错的代码,继续运行
            break


半连接池的概念

server.listen(5)  #半连接池

当有多个客户端来链接的情况下 我们可以设置等待数量(不考虑并发问题)
假设服务端只有一个人的情况下

在测试半连接池的时候 可以不用input获取消息 直接把消息写死即可
posted @   小福福  阅读(58)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
  1. 1 原来你也在这儿 温余福
  2. 2 世间美好和你环环扣扣 温余福
  3. 3 随风起舞 温余福
  4. 4 罪恶都市 温余福
随风起舞 - 温余福
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

作词 : 米果

作曲 : 高橋優

编曲 : 刘胡轶/貢多杰

制作人 : 刘胡轶/吴青峰

配唱制作人 : 刘胡轶

乐器监制 : 刘胡轶

吉他 : 胡晨

贝斯 : 甯子达

弦乐录音棚 : 中国剧院录音棚

录音工程师 : 倪涵文/李游/李杨/邢铜/韩宽/李巍

录音监制 : 倪涵文/李游

混音&母带工作室 : OKmastering studio

混音&母带工程师 : 全相彦

制作协力 : 刘西洋

制作发行 : 智慧大狗 × 天才联盟

出品人 : 张葛

监制 : 崔恕/王明宇

弦乐监制 : 李朋

弦乐 : 国际首席爱乐乐团

鼓(打击乐):祁大为

和音编写&演唱:鱼椒盐

人声&吉他&鼓(打击乐)录音棚:55Tec studio

我曾将青春翻涌成她

我曾将青春翻涌成她

也曾指尖弹出盛夏

心之所动 且就随缘去吧

这一路上走走停停

这一路上走走停停

顺着少年漂流的痕迹

迈出车站的前一刻

竟有些犹豫

不禁笑这近乡情怯

不禁笑这近乡情怯

仍无可避免

而长野的天

依旧那么暖

风吹起了从前

从前初识这世间

从前初识这世间

万般流连

看着天边似在眼前

也甘愿赴汤蹈火去走它一遍

如今走过这世间

如今走过这世间

万般流连

翻过岁月不同侧脸

措不及防闯入你的笑颜

我曾难自拔于世界之大

我曾难自拔于世界之大

也沉溺于其中梦话

不得真假 不做挣扎 不惧笑话

我曾将青春翻涌成她

我曾将青春翻涌成她

也曾指尖弹出盛夏

心之所动 且就随缘去吧

逆着光行走 任风吹雨打

短短的路走走停停

短短的路走走停停

也有了几分的距离

不知抚摸的是故事 还是段心情

也许期待的不过是 与时间为敌

再次看到你

微凉晨光里

笑得很甜蜜

从前初识这世间

从前初识这世间

万般流连

看着天边似在眼前

也甘愿赴汤蹈火去走它一遍

如今走过这世间

如今走过这世间

万般流连

翻过岁月不同侧脸

措不及防闯入你的笑颜

我曾难自拔于世界之大

我曾难自拔于世界之大

也沉溺于其中梦话

不得真假 不做挣扎 不惧笑话

我曾将青春翻涌成她

我曾将青春翻涌成她

也曾指尖弹出盛夏

心之所动 且就随缘去吧

晚风吹起你鬓间的白发

晚风吹起你鬓间的白发

抚平回忆留下的疤

你的眼中 明暗交杂 一笑生花

我仍感叹于世界之大

我仍感叹于世界之大

也沉醉于儿时情话

不剩真假 不做挣扎 无谓笑话

我终将青春还给了她

连同指尖弹出的盛夏

心之所动 就随风去了

以爱之名 你还愿意吗

点击右上角即可分享
微信分享提示