一:TCP和UDP协议
1.tcp协议
1.三次握手
- 1>客户端向服务端发送请求,想要建立一条客户端到服务端的传输数据通道
- 2>服务端向客户端发送回复,同意连接请求,并且同时发送了想要建立从服务端到客户端传输数据通道的请求
- 3>客户端向服务端发送回复,同意连接请求
2.四次挥手
- 1>客户端向服务端发送请求,想要断开客户端到服务端的数据传输的通道
- 2>服务端向客户端发送回复,同意断开请求
- 3>服务端向客户端发送请求,想要断开服务端到客户端的数据传输通道
- 4>客户端向服务端发送回复,同意断开请求
二:socket套接字
1.最基本的服务端:
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8090))
server.listen(5)
sock, addr = server.accept()
sock.send(b'I am server')
data = sock.recv(1024)
print(data)
sock.close()
server.close()
2.最基本的客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8090))
data = client.recv(1024)
print(data)
client.send(b'I am client ')
client.close()
3.能够简单实现循环功能的服务端
import socket
server = socket.socket()
server.bind(('127.0.0.1', 8090))
server.listen(5)
while True:
sock, addr = server.accept()
while True:
msg = input('请输入你想要说的话').strip()
sock.send(msg.encode('utf8'))
data = sock.recv(1024)
print(data.decode('utf8'))
4.能够简单实现循环功能的客户端
import socket
client = socket.socket()
client.connect(('127.0.0.1', 8090))
while True:
data = client.recv(1024)
print(data.decode('utf8'))
msg = input('请输入你想说的话').strip()
client.send(msg.encode('utf8'))
三:半连接池
server.listen(5)
1.语言描述什么是半连接池?
- 所谓的半连接池就相当于是等待服务的固定名额,当名额空出来后,便可以立即添加新的进去等待服务
四:粘包问题
1.出现粘包问题的原因?
- 1.TCP特性
流式协议:所有的数据类似于水流 连接在一起的
例如:当数据量很小 并且时间间隔很多 那么就会自动组织到一起,出现粘包问题 - 2.recv接收出造成的
我们不知道即将要接收的数据量多大 如果知道的话不会产生也不会产生黏包
2.解决粘包问题的方法
-
1.方法:用struct模块无论数据长度是多少 都可以帮你打包成固定长度 然后基于该固定长度 还可以反向解析出真实长度
-
2.思路:
-
服务端
1.先将真实数据的长度制作成固定长度 4
2.先发送固定长度的报头
3.再发送真实数据 -
客户端
1.先接收固定长度的报头 4
2.再根据报头解压出真实长度
3.根据真实长度接收即可
-
-
3.实际案例
-
服务端
import socket
import os
import struct
import json
server = socket.socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)
while True:
sock, address = server.accept()
while True:
file_dict = {
'file_name': '葫芦娃.txt',
'file_size': os.path.getsize(r'../葫芦娃.txt'),
'file_desc': '大战蛇妖',
}
# 2.将字典打包成固定长度的数据
dict_json = json.dumps(file_dict)
file_bytes_dict = dict_json.encode('utf8')
dict_len = struct.pack('i', len(file_bytes_dict))
# 3.发送固定长度的字典报头
sock.send(dict_len)
# 4.发送真实字典数据
sock.send(dict_json.encode('utf8'))
# 5.发送真实数据
with open(r'../葫芦娃.txt', 'rb') as f:
for line in f:
sock.send(line)
break
- 客户端
import socket
import struct
import json
client = socket.socket()
client.connect(('127.0.0.1', 8888))
while True:
# 1.先接收长度为4的报头数据
header_len = client.recv(4)
# 2.根据报头解包出字典的长度
dict_len = struct.unpack('i', header_len)[0]
# 3.直接接收字典数据
dict_data = client.recv(dict_len)
# 4.解码并反序列化出字典
real_dict = json.loads(dict_data)
print(real_dict)
# 5.从数据字典中获取真实数据的各项信息
total_size = real_dict.get('file_size')
file_size = 0
with open(r'%s' % real_dict.get('file_name'), 'wb') as f:
while file_size < total_size:
data = client.recv(1024)
f.write(data)
file_size += len(data)
print('文件接收完毕')
break