socket使用(recv踩坑)

想要写一个client用户端和一个server服务器端,实现客户端向服务器端发送图片文件,服务器端进行处理,并返回处理结果

client.py

import socket
from tqdm import tqdm
import os
import time

if __name__ == "__main__":
    # 传输数据分隔符,减少连接次数
    SEPARATOR = "<SEPARATOR>"
    # 服务器信息
    host = '127.0.0.1'
    port = 5001

    # 客户端信息
    c_host = '127.0.0.1'
    c_port = 5002

    # 文件传输的缓存区
    BUFFERSIZE = 4096

    # 传输文件名,文件路径
    path = r"I:\map\twoMethod\figure"
    filename = "0061_Martigues_As_Surveyed_2022-Model.png"
    # 文件大小
    filesize = os.path.getsize(os.path.join(path,filename))
    # 接受回复文件存储位置
    replyPath = "temp/"

    # 创建socket连接
    s = socket.socket()
    s.bind((c_host,c_port))
    # 连接服务器
    print(f"服务器连接中{host}:{port}...")
    s.connect((host,port))
    print("服务器连接成功...")

    # 发送的文件名和文件大小,必须encode()
    s.send(f"{filename}{SEPARATOR}{filesize}".encode())
    # 文件传输
    sendProgress = tqdm(range(filesize),f"发送{filename}",unit="B",unit_divisor=1024,unit_scale=True) # 传输单位B,换算关系1024
    with open(os.path.join(path,filename),"rb") as f: # 可以自动关闭f,不想f = open()这种需要额外f.close()
        for _ in sendProgress:
            # 读取文件
            bytesData = f.read(BUFFERSIZE)
            if not bytesData: # 五读取数据
                break
            # sendall确保即使网络忙碌的时候,数据仍可以传输
            s.sendall(bytesData)
            sendProgress.update(len(bytesData)) # 更新进度条


    print(f"服务器连接中{host}:{port}...")
    print("开始监听服务器端数据...")

    s.shutdown(socket.SHUT_WR)

    # # connection, address = s.accept()
    receivedFlag = False
    with open(os.path.join(replyPath,"output.xlsx"),'wb') as f:
        while(1):
            bytesData = s.recv(BUFFERSIZE)
            if bytesData:
                print("receive!")
                f.write(bytesData)
                receivedFlag = True
            if (not bytesData) and receivedFlag:
                break
    # connection.close()
    s.close()

client端发送数据到服务器端口之后开始监听服务器端的回复

server.py

import os
from tqdm import tqdm
import socket
import cv2
import detect
from config import config
import time


# 设置服务器地址
SERVER_HOST = config.SERVER_HOST
SERVER_PORT = config.SERVER_PORT

# 设置读写缓冲区
BUFFERSIZE = 4096

# 定义分隔符
SEPARATOR = "<SEPARATOR>"

# 创建Server
s = socket.socket()
s.bind((SERVER_HOST,SERVER_PORT)) # 把IP和PORT绑定
# 设定连接监听数量
s.listen(5)
print(f"服务器端开始监听{SERVER_HOST}:{SERVER_PORT}...")

# 存储地址
savePath = "temp/"
while True:
    # 接受客户端连接
    connection,address = s.accept()
    # 打印客户端IP
    print(f"客户端{address}连接")
    # 接受客户端信息
    received = connection.recv(BUFFERSIZE).decode()
    filename,filesize = received.split(SEPARATOR)
    filename = os.path.basename(filename) # 如果传过来是路径,那么只提取文件名
    filesize = int(filesize) # 传输过来的是字符串转int类型

    # 文件的接受处理
    receive_timestamp = time.time()
    progress = tqdm(range(filesize),f"接受{filename}",unit="B",unit_divisor=1024,unit_scale=True)

    with open(os.path.join(savePath,filename),"wb") as f:
        for _ in progress:
            print("server receive")
            # 从客户端读取数据
            bytesData = connection.recv(BUFFERSIZE)
            # 如果没有数据传输内容终止读取
            if not bytesData:
                break
            # 读取写入
            f.write(bytesData)
            # 更新进度条
            progress.update(len(bytesData))

    # 读取刚刚收到的文件
    print("处理接收到的图片...")
    img = cv2.imread(os.path.join(savePath,filename))
    replyBytes = detect.shelfDetect(img) # 回复的文件数据

    # 向客户端推送结果
    print(f"向客户端{address}回复结果文件{len(replyBytes)}...")
    progress1 = tqdm(range(len(replyBytes)),f"回复{filename}的计算结果",unit="B",unit_divisor=1024,unit_scale=True)
    for _ in progress1:
        print("send")
        bytesData = replyBytes[:BUFFERSIZE]
        replyBytes = replyBytes[BUFFERSIZE:]
        if not bytesData:
            break
        connection.sendall(bytesData)
        progress1.update(len(bytesData))

    # 关闭资源
    print("推送完毕!")
    connection.close() # 先关客户端
# s.close() # 再关服务端

服务器端接受完数据之后,处理数据并将结果以字节的形式推送到client去

这样写完了之后就出现了一个问题
server端卡在了bytesData = connection.recv(BUFFERSIZE)上网查找这个recv方法是阻塞的,如果收不到数据就会一直卡在那里等待接收,进入不了后面的程序,也就处理不了接收到的图片,client也会一直监听等待回复。
在这里插入图片描述
在这里插入图片描述
卡在了recieve的过程中,但是实际上这个时候已经完全接受完了数据

所以这里在client 里面加上s.shutdown(socket.SHUT_WR)意思就是在传完图片之后,把输出通道关闭,那么server端就知道不用再等待recv了
在这里插入图片描述

client.py
在这里插入图片描述
这样在发完之后client主动关闭了发送端口,server也不会再等待接受了,直接就进入了处理图片后面的程序
在这里插入图片描述
client端也能正常接收到数据
在这里插入图片描述

刚刚发现tqdm这里也有一些问题
在这里插入图片描述
如果直接for _ in progress的话,filesize=4096会便利4096次,而你每次接受4096字节的话,其实接受一次就可以了,所以第一次recv接收到数据之后,还会接着循环进入recv的方法,所以卡住了,那么这里其实控制接受的次数就可以了

在这里插入图片描述
这样的话再client端即便不主动关闭传输通道,for循环也会结束,数据传输完成之后会自动结束循环,不会再recv也不会在等待了

改之后的

client.py

import socket
from tqdm import tqdm
import os
import time

if __name__ == "__main__":
    # 传输数据分隔符,减少连接次数
    SEPARATOR = "<SEPARATOR>"
    # 服务器信息
    host = '127.0.0.1'
    port = 5001

    # 客户端信息
    c_host = '127.0.0.1'
    c_port = 5002

    # 文件传输的缓存区
    BUFFERSIZE = 4096

    # 传输文件名,文件路径
    path = r"I:\map\twoMethod\figure"
    filename = "0061_Martigues_As_Surveyed_2022-Model.png"
    # 文件大小
    filesize = os.path.getsize(os.path.join(path,filename))
    # 接受回复文件存储位置
    replyPath = "temp/"

    # 创建socket连接
    s = socket.socket()
    s.bind((c_host,c_port))
    # 连接服务器
    print(f"服务器连接中{host}:{port}...")
    s.connect((host,port))
    print("服务器连接成功...")

    # 发送的文件名和文件大小,必须encode()
    s.send(f"{filename}{SEPARATOR}{filesize}".encode())
    # 文件传输
    sendProgress = tqdm(range(filesize),f"发送{filename}",unit="B",unit_divisor=1024,unit_scale=True) # 传输单位B,换算关系1024
    with open(os.path.join(path,filename),"rb") as f: # 可以自动关闭f,不想f = open()这种需要额外f.close()
        for _ in sendProgress:
            # 读取文件
            bytesData = f.read(BUFFERSIZE)
            if not bytesData: # 五读取数据
                break
            # sendall确保即使网络忙碌的时候,数据仍可以传输
            s.sendall(bytesData)
            sendProgress.update(len(bytesData)) # 更新进度条


    print(f"服务器连接中{host}:{port}...")
    print("开始监听服务器端数据...")

    # s.shutdown(socket.SHUT_WR)

    with open(os.path.join(replyPath,"output.xlsx"),'wb') as f:
        while(1):
            bytesData = s.recv(BUFFERSIZE)
            if bytesData:
                f.write(bytesData)
                receivedFlag = True
            if not bytesData:
                break
    print("接受完毕!")
    s.close()
    # s.shutdown(socket.SHUT_RDWR)



server.py

import os
from tqdm import tqdm
import socket
import cv2
import detect
from config import config
import time
import math


# 设置服务器地址
SERVER_HOST = config.SERVER_HOST
SERVER_PORT = config.SERVER_PORT

# 设置读写缓冲区
BUFFERSIZE = 4096

# 定义分隔符
SEPARATOR = "<SEPARATOR>"

# 创建Server
s = socket.socket()
s.bind((SERVER_HOST,SERVER_PORT)) # 把IP和PORT绑定
# 设定连接监听数量
s.listen(5)
print(f"服务器端开始监听{SERVER_HOST}:{SERVER_PORT}...")

# 存储地址
savePath = "temp/"
while True:
    # 接受客户端连接
    connection,address = s.accept()
    # 打印客户端IP
    print(f"客户端{address}连接")
    # 接受客户端信息
    received = connection.recv(BUFFERSIZE).decode()
    filename,filesize = received.split(SEPARATOR)
    filename = os.path.basename(filename) # 如果传过来是路径,那么只提取文件名
    filesize = int(filesize) # 传输过来的是字符串转int类型

    # 文件的接受处理
    receive_timestamp = time.time()
    progress = tqdm(range(filesize),f"接受{filename}",unit="B",unit_divisor=1024,unit_scale=True)

    with open(os.path.join(savePath,filename),"wb") as f:
        for _ in range(math.ceil(filesize/BUFFERSIZE)):
            # 从客户端读取数据
            bytesData = connection.recv(BUFFERSIZE)
            # 如果没有数据传输内容终止读取
            if not bytesData:
                break
            # 读取写入
            f.write(bytesData)
            # 更新进度条
            progress.update(len(bytesData))

    # 读取刚刚收到的文件
    print("处理接收到的图片...")
    img = cv2.imread(os.path.join(savePath,filename))
    replyBytes = detect.shelfDetect(img) # 回复的文件数据

    # 向客户端推送结果
    print(f"向客户端{address}回复结果文件{len(replyBytes)}...")
    progress1 = tqdm(range(len(replyBytes)),f"回复{filename}的计算结果",unit="B",unit_divisor=1024,unit_scale=True)
    for _ in range(math.ceil(len(replyBytes)/BUFFERSIZE)):
        bytesData = replyBytes[:BUFFERSIZE]
        replyBytes = replyBytes[BUFFERSIZE:]
        if not bytesData:
            break
        connection.sendall(bytesData)
        progress1.update(len(bytesData))

    # 关闭资源
    print("推送完毕!")
    connection.close() # 先关客户端
# s.close() # 再关服务端

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值