commit rest of old client/server fixes

This commit is contained in:
Xevion
2021-01-08 18:52:25 -06:00
parent b792d9cb9c
commit ab1c14adb5
7 changed files with 224 additions and 32 deletions

View File

@@ -100,7 +100,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.client.send(helpers.prepare_json(
{
'type': constants.Types.MESSAGE,
'content': message
'content': message.strip()
}
))

10
constants.py Normal file
View File

@@ -0,0 +1,10 @@
class Requests:
REQUEST_NICK = 'REQUEST_NICK'
REFRESH_CONNECTIONS_LIST = 'REFRESH_CLIENT_LIST'
GET_HISTORY = 'GET_HISTORY'
class Types:
REQUEST = 'REQUEST'
NICKNAME = 'NICKNAME'
USER_LIST = 'USER_LIST'
MESSAGE = 'MESSAGE'

View File

@@ -1,7 +1,19 @@
import json
HEADER_LENGTH = 10
def prepare(message: str, encoding='ascii') -> bytes:
def prepare(message: str, encoding='utf-8') -> bytes:
"""Prepares a message for sending through a socket by adding a proper header and encoding it."""
header = f'{len(message):<{HEADER_LENGTH}}'
return (header + message).encode(encoding)
def prepare_json(object) -> bytes:
"""
Prepares a object for sending as encoded JSON with a header.
:param object: A JSON-encodable object
:return: Encoded JSON
"""
return prepare(json.dumps(object))

View File

@@ -0,0 +1,8 @@
from PyQt5.QtWidgets import QApplication
from client.gui import MainWindow
app = QApplication([])
app.setApplicationName("TCPChat Client")
m = MainWindow()
app.exec_()

View File

@@ -1,6 +1,7 @@
import socket
import threading
import constants
import helpers
from config import config
@@ -15,9 +16,9 @@ client.connect(('127.0.0.1', 55555))
def receive():
while True:
try:
length = int(client.recv(HEADER_LENGTH).decode('ascii'))
message = client.recv(length).decode('ascii')
if message == 'NICK':
length = int(client.recv(HEADER_LENGTH).decode('utf-8'))
message = client.recv(length).decode('utf-8')
if message == constants.Requests.REQUEST_NICK:
client.send(helpers.prepare(nickname))
else:
print(message)
@@ -30,7 +31,7 @@ def receive():
# Sending Messages To Server
def write():
while True:
message = '{}: {}'.format(nickname, input(''))
message = input('> ')
client.send(helpers.prepare(message))

View File

@@ -1,6 +1,11 @@
import json
import socket
import threading
import time
import traceback
import uuid
import constants
import helpers
from config import config
@@ -15,35 +20,60 @@ server.bind((host, port))
server.listen()
# Lists For Clients and Their Nicknames
clients = []
nicknames = []
clients = {}
# Sending Messages To All Connected Clients
def broadcast(message):
print(f'Broadcasting: "{message}"')
encoded = helpers.prepare(message)
for client in clients:
client.send(encoded)
print(f'"{message}"')
encoded = helpers.prepare_json({
'type': constants.Types.MESSAGE,
'content': message
})
for client in clients.values():
client['client'].send(encoded)
# Handling Messages From Clients
def handle(client):
def handle(client_id):
client = clients[client_id]['client']
nickname = clients[client_id]['nickname']
while True:
try:
# Broadcasting Messages
length = int(client.recv(HEADER_LENGTH).decode('ascii'))
message = client.recv(length).decode('ascii')
broadcast(message)
length = int(client.recv(HEADER_LENGTH).decode('utf-8'))
message = json.loads(client.recv(length).decode('utf-8'))
if message['type'] == constants.Types.REQUEST:
if message['request'] == constants.Requests.REFRESH_CONNECTIONS_LIST:
client.send(helpers.prepare_json(
{
'type': constants.Types.USER_LIST,
'users': [
clients[other]['nickname'] for other in clients.keys() if other != client_id
]
}
))
elif message['type'] == constants.Types.NICKNAME:
nickname = message['nickname']
if not clients[client_id]['has_nickname']:
print("Nickname is {}".format(nickname))
broadcast("{} joined!".format(nickname))
clients[client_id]['has_nickname'] = True
else:
print(f'{clients[client_id]["nickname"]} changed their name to {nickname}')
clients[client_id]['nickname'] = nickname
elif message['type'] == constants.Types.MESSAGE:
broadcast(f'<{nickname}>: {message["content"]}')
except:
# Removing And Closing Clients
index = clients.index(client)
clients.remove(client)
client.close()
nickname = nicknames[index]
del clients[client_id]
broadcast('{} left!'.format(nickname))
nicknames.remove(nickname)
break
@@ -52,22 +82,33 @@ def receive():
while True:
# Accept Connection
client, address = server.accept()
print("Connected with {}".format(str(address)))
print("New Client from {}".format(str(address)))
# Request And Store Nickname
client.send(helpers.prepare('NICK'))
length = int(client.recv(HEADER_LENGTH).decode('ascii'))
nickname = client.recv(length).decode('ascii')
nicknames.append(nickname)
clients.append(client)
client_id = str(uuid.uuid4())
client.send(helpers.prepare_json(
{
'type': constants.Types.REQUEST,
'request': constants.Requests.REQUEST_NICK
}
))
# Print And Broadcast Nickname
print("Nickname is {}".format(nickname))
broadcast("{} joined!".format(nickname))
client.send(helpers.prepare('Connected to server!'))
clients[client_id] = {
'client': client,
'nickname': client_id[:10],
'first_seen': int(time.time()),
'has_nickname': False
}
client.send(helpers.prepare_json(
{
'type': constants.Types.MESSAGE,
'content': 'Connected to server!'
}
))
# Start Handling Thread For Client
thread = threading.Thread(target=handle, args=(client,))
thread = threading.Thread(target=handle, args=(client_id,))
thread.start()

120
server/MainWindow.ui Normal file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>651</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Connections</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QTextEdit" name="messageBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>80</height>
</size>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTextBrowser" name="messageHistory">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>5</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QListWidget" name="connectionsList">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Chat History</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionConnect_to"/>
<addaction name="actionSave_chat_to"/>
<addaction name="actionQuit"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionConnect_to">
<property name="text">
<string>Connect to...</string>
</property>
</action>
<action name="actionSave_chat_to">
<property name="text">
<string>Export chat...</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>