Файловый сервер Python работает только с одним потоком - PullRequest
0 голосов
/ 11 июня 2018

Я сделал файловый сервер на Python, используя сокеты и потоки.Программа должна позволять клиенту загружать и скачивать файлы с сервера.

Программа работает отлично, когда работает только один поток, но когда работают оба потока, сервер выдает ошибку при попытке загрузить файл, а при попытке загрузить программу просто перестает делать что-либо после входа клиента'Y', чтобы начать загрузку.

Вот код для клиента:

import socket
import os

def DownloadFile(s, host, port):
    s.connect((host, port))
    s.send(str.encode('DNLD'))
    filename = input('Filename? ->')
    if filename != 'q':
        s.send(str.encode(filename))
        data = s.recv(2048).decode('UTF-8')
        if data[:6] == 'EXISTS':
            filesize = data[6:]
            message = input('File Exists, ' + str(filesize) + ' Bytes. Download? (Y/N) ->')
            if message == 'Y' or message == 'y':
                s.send(str.encode('OK'))
                f = open('copy of '+filename, 'wb')
                data = s.recv(2048)
                totalRecv = len(data)
                f.write(data)
                while totalRecv < int(filesize):
                    data = s.recv(2048)
                    totalRecv += len(data)
                    f.write(data)
                    print('{}'.format(round((totalRecv/float(filesize))*100),2)+'% Complete')
                print('Download Complete!')
                s.close()

        else:
            print('File does not exist')
            s.close()
    Main()

def UploadFile(s, host, port):
    s.connect((host, port))
    s.send(str.encode('UPLD'))
    filename = input('Filename? ->')
    if os.path.isfile(filename):
        filesize = os.path.getsize(filename)
        filesize = str(filesize)
        s.send(str.encode('EXISTS ' + filename))
        s.send(str.encode(filesize))
        ready = input('Ready to upload. Proceed? (Y/N) ->')
        if ready == 'Y' or ready == 'y':
            s.send(str.encode('OK'))
            with open(filename, 'rb') as f:
                bytesToSend = f.read(2048)
                s.send(bytesToSend)
                while bytesToSend != '':
                    bytesToSend = f.read(2048)
                    s.send(bytesToSend)
                s.close()
    else:
        print('File does not exist.')
        s.close()
    Main()

def Main(): 
    host = '127.0.0.1'
    port = 10000
    s = socket.socket()
    while True:
        choice = int(input('Please enter your choice:\n\n1. Upload a file to the server.\n2. Download a file from the server\n3. Quit.\n\n->'))
        if choice == 1:
            UploadFile(s, host, port)
            break
        elif choice == 2:
            DownloadFile(s, host, port)
            break
        elif choice == 3:
            s.close()
            break
        else:
            print('Please enter a valid choice.')

if __name__ == '__main__':
    Main()

А вот код для сервера:

import socket
import threading
import os

def SendFile(name, s):
    check = s.recv(2048).decode('UTF-8')
    if check == 'DNLD':
        filename = s.recv(2048)
        if os.path.isfile(filename):
            send = os.path.getsize(filename)
            send = str(send)
            s.send(str.encode('EXISTS ' + send))
            userResponse = s.recv(2048)
            userResponse = userResponse.decode('UTF-8')
            if userResponse[:2] == 'OK':
                with open(filename, 'rb') as f:
                    bytesToSend = f.read(2048)
                    s.send(bytesToSend)
                    while bytesToSend != '':
                        bytesToSend = f.read(2048)
                        s.send(bytesToSend)
            else:
                s.send(str.encode('ERR'))

    s.close()

def ReceiveFile(name, s):
    check = s.recv(2048).decode('UTF-8')
    if check == 'UPLD':
        data = s.recv(2048).decode('UTF-8')
        if data[:6] == 'EXISTS':
            filename = data[6:]
            data = s.recv(2048).decode('UTF-8')
            filesize = data
            userResponse = s.recv(2048)
            userResponse = userResponse.decode('UTF-8')
            if userResponse[:2] == 'OK':
                f = open('copy of '+filename, 'wb')
                data = s.recv(2048)
                totalRecv = len(data)
                f.write(data)
                while totalRecv < int(filesize):
                    data = s.recv(2048)
                    totalRecv += len(data)
                    f.write(data)
                print('Download Complete!')

def Main():
    host = '127.0.0.1'
    port = 10000
    s = socket.socket()
    s.bind((host, port))
    s.listen(5)
    print('Server Started')

    while True:
        c, addr = s.accept()
        print('Client Connected: ' + str(addr))
        Send = threading.Thread(target=SendFile, args=('sendThread', c))
        Send.start()
        Receive = threading.Thread(target=ReceiveFile, args=('retrThread', c))
        Receive.start()

    s.close()

if __name__ == '__main__':
    Main()

ЕслиЯ должен был закомментировать Send.start () или Receive.start (), тогда любой закомментированный поток будет работать отлично.

Вот ошибка, сообщаемая на сервере при попытке загрузить файл с обоимиРаботающие потоки:

Exception in thread Thread-2:
Traceback (most recent call last):
  File "C:\Python34\lib\threading.py", line 920, in _bootstrap_inner
    self.run()
  File "C:\Python34\lib\threading.py", line 868, in run
    self._target(*self._args, **self._kwargs)
  File "(file location)", line 28, in ReceiveFile
    check = s.recv(2048).decode('UTF-8')
OSError: [WinError 10038] An operation was attempted on something that is not a socket

И вот вывод в клиенте при попытке загрузить файл, когда работают оба потока:

Please enter your choice:

1. Upload a file to the server.
2. Download a file from the server
3. Quit.

->2
Filename? ->cat.jpg
File Exists,  10634 Bytes. Download? (Y/N) ->Y

После ввода Y ничего больше не происходит.

Если кто-нибудь знает, что происходит не так, я был бы очень признателен за помощь.

1 Ответ

0 голосов
/ 11 июня 2018

Это не так io и потоки работают.Здесь у вас есть 2 потока, конкурирующие с одними и теми же входными данными.Один получит первый пакет, будет он за это или нет, и вполне вероятно, что один из следующих пакетов будет съеден другим потоком => первый никогда его не увидит!

Вы можете делегировать обработку разговора с потоком, но с одним, который вызовет функцию отправки или получения, как только он определит запрос.

Это еще не все.TCP - это протокол stream .Пакеты могут быть разделены или повторно собраны любым участком по соединению (отправитель, получатель и любой шлюз).Таким образом, вы должны использовать delimiters , чтобы сообщить партнеру, что имя или команда завершены.И хорошие практики рекомендуют передавать размер при отправке двоичных данных, и здесь одноранговый узел узнает, когда данные полны.

Удачи в вашем путешествии - мир сокетов; -)

...