Отправка нескольких файлов Python с помощью сокета - PullRequest
0 голосов
/ 04 ноября 2018

В настоящее время я пытаюсь создать клиент-серверное приложение, в котором клиент может отправлять несколько файлов на сервер по протоколу TCP. Сервер в конечном итоге создаст хеш-алгоритм и отправит его обратно клиенту, но у меня возникают проблемы с отправкой нескольких файлов с клиента на сервер. В его текущей форме первый файл отправляется правильно, но файлы после сталкиваются с ошибкой, когда информация объединяется вместе. То есть размер файла указан как имя второго файла. Я - чувак из javascript и очень плохо знаком с python, поэтому объяснение того, как я могу это сделать, будет очень цениться. Я верю, что многопоточность - это ответ, но с моим ограниченным пониманием Python я не знаю, как заставить это работать. В настоящее время я могу отправить один файл за раз, и сервер остается открытым. Однако я хотел бы ввести несколько имен файлов из моего текущего каталога и обработать их. В конечном итоге я преобразую всю клиентскую часть в C, но я изо всех сил пытаюсь заставить сервер работать правильно на python. Любой совет будет высоко ценится!

Server.py

import socket
import hashlib
import threading
import struct

HOST = '127.0.0.1'
PORT = 2345

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(10)
print("Waiting for a connection.....")

conn, addr = s.accept()
print("Got a connection from ", addr)

while True:

hash_type = conn.recv(1024)
print('hash type: ', hash_type)
if not hash_type:
    break

file_name = conn.recv(1024)
print('file name: ', file_name)

file_size = conn.recv(1024)
file_size = int(file_size, 2)
print('file size: ', file_size )

f = open(file_name, 'wb')
chunk_size = 4096
while file_size > 0:
    if file_size < chunk_size:
        chuk_size = file_size
    data = conn.recv(chunk_size)
f.write(data)

file_size -= len(data)
f.close()
print('File received successfully')
s.close()

Client.py

import socket
import threading
import os

HOST = '127.0.0.1'
PORT = 2345

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

hash_type = input('Enter hash type: ')

files = input('Enter file(s) to send: ')
files_to_send = files.split()

for file_name in files_to_send:
s.send(hash_type.encode())

print(file_name)
s.send(file_name.encode())

file_size = os.path.getsize(file_name)
file_size = bin(file_size)
print(file_size)
s.send(file_size.encode())

f = open(file_name, 'rb')
l = f.read()
while(l):
    s.send(l)
    l = f.read()
f.close()
print('File Sent')

s.close()

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Одним из способов справиться с тем, что вы делаете, является буферизация данных вашего сокета. Ниже приведен класс данных буфера и он знает, как отправлять и получать завершенные нулем строки в кодировке UTF-8 и необработанные куски байтов:

buffer.py:

class Buffer:
    def __init__(self,s):
        '''Buffer a pre-created socket.
        '''
        self.sock = s
        self.buffer = b''

    def get_bytes(self,n):
        '''Read exactly n bytes from the buffered socket.
           Return remaining buffer if <n bytes remain and socket closes.
        '''
        while len(self.buffer) < n:
            data = self.sock.recv(1024)
            if not data:
                data = self.buffer
                self.buffer = b''
                return data
            self.buffer += data
        # split off the message bytes from the buffer.
        data,self.buffer = self.buffer[:n],self.buffer[n:]
        return data

    def put_bytes(self,data):
        self.sock.sendall(data)

    def get_utf8(self):
        '''Read a null-terminated UTF8 data string and decode it.
           Return an empty string if the socket closes before receiving a null.
        '''
        while b'\x00' not in self.buffer:
            data = self.sock.recv(1024)
            if not data:
                return ''
            self.buffer += data
        # split off the string from the buffer.
        data,_,self.buffer = self.buffer.partition(b'\x00')
        return data.decode()

    def put_utf8(self,s):
        if '\x00' in s:
            raise ValueError('string contains delimiter(null)')
        self.sock.sendall(s.encode() + b'\x00')

С этим классом ваш клиент и сервер становятся:

client.py:

import socket
import threading
import os

import buffer

HOST = '127.0.0.1'
PORT = 2345

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

with s:
    sbuf = buffer.Buffer(s)

    hash_type = input('Enter hash type: ')

    files = input('Enter file(s) to send: ')
    files_to_send = files.split()

    for file_name in files_to_send:
        print(file_name)
        sbuf.put_utf8(hash_type)
        sbuf.put_utf8(file_name)

        file_size = os.path.getsize(file_name)
        sbuf.put_utf8(str(file_size))

        with open(file_name, 'rb') as f:
            sbuf.put_bytes(f.read())
        print('File Sent')

server.py:

import socket
import os

import buffer

HOST = ''
PORT = 2345

# If server and client run in same local directory,
# need a separate place to store the uploads.
try:
    os.mkdir('uploads')
except FileExistsError:
    pass

s = socket.socket()
s.bind((HOST, PORT))
s.listen(10)
print("Waiting for a connection.....")

while True:
    conn, addr = s.accept()
    print("Got a connection from ", addr)
    connbuf = buffer.Buffer(conn)

    while True:
        hash_type = connbuf.get_utf8()
        if not hash_type:
            break
        print('hash type: ', hash_type)

        file_name = connbuf.get_utf8()
        if not file_name:
            break
        file_name = os.path.join('uploads',file_name)
        print('file name: ', file_name)

        file_size = int(connbuf.get_utf8())
        print('file size: ', file_size )

        with open(file_name, 'wb') as f:
            remaining = file_size
            while remaining:
                chunk_size = 4096 if remaining >= 4096 else remaining
                chunk = connbuf.get_bytes(chunk_size)
                if not chunk: break
                f.write(chunk)
                remaining -= len(chunk)
            if remaining:
                print('File incomplete.  Missing',remaining,'bytes.')
            else:
                print('File received successfully.')
    print('Connection closed.')
    conn.close()

Демо

клиент:

Enter hash type: abc
Enter file(s) to send: demo1.dat demo2.dat
demo1.dat
File Sent
demo2.dat
File Sent

Сервер:

Waiting for a connection.....
Got a connection from  ('127.0.0.1', 22126)
hash type:  abc
file name:  uploads\demo1.dat
file size:  488892
File received successfully.
hash type:  abc
file name:  uploads\demo2.dat
file size:  212992
File received successfully.
Connection closed.
0 голосов
/ 04 ноября 2018

1. file_size = conn.recv(1024) В коде вашего сервера вы читаете 1024 байта как file_size, file_size имеет длину всего 4 или 8 байтов

2. file_name = conn.recv(1024) Ваш сервер не знает, как долго находится имя файла / hashtype.

-> Используйте long для обоих размеров и только для чтения sizeof (long) байтов из потока.

Вы можете использовать https://docs.python.org/2/library/struct.html для упаковки / кодирования этих номеров

-> Или просто пойти легким путем и использовать https://docs.python.org/3/library/pickle.html для сериализации

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...