python3 tcp客户端服务器通信

时间:2018-10-30 11:56:41

标签: python-3.x sockets

我想在将图像(.pgm)写入ramdisk后立即通过TCP发送。为此,我正在使用pyinotify和sockets。图片发送后,我想告诉服务器现在停止。 一切正常,但最后一部分给我以下错误:

if data.decode('utf-8') == 'stop': UnicodeDecodeError: 'utf-8' codec can't
decode byte 0x88 in position 319: invalid start byte

客户:

import pyinotify
import socket
import traceback
import sys


class ModHandler(pyinotify.ProcessEvent):
    def __init__(self, socket, buffer_size):
        self.socket = socket
        self.buffer_size = buffer_size

    def process_IN_CLOSE_WRITE(self, event):
        try:
            self.socket.send(bytes(event.pathname, encoding='utf-8'))
            file = open(event.pathname, "rb")
            line = file.read(self.buffer_size) 
            while(line):
                self.socket.send(line)  
                line = file.read(self.buffer_size)

        except Exception:
            traceback.print_exc()
        finally:
            try:
                self.socket.send(bytes('stop', encoding='utf-8'))
                print("done")
                file.close
            except Exception:
                traceback.print_exc()

class TCPStream():
    def __init__(self, ip, port, buffer_size):
        self.ip = ip
        self.port = port
        self.buffer_size = buffer_size
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            self.socket.connect((self.ip, self.port))
        except Exception:
            traceback.print_exc()

    def __del__(self):
        try:
            self.socket.close()
        except Exception:
            traceback.print_exc()


stream = TCPStream('127.0.0.1', 5005, 1024)

handler = ModHandler(stream.socket, stream.buffer_size)
wm = pyinotify.WatchManager()
notifier = pyinotify.Notifier(wm, handler)
wd_value = wm.add_watch("/media/ram_disk", pyinotify.IN_CLOSE_WRITE)
if wd_value["/media/ram_disk"] <= 0:
    print("can't add watchmanager to the ram_disk... insufficient
    authorization? another watchmanager already running?")
    sys.exit(0)
notifier.loop()

服务器:

import socket


TCP_IP = '127.0.0.1'
TCP_PORT = 5005
BUFFER_SIZE = 1024 

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
conn, addr = s.accept()
print("connection address: ", addr)

path = conn.recv(BUFFER_SIZE).decode('utf-8')
filename = path.split("/")

with open(filename[3], 'wb') as f:
    data = conn.recv(BUFFER_SIZE)
    while data:
        print("receiving...")
        f.write(data)
        data = conn.recv(BUFFER_SIZE)

        if not data: 
            break

        if data.decode('utf-8') == 'stop':
            f.close()
            print("done")
            break

conn.close()

目标是将恒定的TCP图像流写入虚拟磁盘。因此,我想通过字节与服务器通信以告诉他该怎么做。似乎在传输第一张图片后,它以某种方式破裂。任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:0)

如果图像在缓冲区开头的四个连续字节碰巧与ASCII(和UTF-8)字符s t o p相匹配怎么办?另外,接收方如何知道文件名何时结束以及文件数据何时开始?

您应该创建一个二进制编码,以对数据的各个位进行构架。这使整个过程具有很好的确定性。通常最好使用struct模块的packunpack方法来完成。给定您要发送的文件,客户端:

import os
import struct

    ...
    pathname = event.pathname.encode('utf-8')  # Encode pathname into bytes
    pathname_len = len(pathname)
    file = open(event.pathname, "rb")
    filesize = os.path.getsize(event.filename) # Get file size
    # Encode size of file name, and size of file into a binary header
    header_format = struct.Struct("!II")
    header = header_format.pack(pathname_len, filesize)
    self.socket.sendall(header)
    self.socket.sendall(pathname)
    while True:
        line = file.read(self.buffer_size)
        if not line: break # EOF
        self.socket.sendall(line)

    # (Remove sending of 'stop' from finally block)

请注意使用sendall来确保发送整个缓冲区(send仅发送缓冲区的一部分是合法的,但是如果您不记帐,则会导致字节丢失)为此)。

服务器端看起来像这样:

import struct

    ...
    def recv_exactly(s, buffer_len):
        """ This is the converse of sendall """
        data = b''
        rem_bytes = buffer_len
        while rem_bytes > 0:
            buf = s.recv(rem_bytes)
            if not buf:
                raise Exception("Received EOF in middle of block")
            data += buf
            rem_bytes -= len(buf)
        return data

    conn, addr = s.accept()
    ...

    header_format = struct.Struct("!II")
    # Receive exactly the bytes of the header
    header = recv_exactly(conn, header_format.size)
    pathname_len, file_len = header_format.unpack(header)
    path = recv_exactly(conn, pathname_len)
    filename = path.split("/")
    ...
    rem_bytes = file_len
    while rem_bytes > 0:
        data = conn.recv(min(rem_bytes, BUFFER_SIZE))
        if not data:
            raise Exception("Received EOF in middle of file")
        f.write(data)
        rem_bytes -= len(data)

该模型的另一个重要优点是,您现在对一个文件和下一个文件之间的边界有了清晰的认识(没有,其数据中可能会出现“信号值”)。接收者总是确切知道直到当前文件末尾还有多少字节,发送者可以继续前进以发送新的头,路径名和文件,而无需打开新的连接。