从客户端向服务器(套接字)发送多个文件

时间:2020-04-26 01:46:51

标签: python

我正在使用带套接字的Python编写客户端服务器程序。我完成了大部分工作。我的最后一个问题似乎无法解决,该项目应于今晚进行。我有一个问题,其中客户端需要在服务器接收文件之前断开连接。如果我在发送文件时未关闭客户端连接,则服务器将不会收到该文件。当我尝试其他解决方案时,客户端根本不会发送文件。我希望能够发送多个文件,而不必每次都从客户端重新连接到服务器。

客户端

#!/usr/bin/python3.6

# import necessary librabries
import socket
from easygui import *
from sys import argv
from ssl import *
import tqdm
import os
import socket as sk

# create a tcip socket so send data through
my_socket = sk.socket(sk.AF_INET, sk.SOCK_STREAM)

# set client connected status to an initial state of false
global connected
attempts = 3

hostname = ''
port = ''

SEPARATOR = "<SEPARATOR>"
BUFFER_SIZE = 1024 * 4
server_address = ()

# server function
def server_info():
    title = 'Server Login'
    msg = 'Enter your server login details'
    global hostname, port
    server_details = (hostname, port)
    server_vals = []

    connected = False

    # create port and sockets and if argument length matches then port is set to specified details
    if len(argv) == 3:
        hostname = (argv[1])
        port = int(argv[2])
    else:
        pass

    while True:
        try:
            server_vals = multenterbox(msg, title, server_details)
            hostname = server_vals[0]
            port = int(server_vals[1])
            break
        except ValueError:
            msgbox('The port value must be an integer.')
        # print()

    # while the client isnt connected to the server
    while not connected:

        # Connect the socket to the port where the server is listening
        server_address = (hostname, port)

        # try this line of code first
        try:
            # send a connection request to the server
            my_socket.connect(server_address)
            connected = True
            break

        # if server isn't running or wrong port entered
        except ConnectionRefusedError:
            title = 'Connection Refused Error'
            msg = ('Server is not running or incorrect port ' + str(port) + '\n\n Select an option and press enter')
            choice = ('Abort', 'Retry', 'Change')

            my_choice = buttonbox(msg, title, choice)

            if my_choice == 'Abort':
                exit()
            elif my_choice == 'Change':
                while True:
                    try:
                        port = int(enterbox('Enter port number: '))
                        break
                    except ValueError:
                        my_msg = msgbox('Please stop messing around')
                    # print()
            elif my_choice == 'Retry':
                pass

        # if user enters incorrect value for port number
        except ValueError:
            msgbox('The port value must be an integer.')
            # print()
            port = int(input('Enter port number: '))

        # if user enters an incorrect hostname
        except socket.gaierror:
            msgbox('Incorrect host name, please enter the correct host and try again.')
            # print()
            hostname = enterbox('Enter server address: ')


def make_choice():
    msg = 'Select Option'
    title = 'Client'
    server_options = ('Access Files', 'Send Files', 'Exit Client')

    my_options = buttonbox(title, msg, server_options)

    if my_options == 'Access Files':
        access_files()
    elif my_options == 'Send Files':
        send_files()
    else:
        exit()


def send_files():
    title = 'Select an option'
    msg = 'Choose your action'

    filetypes = ["*.css", "*.png", "*.jpg", ["*.htm", "*.html", "HTML files"], '*.txt', '*.py', '*.zip']

    # my_dir = diropenbox()
    filename = fileopenbox(msg, title, filetypes=filetypes, default='/home/lay/')

    filesize = os.path.getsize(filename)

    # send the filename and filesize
    my_socket.send(f"{filename}{SEPARATOR}{filesize}".encode())

    # start sending the file
    progress = tqdm.tqdm(range(filesize), f"Sending {filename}", unit="B", unit_scale=True, unit_divisor=1024)
    with open(filename, "rb") as f:
        for _ in progress:
            while True:
                # read the bytes from the file
                bytes_read = f.read(BUFFER_SIZE)
                if not bytes_read:
                    # file transmitting is done
                    break

                # we use sendall to assure transimission in
                # busy networks
                my_socket.sendall(bytes_read)

                # update the progress bar
                progress.update(len(bytes_read))

                #make_choice()

    # shutdown the socket
    my_socket.shutdown(sk.SHUT_RDWR)

    # close the socket
    my_socket.close()
    connected = False


def access_files():
    pass


def re_send():
    make_choice()


# _login()
# username()
# password()
# user_login()
server_info()
make_choice()
re_send()
# send_files()
# access_files()

服务器端

#!/usr/bin/python3.6

# import all features of easygui
from easygui import *
# import argv from sys
from sys import argv
# import socket abbreviated as sk to save time
import socket as sk

# import ssl for security
from ssl import *

# import socket
import socket
import tqdm
import os

# save folder
pics = '/home/lay/server_folder/pictures'
docx = '/home/lay/server_folder/documents'
songs = '/home/lay/server_folder/songs'

# server address
server_address = ()

# Create a socket
sv_sk = sk.socket(sk.AF_INET, sk.SOCK_STREAM)

# this allows the server to use the socket immediately after the server has been closed
sv_sk.setsockopt(sk.SOL_SOCKET, sk.SO_REUSEADDR, 1)

# receive 4096 bytes each time
BUFFER_SIZE = 4096
SEPARATOR = "<SEPARATOR>"


def my_server():
    global server_address
    title = 'Server Port'
    msg = 'Enter your server port'
    hostname = socket.gethostbyname('localhost')
    port = ''

    # set bound to false initially
    bound = False

    # create a port if the length of the arguments match then the port is set
    # else it asks user to input in gui
    if len(argv) == 2:
        port = int(argv[1])
    else:
        pass

    while True:
        try:
            server_vals = enterbox(msg, title, port)
            if server_vals is None:
                exit()
            else:
                port = int(server_vals)
                break
        except ValueError:
            msgbox('The port value must be an integer.')
        # print()

    # while the server socket hasn't been bound to a port
    while not bound:
        # print the server address and port number
        server_address = (hostname, port)

        try:
            # bind the server to a specific socket
            sv_sk.bind(server_address)
            # if all goes as planned bound value set to true
            bound = True

        # if user enters incorrect format for port
        except ValueError:
            msgbox('Value must be an integer.')
            # print()
            port = int(enterbox('Server port number: '))

        # if user tries to use a port they have no access to
        except PermissionError:
            msgbox('Access Denied!! ')
            msgbox('choose a port number over 1023..')
            # print()
            port = int(enterbox('Server port number? '))

        except TypeError:
            msgbox('Client Disconnected')
            exit()

    while True:
        # set the server to listen for incoming connections
        sv_sk.listen(1)

        # indicate when server is listening
        print()
        print('Server listening for incoming connections on port....', port)

        # if a connection request is detected the server accepts
        connection, client_address = sv_sk.accept()

        # a print statement to inform the user when a connection is established
        print()
        print('Server Started at:', server_address)
        print('Connection Established:', client_address)
        print()

        # receive the file infos
        # receive using client socket, not server socket
        received = connection.recv(BUFFER_SIZE).decode()
        filename, filesize = received.split(SEPARATOR)

        # remove absolute path if there is
        filename = os.path.basename(filename)

        # convert to integer
        filesize = int(filesize)

        if filename.endswith(('.txt', '.docx', '.py')):
            # start receiving the file from the socket
            # and writing to the file stream
            progress = tqdm.tqdm(range(filesize), f"Receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)
            with open(os.path.join(docx, filename), "wb") as f:
                for _ in progress:
                    while True:
                        # read 1024 bytes from the socket (receive)
                        bytes_read = connection.recv(BUFFER_SIZE)

                        if not bytes_read:
                            # nothing is received
                            # file transmitting is done
                            break
                        # write to the file the bytes we just received
                        f.write(bytes_read)
                        # update the progress bar
                        progress.update(len(bytes_read))

        elif filename.lower().endswith(('.png', 'jpg', 'jpeg')):
            # start receiving the file from the socket
            # and writing to the file stream
            progress = tqdm.tqdm(range(filesize), f"Receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)
            with open(os.path.join(pics, filename), "wb") as f:
                for _ in progress:
                    while True:
                        # read 1024 bytes from the socket (receive)
                        bytes_read = connection.recv(BUFFER_SIZE)

                        if not bytes_read:
                            # nothing is received
                            # file transmitting is done
                            break

                        # write to the file the bytes we just received
                        f.write(bytes_read)
                        # update the progress bar
                        progress.update(len(bytes_read))

    # use this to shut down and close the connection from client first
    # connection.close()
    # connection.shutdown(sk.SHUT_RDWR)

    # then use this to shut down and close the socket
    # sv_sk.shutdown(sk.SHUT_RDWR)
    # sv_sk.close()


my_server()

如果运行代码,则会有一个进度条,确切显示程序挂起的原因。我尝试了许多不同的方法,但是除非关闭客户端连接,否则服务器将卡在0并不会收到文件。我让客户端发送的消息没有关闭,但是服务器再次不接收文件,但是一旦我关闭客户端,服务器就会接收文件。

1 个答案:

答案 0 :(得分:0)

经过3天的地狱之苦,我终于弄清楚了,这是好奇的人的答案。

客户

# if you choose to send file to server the server will be set to receive file
def send_files():
    title = 'Select an option'
    msg = 'Choose your action'
    total = 0

    filetypes = ["*.txt", "*.png", "*.jpg", ["*.htm", "*.html", "HTML files"], '*.mp3', '*.py', '*.zip']

    # opens a file open box that lets you select file to send to server
    filename = fileopenbox(msg, title, filetypes=filetypes, default='/home/lay/')

    # get the size of the file used to display progress bar
    filesize = os.path.getsize(filename)

    # send the filename and filesize
    tls_client.send(f"{filename}{SEPARATOR}{filesize}".encode('utf-8'))

    # start sending the file using tqdm to display progress of file
    progress = tqdm.tqdm(range(filesize), f"Sending {filename}", unit="B", unit_scale=True, unit_divisor=1024)

    # open file for transimitting in 'readbyte' mode
    with open(filename, "rb") as f:
        # progress or tqdm bar
        for _ in progress:
            while total != filesize:
                # read the bytes from the file
                bytes_read = f.read(BUFFER_SIZE)

                # to check when file is done transmitting
                if total == filesize:
                    break

                # we use sendall to assure transimission in
                # busy networks
                tls_client.sendall(bytes_read)

                # update the progress bar
                progress.update(len(bytes_read))
                total += len(bytes_read)
    f.close()


服务器

def receive_server():
    global connection
    total = 0

    # receive the file infos
    # receive using client socket, not server socket
    received = connection.recv(BUFFER_SIZE).decode()
    filename, filesize = received.split(SEPARATOR)

    # remove absolute path if there is
    filename = os.path.basename(filename)

    # convert to integer
    filesize = int(filesize)

    if filename.endswith(('.txt', '.docx', '.py')):
        # start receiving the file from the socket
        # and writing to the file stream
        progress = tqdm.tqdm(range(filesize), f"Receiving {filename}", unit="B", unit_scale=True, unit_divisor=1024)
        with open(os.path.join(docx, filename), "wb") as f:
            for _ in progress:
                while total != filesize:
                    # read 1024 bytes from the socket (receive)
                    bytes_read = connection.recv(BUFFER_SIZE)

                    if total == filesize:
                        # nothing is received
                        # file transmitting is done
                        break
                    # write to the file the bytes we just received
                    f.write(bytes_read)

                    # update the progress bar
                    progress.update(len(bytes_read))
                    total += len(bytes_read)

        f.close()
        client_choice()

我的意思是我不得不大量摆弄,几乎迷失了方向,但现在它确实可以工作了。文件已发送,服务器重置回给您,并可以选择发送更多文件。现在,只要我能找到一种从服务器请求文件的方法,那就好了。