def _monitor_clients(self): while self.is_running: time.sleep(self.wait_term) now = time.time() with self.lock_clients: for client in list(self.clients): try: with client: if client.state == 2: return if client.last_receive_time is None or int( abs(now - client.last_receive_time) ) > self.wait_term: # 클라이언트의 마지막 수신 시각과 현재 시각을 비교해 heartbeat 전송 주기가 돌아오기 전까지 데이터가 수신한 적이 있는지 확인 if client.wait_count == self.max_wait_count: """ 클라이언트가 최대 heartbeat 허용 횟수를 넘긴 상태라면 연결이 끊긴 상태라고 판단하고 예외를 발생시켜 except 부분에서 클라이언트 종료 처리 """ raise Exception else: # 허용 횟수를 넘기지 않은 상태라면 heartbeat 메시지 전송 후 heartbeat 전송 횟수를 1 증가 시킴 send_message(HEARTBEAT, self.host, client) client.wait_count += 1 except Exception as e: with client: if client.state != 2: client.close() EventManager.call_handler( DISCONNECTED_CLIENT_EVENT, client=client)
def _accept_client(self): channel: socket.socket address: tuple channel, address = self._server_socket.accept() if len(self.clients) != self.max_client: client = Client(address, channel) # 소켓을 유용하고 편하게 사용하기 위해 Client 래퍼 객체로 생성 try: self._sel.register(client.channel, selectors.EVENT_READ, client) # selectors 이벤트 등록 self.clients[client] = client # 클라이언트 리스트 등록 EventManager.call_handler(CONNECT_CLIENT_EVENT, client=client) except socket.error as e: # todo: (try-except) 이제 이벤트 헨들러는 개별 스레드에서 처리하므로 try-except 필요가 없음 with client: if client != 2: client.close() else: # 최대 클라이언트 연결 수를 넘어갔을 경우 꽉 찼다는 메시지르 보내고 연결 종료 channel.sendall(make_message(FULL_CONN, self.host, address[0])) channel.close()
def _receive_client_message(self, client: Client): try: with client: client.last_receive_time = time.time() client.wait_count = 0 # 수신 한 경우 heartbeat 누적 수를 초기화 # todo: Cleint receive 함수 내부에 처리할것 header_bytes = client.receive(13) # 헤더 사이즈 만큼 버퍼에서 가져옴 # todo: 사이즈가 작긴 하지만 이 부분도 아래 BODY 데이터를 가지고 오는 것처럼 처리를 해줘야함 (Client receive 함수 내에 처리) if not header_bytes: if client.state != 2: client.close() EventManager.call_handler(DISCONNECTED_CLIENT_EVENT, client=client) # todo: 이벤트 호출자가 제어하지 않고, 함수 안에서 실행을 제어하도록 변경 # 예) 클라이언트 연결 이벤트에서 헨들러 A가 실행할때 어떠한 이유로 클라이언트를 종료시킨 경우 다음 번에 실행될 # (이벤트에 대한 헨들러들이 순차적으로 실행됨) 클라이언트 연결 이벤트 헨들러 B가 실행 되면 안되므로 return header = Header.decode(header_bytes) if not header: return body_bytes = client.receive(header.SIZE) # body 크기는 가변 적이므로 header에서 body 사이즈를 알아낸 후 버퍼에서 가져옴 while len(body_bytes) < header.SIZE: body_bytes += client.receive(header.SIZE - len(body_bytes)) # 서버 상황상 명시한 사이즈 만큼 데이터를 가지고 온다는 보장이 없으므로, 완전할때까지 버퍼에서 가져옴 receive_message(client, Message(header, body_bytes)) except Exception as e: # 연결 혹은 받아온 데이터에 문제가 있으면 연결을 끊는거로 처리하고 있음 with client: if client.state != 2: client.close() EventManager.call_handler(DISCONNECTED_CLIENT_EVENT, client=client)