简体   繁体   English

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

[英]Sending multiple files from client to server (sockets)

I'm making a client-server program using sockets with Python.我正在使用 sockets 和 Python 制作客户端服务器程序。 I have most of the work done.我已经完成了大部分工作。 My last problem I can't seem to solve and the project is due tonight.我的最后一个问题我似乎无法解决,该项目今晚到期。 I have a problem where either the client needs to disconnect before the server receives the file.我有一个问题,客户端需要在服务器接收文件之前断开连接。 If I don't close the client connection when I send a file the server won't receive the file.如果我在发送文件时没有关闭客户端连接,则服务器将不会收到该文件。 When I try other solutions the client won't send the file at all.当我尝试其他解决方案时,客户端根本不会发送文件。 I want to be able to send multiple files without having to reconnect to the server every time from the client-side.我希望能够发送多个文件,而不必每次都从客户端重新连接到服务器。

Client side客户端

#!/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()

Server side服务器端

#!/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()

If you run the code, there's a progress bar that shows exactly why the program is hanging.如果你运行代码,会有一个进度条显示程序挂起的确切原因。 I tried so many different ways, but unless I close the client connection, the server gets stuck at 0 and won't receive the file.我尝试了很多不同的方法,但除非我关闭客户端连接,否则服务器会卡在 0 并且不会接收到文件。 I got the client to send without closing, but then again the server won't receive the file, but as soon as I close the client, the server receives the file.我让客户端在没有关闭的情况下发送,但是服务器又不会收到文件,但是一旦我关闭客户端,服务器就会收到文件。

After 3 days of hell i actually figure it out, here's the answer for anyone curious.经过 3 天的地狱我真的弄明白了,这是任何好奇的人的答案。

client客户

# 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()


server服务器

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()

I mean i had to do a massive amount of fiddling around and almost lost my mind but now it actually works.我的意思是我不得不做大量的摆弄,几乎失去了理智,但现在它确实有效。 The file is sent and the server resets back to giving you and option to send more files.文件被发送,服务器重新设置为给你发送更多文件的选项。 Now if only i can get find a way to request a file from the server be great.现在,如果我能找到一种从服务器请求文件的方法就好了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM