-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
217 lines (195 loc) · 10.1 KB
/
server.py
File metadata and controls
217 lines (195 loc) · 10.1 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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
import socket
import threading
# Globals
clients = [] # List of connected client sockets
message_boards = {f"g{i}": [] for i in range(1, 6)} # Dictionary to store messages for each group (g1 to g5)
message_board = [] # List of messages for the public message board
client_usernames = {} # Dictionary to map client sockets to their usernames
client_groups = {} # Dictionary to map client sockets to the groups they have joined
# Lock for thread-safe operations
lock = threading.Lock()
# Broadcast a message to all connected clients in the public message board
def broadcast_message(message):
with lock:
for client in clients:
try:
# Send the message to the client
client.send(message.encode('utf-8'))
except:
# If an error occurs (e.g., client disconnected), ignore and continue
pass
# Broadcast a message to all connected clients in a specific group
def broadcast_messages(message, group):
with lock:
print(f"Broadcasting to group {group}: {message}") # Debug message
for client, groups in client_groups.items():
# Check if the client has joined the specified group
if group in groups:
print(f"Sending to client {client}") # Debug message
try:
# Send the message to the client
client.send(f"{message}\n".encode('utf-8'))
except Exception as e:
# If an error occurs, print the error and continue
print(f"Error sending to {client}: {e}")
# Handle individual client communication
def handle_client(client_socket):
try:
# Receive username from the client
username = client_socket.recv(1024).decode('utf-8')
with lock:
# Store the username associated with the client socket
client_usernames[client_socket] = username
clients.append(client_socket)
# Notify all clients in the public group that a new user has joined
broadcast_message(f"{username} has joined the public group.")
history = "Last 2 messages:\n"
with lock:
for msg in message_board[-2:]:
history += msg + "\n"
client_socket.send(history.encode('utf-8'))
# Communication loop: listen for messages from the client
while True:
# Receive a message from the client
message = client_socket.recv(1024).decode('utf-8')
if not message:
# If no message is received, client has disconnected
break
# Check for special commands
if message.strip() == "%leave":
with lock:
if client_socket in clients:
clients.remove(client_socket)
username = client_usernames.pop(client_socket, "Unknown")
# Client wants to leave the public group
broadcast_message(f"{username} has left the public group.")
client_socket.close()
break
elif message.strip() == "%users":
# Client requests the list of users
with lock:
users = [client_usernames[client] for client in clients]
client_socket.send(f"Users: {', '.join(users)}".encode('utf-8'))
elif message.strip() == "%groups":
# Client requests the list of groups
with lock:
groups = list(message_boards.keys())
client_socket.send(f"Groups: {', '.join(groups)}".encode('utf-8'))
elif message.startswith("%groupjoin"):
# Client wants to join one or more groups
_, groups = message.split(maxsplit=1)
selected_groups = groups.split(',')
with lock:
if client_socket not in client_groups:
# If client is not in any groups yet, add the selected groups
client_groups[client_socket] = selected_groups
else:
# Add the selected groups to the client's group list if not already present
for group in selected_groups:
if group not in client_groups[client_socket]:
client_groups[client_socket].append(group)
# Notify all clients in the selected groups that the user has joined
for group in selected_groups:
broadcast_messages(f"{username} has joined {group}.", group)
elif message.startswith("%grouppost"):
# Client wants to post a message to a group
_, group, content = message.split(maxsplit=2)
with lock:
if group not in client_groups.get(client_socket, []):
client_socket.send(f"You are not a member of {group}".encode('utf-8'))
if group in client_groups.get(client_socket, []):
message_id = len(message_boards[group]) # Use the length of the group's message board as the message ID
formatted_message = f"Message from {username} in {group} with ID {message_id}: {content}"
message_boards[group].append(formatted_message)
if group in client_groups.get(client_socket, []):
broadcast_messages(formatted_message, group)
elif message.startswith("%groupusers"):
# Client requests the list of users in a group
_, group = message.split(maxsplit=1)
with lock:
if group in client_groups.get(client_socket, []):
# If the client is a member of the group
users_in_group = [
client_usernames[client]
for client in client_groups
if group in client_groups[client]
]
client_socket.send(f"Users in {group}: {', '.join(users_in_group)}".encode('utf-8'))
else:
# If the client is not a member of the group
client_socket.send(f"You are not a member of {group}".encode('utf-8'))
elif message.startswith("%groupmessage"):
# Client requests a specific message from a group's message board
_, group, message_id = message.split(maxsplit=2)
with lock:
if group in client_groups.get(client_socket, []):
if int(message_id) < len(message_boards[group]):
# If the message ID is valid
client_socket.send(message_boards[group][int(message_id)].encode('utf-8'))
else:
# If the message ID is invalid
client_socket.send("Message ID not found.".encode('utf-8'))
else:
# If the client is not a member of the group
client_socket.send(f"You are not a member of {group}".encode('utf-8'))
elif message.startswith("%groupleave"):
# Client wants to leave a group
_, group = message.split(maxsplit=1)
with lock:
if group in client_groups.get(client_socket, []):
client_groups[client_socket].remove(group)
broadcast_messages(f"{username} has left {group}.", group)
elif message.startswith("%message"):
# Client requests a specific message from the public message board
_, message_id = message.split(maxsplit=1)
with lock:
if int(message_id) < len(message_board):
# If the message ID is valid
client_socket.send(message_board[int(message_id)].encode('utf-8'))
else:
# If the message ID is invalid
client_socket.send("Message ID not found.".encode('utf-8'))
else:
# Treat as a regular message to be added to the public message board
message_id = len(message_board) # Use the length of the message board as the message ID
formatted_message = f"Message from {username} with ID: {message_id}: {message}"
with lock:
# Add the message to the public message board
message_board.append(formatted_message)
# Broadcast the message to all clients in the public message board
broadcast_message(formatted_message)
except:
# Handle any exceptions that occur in the communication loop
pass
finally:
# When the client disconnects or an error occurs
# Remove client from the list
with lock:
if client_socket in clients:
clients.remove(client_socket)
if client_socket in client_usernames:
# Remove the client's username
username = client_usernames.pop(client_socket, "Unknown")
# Notify all clients in the public group that the user has left
broadcast_message(f"{username} has left the group.")
# Close the client socket
client_socket.close()
# Start the server
def main():
host = 'localhost'
port = 8080
# Create a TCP/IP socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind the socket to the address and port
server_socket.bind((host, port))
# Listen for incoming connections, with a maximum backlog of 5
server_socket.listen(5)
print(f"Server is running on {host}:{port}...")
while True:
# Accept a new connection
client_socket, client_address = server_socket.accept()
print(f"New connection from {client_address}")
# Start a new thread to handle the client
threading.Thread(target=handle_client, args=(client_socket,), daemon=True).start()
if __name__ == "__main__":
main()