python笔记8 socket(TCP) subprocess模块 粘包现象 struct模块 基于UDP的套接字协议

时间:2023-03-09 06:47:37
python笔记8  socket(TCP) subprocess模块 粘包现象 struct模块   基于UDP的套接字协议

socket

基于tcp协议socket

服务端

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 买电话
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone.bind(('127.0.0.1', 8080)) # 买电话卡 phone.listen(5) # 开机。
# 5 不是链接数,链接可以产生N个,同一时刻只能监听5个请求。 conn, addr = phone.accept() # 等待接电话 # 阻塞状态
# print(222)
print(conn, addr) # conn 代表的是socket通信的对象,一个管道 client_data = conn.recv(1024) # 交流过程
print(client_data)
conn.send(client_data.upper()) conn.close()
phone.close()

客户端

import socket

phone = socket.socket()  # 买电话

phone.connect(('127.0.0.1', 8080))  # 拨号

msg = input('>>>').strip()
phone.send(msg.encode('utf-8'))
server_data = phone.recv(1024) # 限制的是最大接收字节数。
print(server_data)
phone.close()

服务端与客户端循环 聊

import socket
import subprocess
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话
phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号)
phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
while 1:
conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数
print(addr) #打印连接进来的客户端
while 1:
try:
client_data = conn.recv(1024) #接受的字节数
c = client_data.decode('gbk')
obj = subprocess.Popen(c,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
RET = obj.stdout.read()
RET1 = obj.stderr.read()
print(RET.decode('gbk'))
conn.send(RET+RET1) #返回的信息
except Exception:
break
conn.close()
phone.close()

客户端

import socket
phone = socket.socket()
phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号)
while 1:
fasong = input('>>>')
if fasong.upper() == 'Q': break
elif not fasong :continue #如果发送为空,则跳过本次循环 not False3 = True
phone.send(fasong.encode('gbk')) #刚给服务端发信息
server_data = phone.recv(1024) #接收服务端的信息,1024限制的是最大接受的字节数
print(server_data.decode('gbk'))
phone.close()

subprocess模块

subprocess模块用于接收shell界面执行的命令并返回结果

import subprocess
obj = subprocess.Popen('ip a', #输入命令
shell=True, #shell为Tree
stdout=subprocess.PIPE, #stdout接收正确
stderr=subprocess.PIPE) #stderr接收错误返回
RET = obj.stdout.read()
print(RET.decode('utf-8'))

粘包现象

tcp协议

1. 流式协议.数据全部都像水一样连在一起,一次性发送多字节(全部在客户端操作系统的缓存区),客户端每次只取1024字节,剩余字节在缓存区等待下次取

client_data = conn.recv(1024)    
server_data = phone.recv(1024)    

python笔记8  socket(TCP) subprocess模块 粘包现象 struct模块   基于UDP的套接字协议

2.针对于(客户端/服务端)发送的连续的小的数据,对方会一次性接收.

客户端

import socket
phone = socket.socket()
phone.connect(('127.0.0.1', )) # 拨号,写服务端的IP (俩括号)
phone.send(b'hello') #先发送hello
phone.send(b'word') #紧接着在发送word
while :
server_data = phone.recv() #接收服务端的信息,1024限制的是最大接受的字节数
print(server_data.decode('gbk'))
phone.close()

服务端

import socket
import subprocess
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话
phone.bind(('127.0.0.1', )) # 绑定电话卡,写自己的IP (俩括号)
phone.listen() #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数
print(addr) #打印连接进来的客户端
while :
client_data = conn.recv() #接受的字节数
print(client_data)
conn.send(client_data.upper()) #返回的信息
conn.close()
phone.close() ('127.0.0.1', ) #服务端接收客户端的信息连在了一起
b'helloword'

客户端

import socket
import time
phone = socket.socket()
phone.connect(('127.0.0.1', )) # 拨号,写服务端的IP (俩括号)
phone.send(b'hello')
time.sleep(0.1) #等待0.1秒在发送word
phone.send(b'word')
while :
server_data = phone.recv() #接收服务端的信息,1024限制的是最大接受的字节数
print(server_data.decode('gbk'))
phone.close()

服务端

import socket
import subprocess
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话
phone.bind(('127.0.0.1', )) # 绑定电话卡,写自己的IP (俩括号)
phone.listen() #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数
print(addr) #打印连接进来的客户端
while :
client_data = conn.recv() #接受的字节数
print(client_data)
conn.send(client_data.upper()) #返回的信息
conn.close()
phone.close() ('127.0.0.1', ) #接受的字符分开了
b'hello'
b'word'

解决粘包问题

发送固定头部,固定头部包含(数据的总大小) + 数据

struct模块  将一个数据,int 转化为固定的bytes

import struct
ret = struct.pack('i',) #将151923212 转换为固定的bytes类型
print(ret,type(ret) ,len(ret),sep='\n') #sep='\n',将分隔符换为\n ret1 = struct.unpack('i',ret)
print(ret1) b'\x0c*\x0e\t'
<class 'bytes'>
4 #转为固定的4字节
(,)

解决粘包问题代码

low版

一旦数据传入过大则struct模块报错, struct模块不能转译 较长的字符串

服务端:

将数据大小通过struct模块转为固定大小发给客户端

import socket
import subprocess
import struct
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话
phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号)
phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数
print(addr) #打印连接进来的客户端
while 1 : #以下为粘包解决方法
try:
client_data = conn.recv(1024) # 接受的字节数
obj = subprocess.Popen(client_data.decode('utf-8'), #将接收的字节在本地shell执行 并返回结果
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,)
ret = obj.stdout.read() #正确结果
ret1 = obj.stderr.read() #错误结果
ss = len(ret1 + ret) #算返回结果的总长度
ret2 = struct.pack('i',ss) #通过struct将算返回结果的总长度变为固定长度的bytes类型
conn.send(ret2) #发送报头
conn.send(ret) #发送正确结果
conn.send(ret1) #发送错误结果
except Exception:
break
conn.close()
phone.close()

客户端:

客户端根据服务端发来的内容大小取内容

import socket
import time
import struct phone = socket.socket()
phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号)
while 1:
msg = input('>>>').strip() #输入发往服务端的内容,如果是q退出,如果是空从新输入
if msg.upper() == 'Q':
break
elif not msg: #not + 空就是 True 执行continue 重新输入
continue
phone.send(msg.encode('utf-8')) #往服务端发送内容
head = phone.recv(4) #接收报头 报头大小为固定4字节
head_size = struct.unpack('i', head)[0] #直接将报头反解为服务端发送内容的长度,返回是元组,取第一个值
datas = 0    #定义一个datas大小为0
res = b'' #定义一个空的bytes类型的变量
while datas < head_size: #如果datas小于发送的内容的总长度为真
data = phone.recv(1024) #取1024字节
res = res + data #将取出的内容追加到res里
datas += len(data) #datas加上取出内容字节的大小
print(res.decode('gbk')) #读出res里的内容 phone.close()
uper版

服务端:

将服务端发送数据大小写到字典,

将字典转为json,

再将json转为字符串,

取字符串长度给struct模块转为固定大小

将固定大小,bytes类型的字典以及所有数据传给客户端

import socket
import subprocess
import struct
import json
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 买电话
phone.bind(('127.0.0.1', 8080)) # 绑定电话卡,写自己的IP (俩括号)
phone.listen(5) #开机 5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
conn,addr = phone.accept() #等待接电话 phone.accept() conn和addr分别接收phone..accept()生成的两个参数
print(addr) #打印连接进来的客户端
while 1 :
try:
client_data = conn.recv(1024) # 接受的字节数
obj = subprocess.Popen(client_data.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,)
ret = obj.stdout.read()
ret1 = obj.stderr.read()
ss = len(ret1 + ret)
head_dict = { #定义一个字典,total_size的值为发送数据的大小,字典里可定义多个键值传递多种信息
'total_size':ss
}
head_json = json.dumps(head_dict) #将字典转为json格式
head_bytes = head_json.encode('utf-8') #再将json格式转为bytes格式
ret2 = struct.pack('i',len(head_bytes)) #将转为bytes格式的长度通过struct转为固定字节 conn.send(ret2) #发送固定字节
conn.send(head_bytes) #发送bytes类型的字典
conn.send(ret) #发送内容
conn.send(ret1)
except Exception:
break
conn.close()
phone.close()

客户端:

将服务端发送的头部大小获取字典大小

通过字典大小获取内容大小

通过内容大小获取内容

import socket
import json
import struct phone = socket.socket()
phone.connect(('127.0.0.1', 8080)) # 拨号,写服务端的IP (俩括号)
while 1:
msg = input('>>>').strip()
if msg.upper() == 'Q':
break
elif not msg:
continue
phone.send(msg.encode('utf-8'))
he = struct.unpack('i', phone.recv(4))[0] #将头部的大小(phone.recv(4))通过struct模块解析出字典bytes类型的大小
head_dic_bytes = phone.recv(he) #通过解析出字典bytes类型的大小获取bytes类型字典的数据
head_json = head_dic_bytes.decode('utf-8') #将bytes数据反解为json类型
head_doc = json.loads(head_json) #反解json获得字典
datas = 0
res = b''
while datas < head_doc['total_size']: #将字典total_size键对应的内容大小的值取出,获得内容
data = phone.recv(1024)
res = res + data
datas += len(data)
print(res.decode('gbk')) phone.close()

基于UDP的套接字协议

服务端

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM) # (type=socket.SOCK_DGRAM)基于UDP的套接字TCP为socket.AF_INET, socket.SOCK_STREAM udp_sk.bind(('127.0.0.1', 10000)) #绑定服务器套接字 while 1:
msg, addr = udp_sk.recvfrom(1024) #tcp里的accept()里的recv() 是阻塞的 这里的recvfrom(1024)是非阻塞的
print(msg.decode('utf-8')) #打印内容
udp_sk.sendto(b'hi',addr) #返回给客户端的内容

客户端

import socket
ip = ('127.0.0.1',10000) #创建个IP
udp_sk=socket.socket(type=socket.SOCK_DGRAM) #开启udp的socket
while 1:
sd = input('>>>').encode('utf-8')
udp_sk.sendto(sd,ip) #给服务端发送内容
back_msg,addr=udp_sk.recvfrom(1024) #获取服务端回复的内容
print(back_msg.decode('utf-8'),addr) #打印服务端回复的内容
udp_sk.close()