server_udp_or_tcp.py
7.35 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
#!/usr/bin/env python3
# cf https://docs.python.org/3/library/socketserver.html#socketserver-tcpserver-example
"""Socket Server implementation
To be used as a minimalist telescope simulator to which a socket client (SocketClient) can connect
"""
# Standard library imports
import socketserver
# Third party imports
# None
# Local application imports
# None
class UnknownCommandException(Exception):
pass
###STAMP = '01000000'
#STAMP = '0100000000000000'
#HOST, PORT = "localhost", 11110
#HOST, PORT = "localhost", 9999
#HOST, PORT = "localhost", 20001
# stamp is 8 digits long
STAMP_LENGTH = 8
# COMMON CONSTANTS WITH CLIENT
TERMINATOR = '\x00'
COMMAND5 = '050000'
#COMMAND6_SIMPLE = '6'
COMMAND6 = '\x00\x06\x00'
class Memo:
@classmethod
def get(cls, var):
return cls._variables[var]
@classmethod
def set(cls, var, value):
cls._variables[var] = value
_variables = {
"C" : '10/01/18',
"L" : '10:20:35',
"g" : '+10',
"t" : '+45:00:00',
}
def get_SocketServer_UDP_TCP(myhost:str="localhost", myport:int=11110, PROTOCOL:str="TCP"):
socketserver_type = socketserver.UDPServer if PROTOCOL=="UDP" else socketserver.TCPServer
socketserver_handler_type = socketserver.DatagramRequestHandler if PROTOCOL=="UDP" else socketserver.StreamRequestHandler
"""
A classic version of request handler:
"""
class _MyUDPorTCPHandler_classic(socketserver.BaseRequestHandler):
# The request handler class for our server.
# It is instantiated once per connection to the server, and must
# override the handle() method to implement communication to the client
def handle(self):
# Get received data
# - TCP
if PROTOCOL == "TCP":
# For TCP, self.request is the TCP socket connected to the client
data_rcv = self.request.recv(1024).strip()
# - UDP
else:
# For UDP, self.request consists of a pair of data and client socket
data_rcv = self.request[0].strip()
socket_client = self.request[1]
print("{} wrote:".format(self.client_address[0]))
print(data_rcv)
# Send back the same data, but upper-cased
# - TCP
if PROTOCOL == "TCP":
self.request.sendall(data_rcv.upper())
# - UDP
# Since there is no connection, the client address must be given explicitly when sending data back via sendto().
else:
socket_client.sendto(data_rcv.upper(), self.client_address)
"""
An alternative request handler class that makes use of streams
(file-like objects that simplify communication by providing the standard file interface):
"""
#class MyTCPHandler(socketserver.StreamRequestHandler):
#class MyUDPHandler(socketserver.DatagramRequestHandler):
class _MyUDPorTCPHandler(socketserver_handler_type):
def get_useful_data(self, data_received):
return data_received
def make_answer_for_request(self, request_bytes:bytes):
#if request == STAMP + ":GD#": return STAMP + "+12:28"
#if request == b'0100000000000000:GD#': return bytes(STAMP + "+12:28", "utf-8")
print("Request received is", request_bytes)
'''
3 types of commands:
- TYPE1: '06' or '050000'
- TYPE2: ':cde#' (mainly ':??#' - i.e : ':GD#', ':GR#', ...)
- TYPE3: 'b?#' (bC# = Cold Start, bW# = selecting Warm Start, bR# = selecting Warm Restart)
'''
# Convert to string
request = request_bytes.decode("utf-8")
if len(request) < STAMP_LENGTH+2+1: raise UnknownCommandException(request)
# Remove TERMINATOR
request = request[0:-1]
# Remove leading stamp
stamp = request[0:STAMP_LENGTH]
command = request[STAMP_LENGTH:]
print("Command received is", repr(command))
# TYPE1
if command not in (COMMAND5, COMMAND6):
# TYPE2 or 3
if len(command) < 3: raise UnknownCommandException()
if not (command[-1]=='#'): raise UnknownCommandException()
if not (command[0]==':') and command not in ('bC#','bW#','bR#'): raise UnknownCommandException()
command_start = command[1:3]
if command == COMMAND6: answer = "G"
elif command == COMMAND5: answer = COMMAND5
#gr_request = STAMP + ':GR#' + END
#return bytes(stamp + "15:01:48#" + TERMINATOR, "utf-8")
elif command == ':GR#': answer = "15:01:48"
#gd_request = STAMP + ':GD#' + END
#if request == bytes(gd_request, "utf-8"): return bytes(STAMP + "+12:28#", "utf-8")
#elif useful_request == 'GD': answer = "+12:28"
elif command == ':GD#': answer = "+12:28"
elif command == ':SG+00#': answer = "1"
elif command == ':GG#': answer = "+00"
elif command_start == 'Gv': answer = 'T'
elif command_start in ('GC','GL', 'Gg', 'Gt'):
answer = Memo.get(command_start[1])
# Gemini telescope replaces "*" with ":"
if command_start in ('Gg','Gt'): answer = answer.replace('*',':')
else:
# Remove ending '#'
command = command[0:-1]
if command_start in ('SC', 'SL', 'Sg', 'St'): Memo.set(command_start[1],command[3:])
if command[0] == ':': command = command[1:]
answer = command.upper()
full_answer_in_bytes = bytes(stamp + answer + '#' + TERMINATOR, "utf-8")
#print("request str upper is", str(request).upper())
print("Answer sent is", full_answer_in_bytes)
return full_answer_in_bytes
def handle(self):
# 1) RECEIVE REQUEST from client
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
data_received = self.rfile.readline().strip() # data is "bytes" type
data_useful = self.get_useful_data(data_received)
#print("data type is", type(data))
print("\nFrom {}, received: {}".format(self.client_address[0], data_useful))
# 2) SEND REPLY to client
# Likewise, self.wfile is a file-like object used to write back to the client
self.wfile.write(self.make_answer_for_request(data_useful))
# inutile, ca plante car data est deja en bytes:
#self.wfile.write(data.upper().encode())
# Return a "classic" handler:
#return socketserver_type((HOST, PORT), _MyUDPorTCPHandler_classic)
# or a more "modern" handler:
return socketserver_type((myhost, myport), _MyUDPorTCPHandler)
if __name__ == "__main__":
#with socketserver_type((HOST, PORT), MyUDPorTCPHandler_classic) as myserver:
'''
with socketserver_type((HOST, PORT), MyUDPorTCPHandler) as myserver:
myserver.serve_forever()
'''
with get_SocketServer_UDP_TCP("localhost", 11110, "UDP") as myserver:
myserver.serve_forever()