Continue making HW5 work... fix the client issue hopefully?
This commit is contained in:
parent
61c5acbcfa
commit
d157cf8189
2 changed files with 131 additions and 203 deletions
|
|
@ -32,6 +32,10 @@ def main():
|
|||
client_socket.connect((SERVER_HOST, SERVER_PORT))
|
||||
print(f"Connected to chat server at {SERVER_HOST}:{SERVER_PORT}")
|
||||
|
||||
# Prompt user for a username and send it to the server
|
||||
username = input("Enter your username: ").strip()
|
||||
client_socket.send(username.encode('utf-8'))
|
||||
|
||||
# Send the local path to the server
|
||||
client_socket.send(f"SET_PATH {local_path}".encode('utf-8'))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import socket
|
||||
import select
|
||||
import threading
|
||||
|
||||
CHATPORT = 8181
|
||||
BUFFER_SIZE = 1024
|
||||
|
|
@ -18,60 +19,14 @@ def format_file_info(file_path):
|
|||
file_type = "Directory" if os.path.isdir(file_path) else "File"
|
||||
return f"{os.path.basename(file_path)} - {file_type} - {size_str}"
|
||||
|
||||
def main():
|
||||
# set the flag variable to true for infinite loop
|
||||
quitFlag = True
|
||||
# create a socket object
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# set the socket options, SO_REUSEADDR allows to reuse the same port
|
||||
# SO_KEEPALIVE enables the socket to send keepalive messages
|
||||
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
||||
# bind the socket to the address and port
|
||||
server_socket.bind(('', CHATPORT))
|
||||
# listen for incoming connections
|
||||
server_socket.listen(MAX_CONNECT_QUEUE)
|
||||
# set the server socket to non-blocking
|
||||
server_socket.setblocking(False)
|
||||
|
||||
# create a list to store the sockets, server and client
|
||||
socket_list = [server_socket]
|
||||
# since a socket is associated with a username and a chatroom,
|
||||
# and each one can only be associated one way at once, use dictionaries to
|
||||
# associate with each other.
|
||||
usernames = {}
|
||||
chat_rooms = {}
|
||||
# keep track of the states of each client connected, and if they are
|
||||
# technically in an 'unsafe' state like in change username or chatroom mode.
|
||||
client_states = {}
|
||||
# Add a dictionary to store user-specific paths
|
||||
user_paths = {}
|
||||
# let the user know the server is listening on port CHATPORT.
|
||||
print(f"Chatserver listening on port: {CHATPORT}")
|
||||
|
||||
# infinite loop for the program
|
||||
while quitFlag:
|
||||
# use the select function to monitor the sockets
|
||||
read_sockets, _, _ = select.select(socket_list, [], [])
|
||||
# loop through the sockets
|
||||
for sock in read_sockets:
|
||||
# if the socket is the server socket, accept the connection
|
||||
if sock == server_socket:
|
||||
client_socket, client_address = server_socket.accept()
|
||||
client_socket.setblocking(False)
|
||||
socket_list.append(client_socket)
|
||||
print("\nAccepted connection from new client.")
|
||||
client_socket.send(b"\nWelcome to the chat server!\n")
|
||||
client_socket.send(b"\nUse '%!' to exit, or '%?' for help.\n")
|
||||
client_socket.send(b"\nType in a username: ")
|
||||
client_states[client_socket] = "WAITING_FOR_USERNAME"
|
||||
user_paths[client_socket] = None # Initialize path for new client
|
||||
# otherwise the socket is a client socket.
|
||||
else:
|
||||
# try to receive the message from the client
|
||||
def handle_client(sock, client_address, socket_list, usernames, chat_rooms, client_states, user_paths):
|
||||
"""Handle communication with a single client."""
|
||||
try:
|
||||
while True:
|
||||
message = sock.recv(BUFFER_SIZE).decode('utf-8')
|
||||
if message:
|
||||
if not message:
|
||||
break
|
||||
|
||||
state = client_states.get(sock, "NORMAL")
|
||||
if state == "WAITING_FOR_USERNAME":
|
||||
usernames[sock] = message.strip()
|
||||
|
|
@ -79,81 +34,47 @@ def main():
|
|||
sock.send(b"\nUsername accepted. You are now in the 'default' chatroom.")
|
||||
client_states[sock] = "NORMAL"
|
||||
user_paths[sock] = None # Initialize path for the user
|
||||
elif state == "WAITING_FOR_NEW_USERNAME":
|
||||
usernames[sock] = message.strip()
|
||||
sock.send(b"\nUsername changed successfully.")
|
||||
print(f"\nClient switched username to {usernames[sock]}.")
|
||||
client_states[sock] = "NORMAL"
|
||||
elif state == "WAITING_FOR_NEW_CHATROOM":
|
||||
old_chatroom = chat_rooms[sock]
|
||||
chat_rooms[sock] = message.strip()
|
||||
sock.send(f"\nSwitched to chatroom '{chat_rooms[sock]}.'".encode('utf-8'))
|
||||
print(f"\nClient switched to chatroom '{chat_rooms[sock]}.'")
|
||||
if old_chatroom != "default" and old_chatroom not in chat_rooms.values():
|
||||
print(f"\nChatroom '{old_chatroom}' is empty and removed.")
|
||||
client_states[sock] = "NORMAL"
|
||||
elif state == "NORMAL":
|
||||
if message.startswith("@") and "-ls" in message:
|
||||
# Parse the message format: @RECEIVING_USERNAME@SENDING_USERNAME -ls
|
||||
# Handle directory listing request
|
||||
parts = message.split()
|
||||
if len(parts) >= 2 and "-ls" in parts[1]:
|
||||
usernames_part = parts[0]
|
||||
if usernames_part.count("@") == 2:
|
||||
receiving_username, sending_username = usernames_part[1:].split("@")
|
||||
# Find the receiving user's socket
|
||||
receiving_sock = next(
|
||||
(s for s, u in usernames.items() if u == receiving_username), None
|
||||
)
|
||||
sending_sock = next(
|
||||
(s for s, u in usernames.items() if u == sending_username), None
|
||||
)
|
||||
if receiving_sock and sending_sock:
|
||||
receiving_sock = next((s for s, u in usernames.items() if u == receiving_username), None)
|
||||
if receiving_sock:
|
||||
user_path = user_paths.get(receiving_sock)
|
||||
if user_path and os.path.isdir(user_path):
|
||||
# List files in the receiving user's directory
|
||||
files = os.listdir(user_path)
|
||||
if files:
|
||||
file_list = "\n".join(
|
||||
[format_file_info(os.path.join(user_path, f)) for f in files]
|
||||
)
|
||||
sending_sock.send(
|
||||
f"Files in {receiving_username}'s directory:\n{file_list}".encode('utf-8')
|
||||
)
|
||||
sock.send(f"Files in {receiving_username}'s directory:\n{file_list}".encode('utf-8'))
|
||||
else:
|
||||
sending_sock.send(
|
||||
f"No files in {receiving_username}'s directory.".encode('utf-8')
|
||||
)
|
||||
sock.send(f"No files in {receiving_username}'s directory.".encode('utf-8'))
|
||||
else:
|
||||
sending_sock.send(
|
||||
f"Error: {receiving_username} has no valid directory set.".encode('utf-8')
|
||||
)
|
||||
sock.send(f"Error: {receiving_username} has no valid directory set.".encode('utf-8'))
|
||||
else:
|
||||
sock.send(b"Error: One or both users not found.")
|
||||
sock.send(b"Error: Receiving user not found.")
|
||||
else:
|
||||
sock.send(b"Error: Invalid message format.")
|
||||
elif message.startswith("@") and "-f" in message:
|
||||
# Parse the message format: @RECEIVING_USERNAME@SENDING_USERNAME -f FILENAME
|
||||
# Handle file request or transfer
|
||||
parts = message.split()
|
||||
if len(parts) >= 3 and "-f" in parts[1]:
|
||||
usernames_part = parts[0]
|
||||
if usernames_part.count("@") == 2:
|
||||
receiving_username, sending_username = usernames_part[1:].split("@")
|
||||
# Find the receiving user's socket
|
||||
receiving_sock = next(
|
||||
(s for s, u in usernames.items() if u == receiving_username), None
|
||||
)
|
||||
sending_sock = next(
|
||||
(s for s, u in usernames.items() if u == sending_username), None
|
||||
)
|
||||
if receiving_sock and sending_sock:
|
||||
receiving_sock = next((s for s, u in usernames.items() if u == receiving_username), None)
|
||||
if receiving_sock:
|
||||
if len(parts) == 3:
|
||||
# File request: @RECEIVING_USERNAME@SENDING_USERNAME -f FILENAME
|
||||
# File request
|
||||
filename = parts[2]
|
||||
receiving_sock.send(
|
||||
f"FILE_REQUEST {sending_username} {filename}".encode('utf-8')
|
||||
)
|
||||
receiving_sock.send(f"FILE_REQUEST {sending_username} {filename}".encode('utf-8'))
|
||||
elif len(parts) > 5 and parts[-1] == "ENDFILE":
|
||||
# File transfer: @RECEIVING_USERNAME@SENDING_USERNAME -f FILENAME FILETYPE FILELENGTH FILEBYTES ENDFILE
|
||||
# File transfer
|
||||
filename = parts[2]
|
||||
filetype = parts[3]
|
||||
filelength = int(parts[4])
|
||||
|
|
@ -164,66 +85,69 @@ def main():
|
|||
receiving_sock.send(filebytes)
|
||||
receiving_sock.send(b"ENDFILE")
|
||||
else:
|
||||
sock.send(b"Error: One or both users not found.")
|
||||
sock.send(b"Error: Receiving user not found.")
|
||||
else:
|
||||
sock.send(b"Error: Invalid message format.")
|
||||
elif message.strip() == "%!":
|
||||
sock.close()
|
||||
socket_list.remove(sock)
|
||||
del usernames[sock]
|
||||
del chat_rooms[sock]
|
||||
del client_states[sock]
|
||||
print("\nClient has closed connection.")
|
||||
elif message.strip() == "%?":
|
||||
print("\nClient requested help.")
|
||||
sock.send(b"Help: (%?)\n")
|
||||
sock.send(b"Send messages like normal. Use enter to send a message.\n")
|
||||
sock.send(b"Available Commands:\n")
|
||||
sock.send(b"\t%? : Show this help prompt.\n")
|
||||
sock.send(b"\t%! : Exit from the Chat Server.\n")
|
||||
sock.send(b"\t%* : Switch to another chatroom.\n")
|
||||
sock.send(b"\t%$ : Change Username.\n")
|
||||
elif message.strip() == "%*":
|
||||
sock.send(b"\nEnter chatroom name: ")
|
||||
client_states[sock] = "WAITING_FOR_NEW_CHATROOM"
|
||||
elif message.strip() == "%$":
|
||||
sock.send(b"\nEnter new username: ")
|
||||
client_states[sock] = "WAITING_FOR_NEW_USERNAME"
|
||||
break
|
||||
else:
|
||||
username = usernames[sock]
|
||||
chat_room = chat_rooms[sock]
|
||||
send_message = f"{username} >> {message}".encode('utf-8')
|
||||
for client_sock in socket_list:
|
||||
if client_sock != server_socket and chat_rooms.get(client_sock) == chat_room:
|
||||
if client_sock != sock and chat_rooms.get(client_sock) == chat_room:
|
||||
client_sock.send(send_message)
|
||||
else:
|
||||
except Exception as e:
|
||||
print(f"Error with client {client_address}: {e}")
|
||||
finally:
|
||||
# Clean up on client disconnect
|
||||
sock.close()
|
||||
socket_list.remove(sock)
|
||||
if sock in usernames:
|
||||
del usernames[sock]
|
||||
if sock in chat_rooms:
|
||||
old_chatroom = chat_rooms[sock]
|
||||
del chat_rooms[sock]
|
||||
if old_chatroom != "default" and old_chatroom not in chat_rooms.values():
|
||||
print(f"\nChatroom '{old_chatroom}' is empty and has been removed.")
|
||||
if sock in client_states:
|
||||
del client_states[sock]
|
||||
print("\nClient has closed connection.")
|
||||
except Exception as err:
|
||||
print(f"Error: {err}")
|
||||
sock.close()
|
||||
socket_list.remove(sock)
|
||||
if sock in usernames:
|
||||
del usernames[sock]
|
||||
if sock in chat_rooms:
|
||||
old_chatroom = chat_rooms[sock]
|
||||
del chat_rooms[sock]
|
||||
if old_chatroom != "default" and old_chatroom not in chat_rooms.values():
|
||||
print(f"\nChatroom '{old_chatroom}' is empty and has been removed.")
|
||||
if sock in client_states:
|
||||
del client_states[sock]
|
||||
print("\nClient has closed connection.")
|
||||
if sock in user_paths:
|
||||
del user_paths[sock]
|
||||
print(f"Client {client_address} disconnected.")
|
||||
|
||||
def main():
|
||||
# Initialize server
|
||||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server_socket.bind(('', CHATPORT))
|
||||
server_socket.listen(MAX_CONNECT_QUEUE)
|
||||
print(f"Chatserver listening on port: {CHATPORT}")
|
||||
|
||||
# Shared data structures
|
||||
socket_list = []
|
||||
usernames = {}
|
||||
chat_rooms = {}
|
||||
client_states = {}
|
||||
user_paths = {}
|
||||
|
||||
try:
|
||||
while True:
|
||||
client_socket, client_address = server_socket.accept()
|
||||
print(f"Accepted connection from {client_address}")
|
||||
socket_list.append(client_socket)
|
||||
|
||||
# Initialize client state
|
||||
client_states[client_socket] = "WAITING_FOR_USERNAME"
|
||||
user_paths[client_socket] = None
|
||||
|
||||
# Start a new thread for the client
|
||||
client_thread = threading.Thread(
|
||||
target=handle_client,
|
||||
args=(client_socket, client_address, socket_list, usernames, chat_rooms, client_states, user_paths),
|
||||
daemon=True
|
||||
)
|
||||
client_thread.start()
|
||||
except KeyboardInterrupt:
|
||||
print("\nShutting down server...")
|
||||
finally:
|
||||
server_socket.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue