Add usernames and command messages
This commit is contained in:
parent
e95e2e984d
commit
c6266c1cae
21
client.py
21
client.py
|
@ -3,11 +3,13 @@ __version__ = "0.0.1"
|
|||
__status__ = "Development"
|
||||
|
||||
if (__name__ == "__main__"):
|
||||
import chattle
|
||||
import config
|
||||
import socket
|
||||
import select
|
||||
import sys
|
||||
|
||||
username = input("username: ")
|
||||
address = (config.host, config.port)
|
||||
|
||||
print("Starting connection to", address)
|
||||
|
@ -16,20 +18,31 @@ if (__name__ == "__main__"):
|
|||
server_socket.setblocking(False)
|
||||
server_socket.connect_ex(address)
|
||||
|
||||
# Input may be written by the user or received from the server. Both cases have to be handled.
|
||||
inputs = [sys.stdin, server_socket]
|
||||
|
||||
while True:
|
||||
# Listen for input.
|
||||
readable_io, _, _ = select.select(inputs, [], [])
|
||||
|
||||
for io in readable_io:
|
||||
if (io == server_socket):
|
||||
print(io.recv(4096))
|
||||
response_data = io.recv(config.message_max)
|
||||
|
||||
if not response_data:
|
||||
exit(0)
|
||||
|
||||
print(response_data.decode("utf-8"))
|
||||
|
||||
elif (io == sys.stdin):
|
||||
message = sys.stdin.readline()
|
||||
# Messages produced by this client are written to the terminal locally, rather than sending it to
|
||||
# server to then receive it back.
|
||||
line = sys.stdin.readline()
|
||||
|
||||
server_socket.send(message.encode("utf-8"))
|
||||
if not line.startswith("/"):
|
||||
sys.stdout.write("<You> ")
|
||||
sys.stdout.write(message)
|
||||
sys.stdout.write(line)
|
||||
sys.stdout.flush()
|
||||
|
||||
server_socket.send(chattle.encode_message(username, line))
|
||||
|
||||
|
|
|
@ -2,5 +2,7 @@ __author__ = "Kieran Osborne"
|
|||
__version__ = "0.0.1"
|
||||
__status__ = "Development"
|
||||
|
||||
server_greeting = "Welcome to Chattle"
|
||||
message_max = 4096
|
||||
host = "127.0.0.1"
|
||||
port = 12345
|
||||
|
|
91
server.py
91
server.py
|
@ -4,59 +4,94 @@ __status__ = "Development"
|
|||
|
||||
if (__name__ == "__main__"):
|
||||
import threading
|
||||
import chattle
|
||||
import config
|
||||
import socket
|
||||
|
||||
# Sockets don't automatically close on their own, making a with statement is necessary.
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket:
|
||||
# Avoid bind() exception: OSError: [Errno 48] Address already in use
|
||||
# Avoid "bind() exception: OSError: [Errno 48] Address already in use" error
|
||||
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
client_socket.bind((config.host, config.port))
|
||||
client_socket.listen(100)
|
||||
client_socket.listen()
|
||||
|
||||
clients = []
|
||||
clients = set()
|
||||
|
||||
def spawn_client(client_connection, client_address):
|
||||
client_connection.send("Welcome to this chatroom!".encode("utf-8"))
|
||||
def broadcast(broadcasting_connection, message: str) -> None:
|
||||
"""
|
||||
Handles requests from a client to broadcast a message to all other connected clients but the one requesting
|
||||
the broadcast.
|
||||
|
||||
try:
|
||||
print("Waiting for data")
|
||||
data = client_connection.recv(4096)
|
||||
|
||||
while data:
|
||||
message = f"<{client_address[0]}> {data}"
|
||||
:param broadcasting_connection: Originating connection that requested the message be broadcast.
|
||||
:param message: Message requested to be broadcast.
|
||||
:return: Nothing.
|
||||
"""
|
||||
|
||||
print(message)
|
||||
print("Propagating to clients")
|
||||
|
||||
for client in clients:
|
||||
if client != client_connection:
|
||||
# No point broadcasting the message that the client sent the server back to the same client, it already
|
||||
# has its own local copy since it was the one that wrote it.
|
||||
if client != broadcasting_connection:
|
||||
try:
|
||||
client.send(message.encode("utf-8"))
|
||||
|
||||
except:
|
||||
except socket.error:
|
||||
# Remove clients who's connections to the server have been broken
|
||||
client.close()
|
||||
clients.discard(client)
|
||||
|
||||
# if the link is broken, we remove the client
|
||||
if client_connection in clients:
|
||||
clients.remove(client_connection)
|
||||
def handle_client(client_connection, client_address) -> None:
|
||||
"""
|
||||
Handles a single client connection, awaiting and processing input from it as and when it comes.
|
||||
|
||||
print("Finished propagating")
|
||||
:param client_connection: Client connection.
|
||||
:param client_address: Client IP and port address tuple.
|
||||
:return: Nothing.
|
||||
"""
|
||||
|
||||
print("Waiting for data")
|
||||
data = client_connection.recv(4096)
|
||||
client_ip = client_address[0]
|
||||
|
||||
if client_connection in clients:
|
||||
print("dead")
|
||||
clients.remove(client_connection)
|
||||
# Roll out the red carpet.
|
||||
client_connection.send(config.server_greeting.encode("utf-8"))
|
||||
|
||||
try:
|
||||
# Handle each incoming message from the connected client.
|
||||
data = client_connection.recv(config.message_max)
|
||||
|
||||
while data:
|
||||
message = chattle.Message(data)
|
||||
command = message.as_command()
|
||||
|
||||
if (command):
|
||||
# It's a command message!
|
||||
if (command == "quit"):
|
||||
clients.discard(client_connection)
|
||||
broadcast(client_connection, client_ip + " disconnected")
|
||||
client_connection.close()
|
||||
|
||||
except:
|
||||
return
|
||||
|
||||
else:
|
||||
client_connection.send(f"Unknown command: {command}".encode("utf-8"))
|
||||
|
||||
else:
|
||||
# It's a regular message
|
||||
broadcast(client_connection, f"<{message.author}@{client_ip}> {message.body}")
|
||||
|
||||
data = client_connection.recv(config.message_max)
|
||||
|
||||
except socket.timeout:
|
||||
print(client_ip + " timed out")
|
||||
client_connection.close()
|
||||
clients.discard(client_connection)
|
||||
|
||||
while True:
|
||||
# Handle each incoming connection as and when they come.
|
||||
connection, address = client_socket.accept()
|
||||
|
||||
clients.append(connection)
|
||||
|
||||
# prints the address of the user that just connected
|
||||
clients.add(connection)
|
||||
print(address[0] + " connected")
|
||||
threading.Thread(target=spawn_client, args=(connection, address)).start()
|
||||
# Once a connection has been received it can be sent off to be handled elsewhere and this loop continues to
|
||||
# handle connecting users.
|
||||
threading.Thread(target=handle_client, args=(connection, address)).start()
|
||||
|
|
Loading…
Reference in New Issue