· 6 years ago · Dec 07, 2019, 05:18 PM
1import sqlite3
2import socket
3import select
4import time
5import sys
6
7IP = "127.0.0.1"
8PORT = 3004
9
10conn = sqlite3.connect('users.db')
11db = conn.cursor()
12db.execute("""CREATE TABLE IF NOT EXISTS user_data (
13 username TEXT type UNIQUE,
14 password TEXT,
15 ip_address TEXT,
16 port INTEGER
17 )""")
18
19# Create a socket
20server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
22server_socket.bind((IP, PORT))
23server_socket.listen()
24
25sockets_list = [server_socket]
26clients = {}
27
28print(f'Listening for connections on {IP}:{PORT}...')
29
30
31# Handles message receiving
32def receive_username(client_socket):
33
34 try:
35 login = client_socket.recv(1024).decode("utf-8")
36 login = login.split('+/*')
37 time.sleep(2)
38
39 db.execute("INSERT INTO user_data VALUES(?, ?, ?, ?)", (login[0], login[1], client_address[0], client_address[1]))
40 conn.commit()
41
42 # Return an object of message data
43 return login[0]
44
45 except:
46 db.execute('''DELETE FROM user_data WHERE port=?''', (client_address[1],))
47 conn.commit()
48 return False
49
50
51def receive_message(client_socket):
52
53 try:
54 username_message = client_socket.recv(1024).decode("utf-8")
55 username_message = username_message.split('+/*')
56
57 # Return an object of message data
58 return {'username':username_message[0], 'message':username_message[1]}
59
60 except:
61 db.execute('''DELETE FROM user_data WHERE port=?''', (client_address[1],))
62 conn.commit()
63 return False
64
65
66def send_message(clients, username_message):
67
68 for client_socket in clients:
69
70 # But don't sent it to sender
71 if client_socket != notified_socket:
72 # Send user and message
73 client_socket.send(
74 username_message['username'].encode("utf-8") + '+/*'.encode('utf-8') + username_message['message'].encode("utf-8"))
75 print(f"Sent message from {username_message['username']} saying {username_message['message']} to everyone")
76
77
78def private_message(username_message):
79 split_message = username_message['message'].split()
80 print(split_message)
81 receiving_member = split_message[1]
82 print(receiving_member)
83 split_message.pop(0)
84 split_message.pop(0)
85 print(split_message)
86 message = username_message['username'] + '+/*' + ' '.join(split_message)
87 print(message)
88 print(client_address)
89 server_socket.sendto(message.encode('utf-8'), (client_address))
90
91
92
93
94while True:
95
96 # Calls Unix select() system call or Windows select() WinSock call with three parameters:
97 # - rlist - sockets to be monitored for incoming data
98 # - wlist - sockets for data to be sent to (checks if for example buffers are not full and socket is ready to send some data)
99 # - xlist - sockets to be monitored for exceptions (we want to monitor all sockets for errors, so we can use rlist)
100 # Returns lists:
101 # - reading - sockets we received some data on (that way we don't have to check sockets manually)
102 # - writing - sockets ready for data to be send through
103 # - errors - sockets with some exceptions
104 # This is a blocking call, code execution will "wait" here and "get" notified in case any action should be taken
105 read_sockets, _, exception_sockets = select.select(sockets_list, [], sockets_list)
106
107 # Iterate over notified sockets
108 for notified_socket in read_sockets:
109
110 # If notified socket is a server socket - new connection, accept it
111 if notified_socket == server_socket:
112
113 # Accept new connection
114 # That gives us new socket - client socket, connected to this given client only, it's unique for that client
115 # The other returned object is ip/port set
116 client_socket, client_address = server_socket.accept()
117
118 # Client should send his name right away, receive it
119 username = receive_username(client_socket)
120
121 # If False - client disconnected before they sent their username.
122 if username is False:
123 print(f"Closed connection from: {clients[notified_socket]['data']}")
124 db.execute('''DELETE FROM user_data WHERE port=?''', (client_address[1],))
125 conn.commit()
126 continue
127
128 # Add accepted socket to select.select() list
129 sockets_list.append(client_socket)
130
131 # Also save username
132 clients[client_socket] = username
133
134 print(f"Accepted new connection from {client_address}, their username is {username}'")
135
136 # Else existing socket is sending a message
137 else:
138
139 # Receive message
140 username_message = receive_message(notified_socket)
141
142 # If False means client disconnected
143 if username_message['message'] is False:
144 print(f"Closed connection from: {clients[notified_socket]['data']}")
145
146 # Remove from list for socket.socket()
147 sockets_list.remove(notified_socket)
148
149 # Remove from our list of users
150 del clients[notified_socket]
151
152 db.execute('''DELETE FROM user_data WHERE port=?''', (client_address[1],))
153 conn.commit()
154
155 continue
156
157
158 # Get user by notified socket, so we will know who sent the message
159 # username = clients[notified_socket]
160 print(f"Received message from {username_message['username']}:{username_message['message']}")
161
162 # Iterate over connected clients and broadcast message
163 if username_message['message'].startswith('/msg'):
164 private_message(username_message)
165 else:
166 send_message(clients, username_message)
167
168 # It's not really necessary to have this, but will handle some socket exceptions just in case
169 for notified_socket in exception_sockets:
170
171 # Remove from list for socket.socket()
172 sockets_list.remove(notified_socket)
173
174 # Remove from our list of users
175 del clients[notified_socket]
176
177 db.execute('''DELETE FROM user_data WHERE port=?''', (client_address[1],))
178 conn.commit()