-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathchat_server.py
More file actions
139 lines (113 loc) · 4.95 KB
/
chat_server.py
File metadata and controls
139 lines (113 loc) · 4.95 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
import select
import socket
import sys
import signal
import argparse
import ssl
from utils import *
SERVER_HOST = 'localhost'
class ChatServer(object):
""" An example chat server using select """
def __init__(self, port, backlog=5):
self.clients = 0
self.clientmap = {}
self.outputs = [] # list output sockets
self.context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
self.context.load_cert_chain(certfile="cert.pem", keyfile="cert.pem")
self.context.load_verify_locations('cert.pem')
self.context.set_ciphers('AES128-SHA')
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server.bind((SERVER_HOST, port))
self.server.listen(backlog)
self.server = self.context.wrap_socket(self.server, server_side=True)
# Catch keyboard interrupts
signal.signal(signal.SIGINT, self.sighandler)
print(f'Server listening to port: {port} ...')
def sighandler(self, signum, frame):
""" Clean up client outputs"""
print('Shutting down server...')
# Close existing client sockets
for output in self.outputs:
output.close()
self.server.close()
def get_client_name(self, client):
""" Return the name of the client """
info = self.clientmap[client]
host, name = info[0][0], info[1]
return '@'.join((name, host))
def run(self):
# inputs = [self.server, sys.stdin]
inputs = [self.server]
self.outputs = []
running = True
while running:
try:
readable, writeable, exceptional = select.select(
inputs, self.outputs, [])
except select.error as e:
break
for sock in readable:
sys.stdout.flush()
if sock == self.server:
# handle the server socket
client, address = self.server.accept()
print(
f'Chat server: got connection {client.fileno()} from {address}')
# Read the login name
cname = receive(client).split('NAME: ')[1]
# Compute client name and send back
self.clients += 1
send(client, f'CLIENT: {str(address[0])}')
inputs.append(client)
self.clientmap[client] = (address, cname)
# Send joining information to other clients
msg = f'\n(Connected: New client ({self.clients}) from {self.get_client_name(client)})'
for output in self.outputs:
send(output, msg)
self.outputs.append(client)
# elif sock == sys.stdin:
# # didn't test sys.stdin on windows system
# # handle standard input
# cmd = sys.stdin.readline().strip()
# if cmd == 'list':
# print(self.clientmap.values())
# elif cmd == 'quit':
# running = False
else:
# handle all other sockets
try:
data = receive(sock)
if data:
# Send as new client's message...
msg = f'\n#[{self.get_client_name(sock)}]>> {data}'
# Send data to all except ourself
for output in self.outputs:
if output != sock:
send(output, msg)
else:
print(f'Chat server: {sock.fileno()} hung up')
self.clients -= 1
sock.close()
inputs.remove(sock)
self.outputs.remove(sock)
# Sending client leaving information to others
msg = f'\n(Now hung up: Client from {self.get_client_name(sock)})'
for output in self.outputs:
send(output, msg)
except socket.error as e:
# Remove
inputs.remove(sock)
self.outputs.remove(sock)
self.server.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Socket Server Example with Select')
parser.add_argument('--name', action="store", dest="name", required=True)
parser.add_argument('--port', action="store",
dest="port", type=int, required=True)
given_args = parser.parse_args()
port = given_args.port
name = given_args.name
server = ChatServer(port)
server.run()