server_udp_or_tcp.py
6.06 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
#!/usr/bin/env python3
# cf https://docs.python.org/3/library/socketserver.html#socketserver-tcpserver-example
# https://www.linuxtopia.org/online_books/programming_books/python_programming/python_ch36s06.html
"""Socket Server implementation
To be used as a minimalist telescope simulator to which a socket client (SocketClient) can connect
"""
# Standard library imports
import os
import socketserver
import sys
# Third party imports
# None
# Local application imports
# None
#from .client_channel import printd
'''
sys.path.append('..')
from device_controller.abstract_component.device_simulator import getc, getp
'''
def printd(*args, **kwargs):
if os.environ.get('PYROS_DEBUG', '0')=='1': print(*args, **kwargs)
# Very BASIC implementation
def make_answer_for_request_CMD_TO_UPPER(request_bytes):
#raise NotImplementedError
printd("(SIM serv) Request received is", request_bytes)
# 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:]
command = request
printd("(SIM serv) Command received is", repr(command))
answer = 'ANSWER TO '+command.upper()
#if command == ':GR#': answer = "15:01:49"
#elif command == ':GD#': answer = "+12:29"
#elif command == ':SG+00#': answer = "1"
#elif command == ':GG#': answer = "+00"
#full_answer_in_bytes = bytes(stamp + answer + '#' + TERMINATOR, "utf-8")
full_answer_in_bytes = bytes(answer, "utf-8")
#printd("request str upper is", str(request).upper())
printd("(SIM serv) Answer sent is", full_answer_in_bytes)
return full_answer_in_bytes
# Default BASIC function "make_answer_for_request" to be overriden by subclass
# Select the function we want to use by default
make_answer_for_request = make_answer_for_request_CMD_TO_UPPER
def get_SocketServer_UDP_TCP(myhost:str="localhost", myport:int=11110, PROTOCOL:str="TCP", func_make_answer_for_request=make_answer_for_request):
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]
printd("(SIM serv) {} wrote:".format(self.client_address[0]))
printd("(SIM serv)", 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):
##return self.make_answer_for_request_Gemini(request_bytes)
raise NotImplementedError
'''
# @override
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.request.recv(1024)
data_received = self.rfile.readline().strip() # data is "bytes" type
data_useful = self.get_useful_data(data_received)
#printd("data type is", type(data))
#printd("\nFrom {}, received: {}".format(self.client_address[0], data_useful))
printd(f"\n(SIM server_udp_or_tcp) From {self.client_address[0]}, received: {data_useful}\n")
# 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))
self.wfile.write(func_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", make_answer_for_request) as myserver:
with get_SocketServer_UDP_TCP("localhost", 11110, "UDP") as myserver:
# Handle requests in an infinite loop. Runs until the loop is broken with an exception
myserver.serve_forever()