-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
151 lines (125 loc) · 6.84 KB
/
server.py
File metadata and controls
151 lines (125 loc) · 6.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
from socket import *
from _thread import *
from threading import *
import time
CONNECTION_LIMIT = 10
serverSocket = socket(AF_INET, SOCK_STREAM)
serverPort = 5555
serverAddress = gethostbyname(gethostname()) # server ip
serverSocket.bind((serverAddress, serverPort))
serverSocket.listen(20)
clientList = {} # list of client (address,username)
# thread for one user connection
def userThread(connection, address, senderName):
pmUser = None
while True:
try:
# waiting for client message
message = connection.recv(1024).decode()
print(message)
if message:
senderName, msgType, messageContent = extractMsgHeader(message)
if msgType == 'cmd':
if messageContent == '/join':
clientList[connection]['isActive'] = True
connection.send(formatMessage('[system]',
"You have joined the public chatroom. Use '/esc' to exit. "
"Type your message below\r\n").encode())
notification = senderName + " (" + address[0] + ") has joined the chat room"
print(notification)
publish(notification, connection, "[system]")
elif messageContent == '/fetch': # display active users
activeList = "Active users:\r\n"
for user in clientList:
if clientList[user]['isActive']:
activeList += clientList[user]['username'] + " (" + clientList[user]['ip'] + "/" + str(clientList[user]['port']) + ")\r\n"
connection.send(formatMessage('[system]', activeList).encode())
elif messageContent.startswith('/connect'): # forward invitation
# clientList[connection]['isActive'] = False
messageContent = messageContent[1:].split('/')
ip = messageContent[1]
port = messageContent[2]
# find receiver in list
for conn in clientList:
#matching receiver
if clientList[conn]['ip'] == ip and clientList[conn]['port'] == int(port):
pmUser = (conn, clientList[conn]['username'])
forwdMsg = formatMessage('[system]',
senderName + "(" + address[0] + "/" + str(address[1]) + ") invited you to private "
"message.\r\nType "
"'/accept/ip/port' to accept.")
conn.send(forwdMsg.encode())
break
elif messageContent.startswith('/accept'):
messageContent = messageContent[1:].split('/')
ip = messageContent[1]
port = messageContent[2]
# find inviter
for conn in clientList:
if clientList[conn]['ip'] == ip and clientList[conn]['port'] == int(port):
clientList[conn]['isActive'] = False
clientList[connection]['isActive'] = False # disable public msg
pmUser = (conn, clientList[conn]['username'])
forwdMsg = formatMessage('[system]', '/accept', 'cmd')
conn.send(forwdMsg.encode())
publish(senderName + " has left.", connection, '[system]')
#publish(clientList[conn]['username'] + " has left!", conn, '[system]')
break
elif pmUser:
try:
pm = formatMessage(senderName, messageContent, 'pm')
pmUser[0].send(pm.encode())
except IOError:
pmUser = None
print(senderName+" couldn't connect to "+ pmUser[1])
else:
publish(messageContent, connection, senderName)
except IOError:
# remove user from chat room if there is an error/timeout
if connection in clientList:
if clientList[connection]['isActive']:
disconnectMsg = clientList[connection]["username"] + " has disconnected!"
publish(disconnectMsg, connection, "[system]")
print(disconnectMsg)
del clientList[connection]
def publish(message, connection, username):
for user in clientList:
if user != connection and clientList[user]["isActive"]:
try:
msgheader = formatMessage(username, message)
user.send(msgheader.encode())
except IOError:
if user in clientList.keys():
del clientList[user]
def formatMessage(username, message, msgType="msg"):
return "UNameL: " + str(len(username)) + "\r\nMessageL: " + str(
len(message)) + "\r\nMessageType: " + msgType + "\r\nUsername: " + username + "\r\nMessage: " + message
def extractMsgHeader(message):
message = message.split('\r\n', 4)
username = message[3].split()[1]
messageType = message[2].split()[1]
messageContent = message[4].split(' ', 1)[1]
return (username, messageType, messageContent)
def displayAllUsers():
while True:
time.sleep(30) # repeat every 30sec
if len(clientList):
print("Connected clients:")
for user in clientList:
print(clientList[user]['username']+" ("+clientList[user]['ip']+"/"+str(clientList[user]['port'])+")")
Thread(target=displayAllUsers).start() # periodically display client list
while True:
connection, cAddress = serverSocket.accept()
# check room capacity
if len(clientList) >= CONNECTION_LIMIT:
msg = "Server is full at the moment, please try again later!"
connection.send(format("[server]", msg).encode())
connection.close()
continue
unameMsg = connection.recv(1024).decode() # recv username
username, type, content = extractMsgHeader(unameMsg)
welcomeMsg = "Hi "+username+", welcome to chat server " + gethostname()
connection.send(formatMessage("[server]", welcomeMsg).encode())
clientList[connection] = {"username": username, "ip": cAddress[0], "port": cAddress[1], "isActive": False}
# create a new thread for each arriving connection
Thread(target=userThread, args=(connection, cAddress, username)).start()