Python文件服务器仅适用于一个线程运行

时间:2018-06-11 13:25:41

标签: python multithreading sockets python-multithreading

我使用套接字和线程在python上创建了一个文件服务器。该程序应该允许客户端从服务器上传和下载文件。

当只有一个线程正在运行时,程序运行正常,但是当两个线程都在运行时,服务器在尝试上传文件时会出错,而在尝试下载程序时,只需在客户端进入&#39后停止执行任何操作; 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 :(得分:0)

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

您可以将会话的处理委派给一个线程,但是一旦它确定了请求就会调用一个将调用发送或接收功能的线程。

这不是全部。 TCP是协议。数据包可以通过连接(发送方,接收方和任何网关)的任何部分进行拆分或重新组装。因此,您应该使用分隔符告诉对等方名称或命令是否完整。并且好的做法建议在发送二进制数据时传递大小,这里同样要让对等方知道数据何时完成。

祝你旅途中的好运是套接字世界; - )