我正在使用带套接字的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并不会收到文件。我让客户端发送的消息没有关闭,但是服务器再次不接收文件,但是一旦我关闭客户端,服务器就会接收文件。
答案 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()
我的意思是我不得不大量摆弄,几乎迷失了方向,但现在它确实可以工作了。文件已发送,服务器重置回给您,并可以选择发送更多文件。现在,只要我能找到一种从服务器请求文件的方法,那就好了。