【问题标题】:Python File Server Only Works with One Thread RunningPython 文件服务器仅适用于一个线程运行
【发布时间】:2018-06-11 21:59:07
【问题描述】:

我已经使用套接字和线程在 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 后没有其他反应。

如果有人知道出了什么问题,我将非常感谢一些帮助。

【问题讨论】:

    标签: python multithreading sockets python-multithreading


    【解决方案1】:

    这不是 io 和线程的工作方式。您在这里有 2 个线程从相同的输入数据竞争。无论是否为它,一个人都会得到第一个数据包,并且很可能以下数据包中的一个会被另一个线程吃掉=>第一个永远不会看到它!

    您可以将对话的处理委托给一个线程,但也可以委托给一个线程,一旦它识别出请求,它将调用发送或接收函数。

    这还不是全部。 TCP 是一个 协议。数据包可以通过连接上的任何部分(发送方、接收方和任何网关)进行拆分或重新组合。所以你应该使用 delimiters 告诉对端一个名字或一个命令是否完整。并且良好的做法建议在发送二进制数据时传递大小,在这里再次让对等方知道数据何时完成。

    祝你旅途顺利,插座世界 ;-)

    【讨论】:

    • 感谢您的回复。我理解你所说的线程竞争相同数据的意思,但我投入了一些东西来试图阻止这种情况。客户端向服务器发送一个字符串,如果它与服务器代码中的预期字符串匹配,则开始上传/下载代码。这还不足以防止他们窃取彼此的数据吗?还要感谢有关使用分隔符和传递文件大小的提示。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-13
    • 1970-01-01
    • 2011-05-24
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多