#!/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()