-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimpleSocket.py
More file actions
240 lines (173 loc) · 8.42 KB
/
Copy pathsimpleSocket.py
File metadata and controls
240 lines (173 loc) · 8.42 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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
from traceback import format_exc
import threading
import socket
import time
class Server:
def __init__(
self,
ip="127.0.0.1",
port=1337,
interpreter=print,
splitCmdStr=None,
splitArgStr=None,
readLen=1024,
retryAddress=True,
autoDisconnect=False,
welcomeString=b"Valkommen",
clientAgnostic=True
):
self.ip = ip
self.port = port
self.socket = None
self.clients = []
self.autoDisconnect = autoDisconnect # whether to disconnect the client after receiving the first messages (REST-esque)
self.welcomeString = welcomeString # the string to send to the client on connection
self.retryAddress = retryAddress # whether to retry binding to the address if binding fails
self.interpreter = interpreter # where to send the (parsed) data
self.splitCmdStr = splitCmdStr # delimeter used to separate commands (ex. "\n")
self.splitArgStr = splitArgStr # delimiter used to separate arguments (ex. " ")
self.readLen = readLen # how many bytes to read from the stream at once
if clientAgnostic: # if the client should be included to the interpreter
self.recver = self.recver_clientAgnostic
def accepter(self):
# loop to continuously accept new incoming connections
connected = False
while not connected:
try:
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.bind((self.ip, self.port))
self.socket.listen(5) # https://docs.python.org/3/library/socket.html#socket.socket.listen
print("receiver started\n") # redundant new line for thread safer printing
connected = True
except OSError as error:
if self.retryAddress:
print("Address already in use. Waiting 4 seconds.")
time.sleep(4)
else:
raise error # propagate
while True:
try:
client, address = self.socket.accept()
self.clients.append(client)
if self.welcomeString: client.send(self.welcomeString)
print(f"Connection from {address} has been accepted.")
self.threcver(client)
except OSError: # from previous testing, "self.socket.accept()" throws an OSError when the socket is shutdown, so we want to exit quietely.
pass
def recver(self, client):
while True:
try:
received = client.recv(self.readLen)
if received:
self.process(received, client)
if self.autoDisconnect:
try: self.clients.remove(client)
except Exception: pass
client.close()
return
except Exception as e:
try: peername = client.getpeername()
except Exception: peername = client
if client:
print(f"Error trying to receive from {peername}. Stopping receiver.")
try: self.clients.remove(client)
except Exception: pass
client.close()
else: print(f"Encountered error in receiver: {str(e)}")
return
def recver_clientAgnostic(self, client):
while True:
try:
received = client.recv(self.readLen)
if received:
self.process(received)
if self.autoDisconnect:
try: self.clients.remove(client)
except Exception: pass
client.close()
return
except Exception as e:
try: peername = client.getpeername()
except Exception: peername = client
print(format_exc())
if client:
print(f"Error trying to receive from {peername}. Stopping receiver.")
try: self.clients.remove(client)
except Exception: pass
client.close()
else: print(f"Encountered error in receiver: {str(e)}")
return
def threcver(self, socket):
threading.Thread(target=self.recver, args=[socket], daemon=True).start()
def sendAll(self, payload: bytes): # send "payload" to all connected clients
for client in self.clients:
try:
client.sendall(payload)
except Exception:
try: peername = client.getpeername()
except Exception: peername = None
print(f"Error trying to send to {peername or client}. Shutting Down.")
self.clients.remove(client) # this order of removing and closing client is used to
client.close() # ensure that a broken client doesn't stay in self.clients
def start(self):
threading.Thread(target=self.accepter, daemon=True).start()
def process(self, msg, *client): # processes received data
if self.interpreter: # if no interpreter is given, discard the data
if self.splitCmdStr:
for cmd in msg.split(self.splitCmdStr):
if self.splitArgStr:
args = cmd.split(self.splitArgStr)
self.interpreter(*args)
else: self.interpreter(cmd)
elif self.splitArgStr: self.interpreter(*msg.split(self.splitArgStr))
else: self.interpreter(*(msg, *client))
def close(self): # A function to properly close down self.socket
try:
self.socket.shutdown(socket.SHUT_RDWR) # close the socket for receiving and sending
except OSError:
pass # this happens sometimes
self.socket.close()
class Client:
def __init__(self, ip="127.0.0.1", port=1337, interpreter=print, readLen=10124, retryAddress=True):
self.ip = ip
self.port = port
self.interpreter = interpreter
self.server = None
self.retryAddress = retryAddress
self.readLen = readLen
def connect(self):
connected = False
while not connected:
try:
self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server.connect((self.ip, self.port))
print("connected\n")
connected = True
except ConnectionRefusedError as error:
if self.retryAddress:
print("Could not connect to server. Waiting 4 seconds.")
time.sleep(4)
else:
raise error
self.threcver(self.server)
def recver(self, client):
while True:
try:
received = client.recv(self.readLen)
if received and self.interpreter:
self.interpreter(received.decode())
except Exception as e:
try: peername = client.getpeername()
except Exception: peername = None
if client: print(f"Error trying to receive from {peername}. Stopping receiver.")
else: print(f"Encountered error in receiver: {str(e)}")
client.close()
return
print(f"Encountered error: {str(e)}")
print(f"Error trying to receive from {client.getpeername()}. Shutting Down.")
client.close()
return
def threcver(self, socket):
threading.Thread(target=self.recver, args=[socket], daemon=True).start()
def send(self, payload):
self.server.send(payload)