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

时间:2021-02-25 23:56:18

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', 8080))  # 拨号,写服务端的IP  (俩括号)
phone.send(b'hello')              #先发送hello
phone.send(b'word')               #紧接着在发送word while 1:
    server_data = phone.recv(1024)    #接收服务端的信息,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', 8080))  # 绑定电话卡,写自己的IP  (俩括号)
phone.listen(5)   #开机   5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
conn,addr = phone.accept()  #等待接电话 phone.accept()  conn和addr分别接收phone..accept()生成的两个参数
print(addr)  #打印连接进来的客户端
while 1 :
    client_data = conn.recv(1024)   #接受的字节数
    print(client_data)
    conn.send(client_data.upper()) #返回的信息
conn.close()
phone.close()


('127.0.0.1', 53961)      #服务端接收客户端的信息连在了一起
b'helloword'

 

客户端

import socket
import time
phone = socket.socket()
phone.connect(('127.0.0.1', 8080))  # 拨号,写服务端的IP  (俩括号)
phone.send(b'hello')
time.sleep(0.1)                #等待0.1秒在发送word
phone.send(b'word')
while 1:
    server_data = phone.recv(1024)    #接收服务端的信息,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', 8080))  # 绑定电话卡,写自己的IP  (俩括号)
phone.listen(5)   #开机   5不是限制连接数,而是同一时刻只接受5个请求,可以不写,不写为默认,默认可以开启N个
#运行后停在这里,下边不执行
conn,addr = phone.accept()  #等待接电话 phone.accept()  conn和addr分别接收phone..accept()生成的两个参数
print(addr)  #打印连接进来的客户端
while 1 :
    client_data = conn.recv(1024)   #接受的字节数
    print(client_data)
    conn.send(client_data.upper()) #返回的信息
conn.close()
phone.close()
 
('127.0.0.1', 53985)             #接受的字符分开了
b'hello'
b'word'

解决粘包问题

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

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

import struct
ret = struct.pack('i',151923212)    #将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字节
(151923212,)

解决粘包问题代码

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()