def _protocol_client_ping_send(self): """ Must send a client ping """ with self._protocol_lock: # Reset (we are called by it) self._ping_greenlet = None # Schedule a timeout self._schedule_client_pingservertimeout() # Timestamp self.dt_last_ping_send = SolBase.datecurrent() # Send a ping b = self.send_unicode_to_socket("C.PING") if not b: # Stat logger.error("send failed, fatal, disconnecting") Meters.aii("ping.client.client_send_error") # Disconnect self.disconnect() else: # Stats Meters.aii("ping.client.client_ping_sent")
def _protocol_server_ping_send(self): """ Must send a client ping """ with self._protocolLock: # Reset (we are called by it) self._pingGreenlet = None # Schedule a timeout self._schedule_server_ping_client_timeout() # Timestamp self.dtLastPingSend = SolBase.datecurrent() # Send a ping b = self.send_unicode_to_socket("S.PING") if not b: # Stat logger.error("send failed, fatal, disconnecting") Meters.aii("ping.server.serverSendError") # Disconnect self.stop_asynch() else: # Stats Meters.aii("ping.server.serverPingSent")
def start(self): """ Connect to server. :return Return true upon success. """ # Timestamp self.dtClientConnect = SolBase.datecurrent() # Call base b = TcpServerQueuedClientContext.start(self) if not b: # Stat logger.error("start failed, fatal, exiting") Meters.aii("ping.server.serverStartError") # Exit return False # Stat Meters.aii("ping.server.serverStartCount") # Schedule an hello timeout self._schedule_client_hello_timeout() return True
def connect(self): """ Connect to server. :return Return true upon success. """ # Stats Meters.aii("ping.client.client_connect_count") # Call base dt_start = SolBase.datecurrent() b = TcpSimpleClient.connect(self) if not b: # Stat logger.error("PingSimpleClient : connect failed, fatal, exiting") Meters.aii("ping.client.client_connect_error") # Exit return False # Stat Meters.dtci("ping.client.delay_client_connect", SolBase.datediff(dt_start)) # SSL stats ms = self._get_ssl_handshake_ms() if ms: Meters.dtci("ping.client.delay_client_sslhandshake", ms) # Send hello self._protocol_client_hello_send() return True
def _start_all(self, server_config): """ Start server and client. :param server_config: Server config. :return: tuple pysoltcp.tcpserver.TcpServer.TcpServer,pysoltcp.tcp_client.TcpSimpleClient.TcpSimpleClient :rtype tuple """ # Allocate self.tcp_server = TcpServer(server_config) # Check self.assertIsNotNone(self.tcp_server) self.assertFalse(self.tcp_server._is_started) self.assertTrue(self.tcp_server._server is None) # Start self.assertTrue(self.tcp_server.start_server()) self.assertTrue(self.tcp_server._is_started) self.assertFalse(self.tcp_server._server is None) # Client config client_config = TcpClientConfig() client_config.target_addr = "127.0.0.1" client_config.target_port = 3201 client_config.debug_log = True # Client self.tcp_client = TcpSimpleClient(client_config) # Check self.assertTrue(self.tcp_client.current_socket is None) self.assertTrue(not self.tcp_client.is_connected) # Connect logger.info("Starting connect()") self.assertTrue(self.tcp_client.connect()) logger.info("Starting connect() : done") # Check client self.assertIsNotNone(self.tcp_client.current_socket) self.assertTrue(self.tcp_client.is_connected) # Wait for server logger.info("TestLog : server : wait connection") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOut: if len(self.tcp_server._client_connected_hash) > 0: break SolBase.sleep(int(self.checkTimeOut / 100)) logger.info("TestLog : server : wait connection : done") # Check self.assertEqual(len(self.tcp_server._client_connected_hash), 1) # Ok logger.info("Started and connected, effectiveMs=%s", self.tcp_server.get_effective_controlinterval_ms()) return self.tcp_server, self.tcp_client
def test_date(self): """ Test """ dt = SolBase.datecurrent() SolBase.sleep(100) # Gevent 1.3 : this is buggy (may be related to https://github.com/gevent/gevent/issues/1227) self.assertGreaterEqual(SolBase.datediff(dt), 100) self.assertLessEqual(SolBase.datediff(dt), 200)
def _stop_one_client(self): """ Test """ # Check logger.info("TestLog : server : get server context") self.assertTrue(len(self.tcp_server._client_connected_hash) == 1) ctx = None for cur in self.tcp_server._client_connected_hash.values(): ctx = cur break self.assertIsNotNone(ctx) self.assertEqual(ctx.stop_synchCalled, False) self.assertEqual(ctx.stop_synch_internalCalled, False) # Kill client self.tcpClient.disconnect() # Check client self.assertTrue(self.tcpClient.current_socket is None) self.assertFalse(self.tcpClient.is_connected) # Reset client self.tcpClient = None # Server should be disconnected from client # Wait for server logger.info("TestLog : server : wait for disconnection") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOutMs: ok = True if len(self.tcp_server._client_connected_hash) != 0: ok = False elif not ctx.stop_synchCalled: ok = False elif not ctx.stop_synch_internalCalled: ok = False if ok: logger.info("TestLog : server : wait for disconnection : done") break else: SolBase.sleep(int(self.checkTimeOutMs / 100)) # Check logger.info("TestLog : server : check for disconnection") self.assertTrue(len(self.tcp_server._client_connected_hash) == 0) # Check self.assertEqual(ctx.stop_synchCalled, True) self.assertEqual(ctx.stop_synch_internalCalled, True)
def _start_one_client_checkallping_stop(self): """ Test """ # Start self._start_one_client() # Here we must wait # 1) Client : already connected, must # - send hello, have a reply # - send a ping, have a reply # - reply to one server ping # 2) Server # - receive a hello and reply # - send a ping, have a reply # - reply to one client ping # => We check using the static PingStatXXX dt = SolBase.datecurrent() while SolBase.datediff(dt) < self.checkPingTimeOutMs: # Client client_ko = PingTestTools.get_client_ko_count() # Server server_ko = PingTestTools.get_server_ko_count() # Check full ok if client_ko == 0 and server_ko == 0: break # Wait logger.info("Test : client_ko=%s, server_ko=%s", client_ko, server_ko) SolBase.sleep(1000) # Final check client_ko = PingTestTools.get_client_ko_count(True) server_ko = PingTestTools.get_server_ko_count(True) self.assertEqual(client_ko, 0) self.assertEqual(server_ko, 0) # Stop self._stop_one_client()
def to_udp_list(self, d_tag=None, d_opt_tag=None): """ To dict :param d_tag: dict,None :type d_tag: dict,None :param d_opt_tag: dict,None :type d_opt_tag: dict,None :return list :rtype: list """ if not d_tag: d_tag = {} if not d_opt_tag: d_opt_tag = {} ar_out = list() ar = list(self._sorted_dict.keys()) for i in range(0, len(ar) - 1): # Build key ms1 = ar[i] ms2 = ar[i + 1] if ms2 == sys.maxsize: ms2 = "MAX" out_k = "{0}_{1}-{2}".format(self._instance_name, ms1, ms2) # Get value ai = self._sorted_dict[ms1] out_v = ai.get() # Build local array ar_local = [ # probe name out_k, # tag dict d_tag, # value out_v, # epoch SolBase.dt_to_epoch(SolBase.datecurrent()), # additional tags d_opt_tag ] # Add to output ar_out.append(ar_local) # Over return ar_out
def _start_one_client(self): """ Test """ # Client config client_config = TcpClientConfig() client_config.target_addr = "127.0.0.1" client_config.target_port = 3201 client_config.debug_log = True # ssl if self.testSsl: client_config.ssl_enable = True # Client self.tcpClient = PingSimpleClient(client_config, self.clientHelloTimeOutMs, self.clientPingIntervalMs, self.clientPingTimeOutMs) # Check self.assertTrue(self.tcpClient.current_socket is None) self.assertFalse(self.tcpClient.is_connected) # Connect self.assertTrue(self.tcpClient.connect()) # Check client self.assertIsNotNone(self.tcpClient.current_socket) self.assertTrue(self.tcpClient.is_connected) # Wait for server logger.info("TestLog : server : wait connection") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOutMs: if len(self.tcp_server._client_connected_hash) > 0: break SolBase.sleep(int(self.checkTimeOutMs / 100)) logger.info("TestLog : server : wait connection : done") # Check self.assertEqual(len(self.tcp_server._client_connected_hash), 1)
def _waitforserver_disconnection(self, timeout_ms): """ Wait for client disconnection at server level :return: Nothing """ logger.info("server : wait for disconnection") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < timeout_ms: if len(self.tcp_server._client_connected_hash ) == 0 and self.tcp_client.current_socket is None: break SolBase.sleep(timeout_ms / 1000) logger.info("server : wait for disconnection : done, ms=%s", SolBase.datediff(dt_start)) # Check self.assertEqual(len(self.tcp_server._client_connected_hash), 0) self.assertIsNone(self.tcp_client.current_socket)
def __init__(self, callback_disconnect, callback_receive): """ Constructor. :param callback_disconnect: Callback to call upon socket disconnection. :type callback_disconnect: Callable :param callback_receive: Callback to call upon socket receive. :type callback_receive: Callable """ # Connected flag (backed by is_connected property) self.is_connected = False # Socket instance self._socket = None # Send queue self.__send_queue = Queue() # Callback : called when something is received from the socket self._callback_receive = callback_receive # Callback : called upon socket disconnection self._callback_disconnect = callback_disconnect # Timestamps self._dt_created = SolBase.datecurrent() self._dt_last_recv = self._dt_created self._dt_last_send = self._dt_created # SSL Handshake asynch self.__ssl_handshake_asynch = False self.__ssl_handshake_pending = False self.__ssl_handshake_timeout_ms = None self.__ssl_wait_debug_ms = None self.__ssl_timeout_greenlet = None self.__ssl_locker = Lock() # SSL handshake time self.__ssl_handshake_ms = None # Fatal error self.__internal_fatal_error = False
def _protocol_client_hello_send(self): """ Must send a hello """ with self._protocol_lock: # Schedule a timeout self._schedule_client_helloservertimeout() # Timestamp self.dt_hello_send = SolBase.datecurrent() # Send a ping b = self.send_unicode_to_socket("C.HI") if not b: # Stat logger.error("send failed, fatal, disconnecting") Meters.aii("ping.client.client_send_error") # Disconnect self.disconnect() else: # Stats Meters.aii("ping.client.client_hello_sent")
def test_tcp_svr_start_cli_connect_clidisco_clicanreco_deadlock(self): """ Test """ # Instance self.tcp_server = None try: # Start server self._start_server_and_check(deadlock=True) # Start client, check and stop client self._start_one_client_checkstop() # Check stats logger.info("TestLog : server : wait for timeout on stop calls") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOutMs: ok = True if Meters.aig( "tcp.server.client_remove_timeout_internal") != 1: ok = False elif Meters.aig( "tcp.server.client_remove_timeout_business") != 1: ok = False if ok: logger.info( "TestLog : server : wait for timeout on stop calls : done" ) break else: SolBase.sleep(int(self.checkTimeOutMs / 100)) # Check self.assertEqual( Meters.aig("tcp.server.client_remove_timeout_internal"), 1) self.assertEqual( Meters.aig("tcp.server.client_remove_timeout_business"), 1) # Start client, check and stop client self._start_one_client_checkstop() # Check stats logger.info("TestLog : server : wait for timeout on stop calls") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOutMs: ok = True if Meters.aig( "tcp.server.client_remove_timeout_internal") != 2: ok = False elif Meters.aig( "tcp.server.client_remove_timeout_business") != 2: ok = False if ok: logger.info( "TestLog : server : wait for timeout on stop calls : done" ) break else: SolBase.sleep(int(self.checkTimeOutMs / 100)) # Check self.assertEqual( Meters.aig("tcp.server.client_remove_timeout_internal"), 2) self.assertEqual( Meters.aig("tcp.server.client_remove_timeout_business"), 2) except Exception as e: logger.error("Exception in test, ex=%s", SolBase.extostr(e)) raise finally: if self.tcp_server: self.tcp_server.stop_server() if self.tcpClient: self.tcpClient.disconnect()
def _start_multi_client_checkallping_stop(self): """ Test """ # Start self._start_multi_client(self.clientMaxCount) # Here we must wait # 1) Client : already connected, must # - send hello, have a reply # - send a ping, have a reply # - reply to one server ping # 2) Server # - receive a hello and reply # - send a ping, have a reply # - reply to one client ping # => We check using the static PingStatXXX flush_stat_inloop = False dt = SolBase.datecurrent() dt_stat = SolBase.datecurrent() dt_loop = SolBase.datecurrent() client_prev_completed_ping = 0 server_prev_completed_ping = 0 while SolBase.datediff(dt) < self.runTimeMs: # Wait SolBase.sleep(1000) # Client client_ko = PingTestTools.get_client_ko_count( False, self.clientMaxCount) # Server server_ko = PingTestTools.get_server_ko_count( False, self.clientMaxCount) # Total ping client_total_completed_ping = Meters.aig( "ping.client.client_ping_server_reply") server_total_completed_ping = Meters.aig( "ping.server.serverPingClientReply") # Current ping client_local_completed_ping = client_total_completed_ping - client_prev_completed_ping server_local_completed_ping = server_total_completed_ping - server_prev_completed_ping # Store prev client_prev_completed_ping = client_total_completed_ping server_prev_completed_ping = server_total_completed_ping # Elapsed ms elapsed_ms = SolBase.datediff(dt_loop) elapsed_total_ms = SolBase.datediff(dt) # Ping per sec. client_local_pps = (client_local_completed_ping / (elapsed_ms / 1000.0)) server_local_pps = (server_local_completed_ping / (elapsed_ms / 1000.0)) client_avg_pps = (client_total_completed_ping / (elapsed_total_ms / 1000.0)) server_avg_pps = (server_total_completed_ping / (elapsed_total_ms / 1000.0)) # Reset date dt_loop = SolBase.datecurrent() # Wait logger.info( "Running : ko=%s:%s, sec=%s/%s, cli.ping=%s, svr.ping=%s, exp.pps=%.2f, " "cli.pps=%.2f, svr.pps=%.2f, cli.aps=%.2f, svr.aps=%.2f", client_ko, server_ko, int(SolBase.datediff(dt) / 1000), int(self.runTimeMs / 1000), client_local_completed_ping, server_local_completed_ping, self.expectedPps, client_local_pps, server_local_pps, client_avg_pps, server_avg_pps) # Stat if flush_stat_inloop and SolBase.datediff( dt_stat) > self.statEveryMs: Meters.write_to_logger() dt_stat = SolBase.datecurrent() # Final check client_ko = PingTestTools.get_client_ko_count(True, self.clientMaxCount) server_ko = PingTestTools.get_server_ko_count(True, self.clientMaxCount) self.assertEqual(client_ko, 0) self.assertEqual(server_ko, 0) # Stop self._stop_multi_client()
def _stop_multi_client(self): """ Test """ if self.arTcpClient is None: return # -------------------------------- # Disconnect # -------------------------------- logger.info("disconnecting, clientCount=%s", len(self.arTcpClient)) ko_count_socket = 0 ko_count_is_connected = 0 for local_client in self.arTcpClient: # Kill client local_client.disconnect() # Check client if local_client.current_socket: ko_count_socket += 1 if local_client.is_connected: ko_count_is_connected += 1 if ko_count_socket == 0 and ko_count_is_connected == 0: logger.info( "disconnecting, ko_count_socket=%s, ko_count_is_connected=%s", ko_count_socket, ko_count_is_connected) else: logger.error( "disconnecting, ko_count_socket=%s, ko_count_is_connected=%s", ko_count_socket, ko_count_is_connected) # -------------------------------- # Clean up # -------------------------------- self.arTcpClient = None # -------------------------------- # Server should be disconnected from client # Wait for server # -------------------------------- logger.info("wait for server disconnection") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOutMs: # Check if len(self.tcp_server._client_connected_hash) == 0: break # Log logger.info("waiting for server : connected/target=%s/0", len(self.tcp_server._client_connected_hash)) # Wait SolBase.sleep(int(self.checkTimeOutMs / 100)) # Check logger.info("check for disconnection") self.assertTrue(len(self.tcp_server._client_connected_hash) == 0) self.assertTrue(ko_count_socket == 0) self.assertTrue(ko_count_is_connected == 0) # Done logger.info("all done")
def _read_loop_internal(self): """ Low level read loop on socket """ logger.debug("entering now, self=%s", self) try: while self.__is_running(): try: if self.__ssl_handshake_pending: # Pending SSL handshake + received something # Handle SSL handshake now # Stats Meters.dtci("tcp.server.delay_server_accept_to_sslhandshakestart", SolBase.datediff(self._dt_created)) # Timestamps self._dt_last_recv = SolBase.datecurrent() # Debug ONLY if self.__ssl_wait_debug_ms: logger.warning("DEBUG : forcing a wait for SSL handshake timeout, ms=%s, self=%s", self.__ssl_wait_debug_ms, self) SolBase.sleep(self.__ssl_wait_debug_ms) logger.warning("DEBUG : forcing a wait for SSL handshake timeout, done, self=%s", self) # Do the handshake # TODO : gevent 1.3 : This is now broken due to underlying _sslobj None. To be checked. SSL support current disable. raise Exception("SSL Support currently disabled") # noinspection PyUnreachableCode self.current_socket.do_handshake() # Done, cancel timeout self._unschedule_ssl_handshake_timeout() # Ms ms = SolBase.datediff(self._dt_last_recv) # SSL Stats (for client) self._set_ssl_handshake_ms(ms) # Server stats Meters.dtci("tcp.server.delay_server_sslhandshake", ms) # Done self.__ssl_handshake_pending = False # Non blocking mode now self.current_socket.setblocking(0) self.current_socket.settimeout(None) # Reloop in normal mode continue # Wait for socket to be available for read ok = self._wait_for_socket_recv() if not ok: # This is not really normal logger.warning("_wait_for_socket_recv returned False, self=%s", self) elif not self.__is_running(): logger.debug("_wait_for_socket_recv returned True, __is_running()==False, exiting, self=%s", self) return else: # Something to read... local_buf = self._read_from_socket() if not self.__is_running(): logger.debug("_read_from_socket returned, __is_running()==False, exiting, self=%s", self) elif local_buf is None: # This is not really normal logger.debug("_read_from_socket returned None, self=%s", self) elif len(local_buf) == 0: # This is not really normal logger.debug("_read_from_socket returned empty string, self=%s", self) # Gevent 1.0.2 : call disconnect self._disconnect_helper("_read_from_socket returned empty string") else: # Timestamps self._dt_last_recv = SolBase.datecurrent() # Notify if self._callback_receive: self._callback_receive(local_buf) else: logger.error("_callback_receive is None, check you implementation, self=%s", self) # Next read SolBase.sleep(0) except Exception as e: logger.warning("IN_LOOP Exception raised, ex=%s, self=%s", SolBase.extostr(e), self) except Exception as e: logger.error("METHOD Exception raised, ex=%s, self=%s", SolBase.extostr(e), self) finally: logger.debug("exiting now, self=%s", self) SolBase.sleep(0)
def _write_loop_internal(self): """ Low level read/write loop on socket """ logger.debug("entering now, self=%s", self) try: while self.__is_running(): try: # Wait for the queue try: # Call, with blocking. item = self.send_queue.get(True, TcpSocketManager.QUEUE_WAIT_SEC_PER_LOOP) except Empty: # Next read SolBase.sleep(0) continue # Go some if isinstance(item, binary_type): # Length length = len(item) # Buffer to send buf_to_send = item # Event to signal event_to_signal = None elif isinstance(item, SignaledBuffer): # Stats length = len(item.binary_buffer) # Buffer to send buf_to_send = item.binary_buffer # Event to signal event_to_signal = item.send_event else: logger.warning("not managed class in queue, class=%s, item=%s", SolBase.get_classname(item), item) continue # Stat now Meters.aii("tcp.server.server_bytes_send_pending", -length) # Wait for socket to be available for write logger.debug("waiting for socket to send=%s, self=%s", repr(item), self) ok = self._wait_for_socket_send() if not ok: # This is not really normal if self.__is_running(): logger.warning("_wait_for_socket_send returned False (running true), self=%s", self) else: logger.info("_wait_for_socket_send returned False (running false), self=%s", self) elif not self.__is_running(): logger.debug("Ready for send, but __is_running==False, exiting, send=%s, self=%s", self) return else: try: # Ready to write, fire logger.debug("writing to socket to send=%s, self=%s", repr(item), self) ok = self._write_to_socket(buf_to_send) if not ok: if self.__is_running(): # This is not really normal logger.warning("_write_to_socket returned False (running true), self=%s", self) else: logger.warning("_write_to_socket returned False (running false), self=%s", self) # Signal if applicable if event_to_signal: event_to_signal.set() finally: # Timestamps self._dt_last_send = SolBase.datecurrent() # Stats Meters.aii("tcp.server.server_bytes_send_done", length) # Next read SolBase.sleep(0) except Exception as e: logger.warning("IN_LOOP Exception raised, ex=%s, self=%s", SolBase.extostr(e), self) except Exception as e: logger.error("METHOD Exception raised, ex=%s, self=%s", SolBase.extostr(e), self) finally: logger.debug("exiting now, self=%s", self) SolBase.sleep(0)
def _start_multi_client(self, client_count): """ Test :param client_count: Client count. """ # Client config client_config = TcpClientConfig() client_config.target_addr = "127.0.0.1" client_config.target_port = 3201 client_config.debug_log = self.debug_log # ssl if self.testSsl: client_config.ssl_enable = True # Proxy ? if self.testProxy: client_config.proxy_enable = True client_config.proxy_addr = "127.0.0.1" client_config.proxy_port = 1080 # -------------------------------- # Client array : allocate # -------------------------------- logger.info("allocating, client_count=%s", client_count) self.arTcpClient = list() dt_cur = SolBase.datecurrent() for _ in range(client_count): # Alloc local_client = PingSimpleClient(client_config, self.clientHelloTimeOutMs, self.clientPingIntervalMs, self.clientPingTimeOutMs) # Register self.arTcpClient.append(local_client) # Check self.assertTrue(local_client.current_socket is None) self.assertFalse(local_client.is_connected) if SolBase.datediff(dt_cur) > 1000: dt_cur = SolBase.datecurrent() logger.info("allocating, client_count=%s, done=%s", client_count, len(self.arTcpClient)) logger.info("allocating, client_count=%s, done=%s", client_count, len(self.arTcpClient)) # -------------------------------- # Client array : start # -------------------------------- logger.info("connecting, client_count=%s", client_count) dt_start = SolBase.datecurrent() dt_cur = SolBase.datecurrent() connected_count = 0 for local_client in self.arTcpClient: # Connect self.assertTrue(local_client.connect()) # Check client self.assertIsNotNone(local_client.current_socket) self.assertTrue(local_client.is_connected) connected_count += 1 if SolBase.datediff(dt_cur) > 1000: dt_cur = SolBase.datecurrent() elapsed_ms = SolBase.datediff(dt_start) if elapsed_ms > 0: connect_per_sec = client_count / (elapsed_ms * 0.001) else: connect_per_sec = 0 logger.info( "connecting, client_count=%s, connected_count=%s, perSec=%.2f", client_count, connected_count, connect_per_sec) elapsed_ms = SolBase.datediff(dt_start) if elapsed_ms > 0: connect_per_sec = client_count / (elapsed_ms * 0.001) else: connect_per_sec = 0 logger.info( "connecting, client_count=%s, connected_count=%s, perSec=%.2f", client_count, connected_count, connect_per_sec) # -------------------------------- # Wait for server # -------------------------------- logger.info("waiting for server") dt_start = SolBase.datecurrent() while SolBase.datediff(dt_start) < self.checkTimeOutMs: if len(self.tcp_server._client_connected_hash) == client_count: break logger.info("waiting for server : connected=%s, target=%s", len(self.tcp_server._client_connected_hash), client_count) SolBase.sleep(int(self.checkTimeOutMs / 100)) logger.info("waiting for server : done : connected=%s, target=%s", len(self.tcp_server._client_connected_hash), client_count) # Check self.assertEqual(len(self.tcp_server._client_connected_hash), client_count) # -------------------------------- # Done # -------------------------------- logger.info("all client connected")