Add usernames and command messages

This commit is contained in:
Kayomn 2021-10-01 00:16:27 +01:00
parent e95e2e984d
commit c6266c1cae
3 changed files with 89 additions and 39 deletions

View File

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

View File

@ -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

View File

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