内容概要
- socket
- 通信循环
- 粘包现象
- 小练习(实现文件上传)
socket
socket模块可以实现远程数据交互
架构启动先启动服务端在启动客户端
通讯循环
简易代码
1.服务端
import socket
server = socket.socket() # 默认基于网络的TCP传输协议
server.bind(('127.0.0.1', 9999)) # 绑定ip和端口
server.listen() # 半连接池 设置可以等待的客户端数量
sock, address = server.accept() # 监听 三次握手的listen态
print(address) # 客户端ip
data = sock.recv(1024) # 客户端发送的信息
print(data.decode('utf8'))
sock.send(b'hi hello') # 回复客户端
sock.close() # 结束会话
server.close() # 关闭服务端
2.客户端
import socket
client = socket.socket() # 默认基于网络的TCP传输协议
client.connect(('127.0.0.1', 9999)) # 找到服务端的ip和端口
client.send(b'hello') # 给服务端发送信息
data = client.recv(1024) # 服务端回复的信息
print(data.decode('utf8'))
client.close() # 结束聊天
![image]()
循环交互和优化
优化内容:1.客户端校验消息不能为空
2.服务端添加兼容性代码(mac linux)
3.服务端重启频繁报端口占用错误
4.客户端异常关闭服务端报错的问题(异常捕获)
5.服务端链接循环
6.半连接池(设置可以等待的客户端数量)
服务端
import socket
from socket import SOL_SOCKET, SO_REUSEADDR # 解决服务端重启频繁报端口占用错误
server = socket.socket() # 默认基于网络的TCP传输协议
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加
server.bind(('127.0.0.1', 9999)) # 绑定IP和端口
server.listen() # 半连接池 设置可以等待的客户端数量
while True: # 服务其他客户端
sock, address = server.accept() # 监听 三次握手listen态
print(address) # 客户端ip
while True:
try: # 异常捕获,解决客户端突然终止会话
data = sock.recv(1024) # 客户端发送的信息
print('对方回复: %s' % data.decode('utf8'))
sock.send(data + b'666') # 回复客户端
except ConnectionResetError as e:
print(e)
break
客户端
import socket
client = socket.socket() # 默认基于网络的TCP传输协议
client.connect(('127.0.0.1', 9999)) # 找到服务端IP
while True:
gg = input('输入内容>>>').strip()
if len(gg) == 0:
continue
client.send(gg.encode('utf8')) # 给服务端发信息
data = client.recv(1024) # 服务端的回复
print('对方回复: %s' % data.decode('utf8'))
![image]()
粘包现象
数据管道的数据没有被完全取出
TCP特性:当数据量比较小 且时间间隔比较短的多次数据
那么TCP会自动打包成一个数据包发送
解决粘包问题
制作包头解决
1.服务端
import socket
from socket import SOL_SOCKET, SO_REUSEADDR # 解决服务端重启频繁报端口占用错误
import subprocess
import struct
import json
server = socket.socket() # 默认基于网络的TCP传输协议
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加
server.bind(('127.0.0.1', 9999)) # 绑定IP和端口
server.listen() # 半连接池 设置可以等待的客户端数量
while True: # 服务其他客户端
sock, address = server.accept() # 监听 三次握手listen态
print(address) # 客户端ip
while True:
try: # 异常捕获,解决客户端突然终止会话
data = sock.recv(1024) # 客户端发送的信息
command_cmd = data.decode('utf8')
sub = subprocess.Popen(command_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
res = sub.stdout.read() + sub.stderr.read() # 得到的是二进制
# 1.定义一个字典数据
d = {
'name': '性感荷官在线发牌!!!',
'size': len(res),
'desc': '还不快来!!!'
}
# 2.字典序列化
dict_json = json.dumps(d)
# 3.制作一个字典报头
server_dict = struct.pack('i', len(dict_json))
# 4.发送字典报头
sock.send(server_dict)
# 5.发送字典
sock.send(dict_json.encode('utf8'))
# 6.发送真实数据
sock.send(res)
except ConnectionResetError as e:
print(e)
break
2.客户端
import json
import socket
import struct
client = socket.socket() # 默认基于网络的TCP传输协议
client.connect(('127.0.0.1', 9999)) # 找到服务端IP
while True:
gg = input('输入终端命令>>>').strip()
if len(gg) == 0:
continue
client.send(gg.encode('utf8')) # 给服务端发信息
# 1.接收长度为4的字典报头
client_dict = client.recv(4)
# 2.解析字典报头
data_dict = struct.unpack('i', client_dict)[0]
# 3.接收字典数据
data = client.recv(data_dict)
# 4.解析字典(json格式bytes数据,loads会先解码在反序列化)
real_data = json.loads(data)
print(real_data)
# 5.获得字典中的各项数据
length_data = real_data.get('size')
# 6.接收想要的数据
bytes_data = client.recv(length_data)
print(bytes_data.decode('utf8'))
![image]()
小练习(实现文件上传)
1.服务端
import socket
from socket import SOL_SOCKET, SO_REUSEADDR # 解决服务端重启频繁报端口占用错误
import struct
import json
server = socket.socket() # 默认基于网络的TCP传输协议
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 在bind前加
server.bind(('127.0.0.1', 9999)) # 绑定IP和端口
server.listen() # 半连接池 设置可以等待的客户端数量
while True: # 服务其他客户端
sock, address = server.accept() # 监听 三次握手listen态
while True:
try: # 异常捕获,解决客户端突然终止会话
# 1.接收长度为4的字典报头
server_dict = sock.recv(4)
# 2.解析字典报头
data_dict = struct.unpack('i', server_dict)[0]
# 3.接收字典数据
data = sock.recv(data_dict)
# 4.解析字典(json格式bytes数据,loads会先解码在反序列化)
real_data = json.loads(data)
# 5.获得字典中的各项数据
length_data = real_data.get('size')
file_name = real_data.get('name')
# 6.接收想要的数据
recv_size = 0
with open(file_name, 'wb') as f:
while recv_size < length_data:
index = sock.recv(1024)
recv_size += len(index)
f.write(index)
except ConnectionResetError as e:
print(e)
break
2.客户端
import json
import socket
import struct
import os
client = socket.socket() # 默认基于网络的TCP传输协议
client.connect(('127.0.0.1', 9999)) # 找到服务端IP
while True:
# 1.获取视频文件目录
data_path = r'/Users/mac/Movies/Linux/day06/视频'
# 2.列出视频选项
data_mv = os.listdir(data_path)
for i, j in enumerate(data_mv, 1):
print(i, j)
choice = input('选择要上传的文件>>>').strip()
if len(choice) == 0:
continue
if choice.isdigit():
choice = int(choice)
if choice in range(1, len(data_mv) + 1):
# 获取文件名称
data_name = data_mv[choice - 1]
# 拼接文件名的绝对路径
file_path = os.path.join(data_path, data_name)
# 1.定义一个字典数据
d = {
'name': '性感荷官在线发牌.mp4',
'size': os.path.getsize(file_path),
'desc': '还不快来!!!'
}
# 2.字典序列化
dict_json = json.dumps(d)
# 3.制作一个字典报头
client_dict = struct.pack('i', len(dict_json))
# 4.发送字典报头
client.send(client_dict)
# 5.发送字典
client.send(dict_json.encode('utf8'))
# 6.发送真实数据(需要读取文件)
with open(file_path, 'rb') as f:
# 一行一行读取
for line in f:
# 一行一行发送
client.send(line)
![image]()
![image]()