2014-02-21 3 views
0

길이가 접두사가 붙은 패킷을 구문 분석 한 후 요청에 응답하는 간단한 파이썬 TCP 서버를 작성하는 경우가 있습니다. 된 설정으로 소켓을 가정하면, 이것은 일반적으로 많은 다음과 같습니다TCP 스트림에서 패킷 구문 분석하기

def tcp_server_loop(): 
    msg = '' 
    msg_len = 0 
    while True: 
     msg += sock.recv(4096) 
     if len(msg) >= 4 and msg_len == 0: 
      msg_len, = struct.unpack_from("!I", msg) 
     if len(msg) >= msg_len: 
      protocol.parse_packet(msg[:msg_len]) 
      msg = msg[msg_len:] 
      msg_len = 0 

이 작동하고 나에게 잘 많은 시간을 봉사했다,하지만 난 항상 msg += sock.recv(4096)에 추가 문자열이 irk'ed했습니다. 작은 패킷의 경우이 작은 문자열에 대한 새 저장소를 할당하는 오버 헤드가 나쁘지 않으므로 그리 좋지 않습니다. 그러나 큰 패킷 (MB)의 경우, 많은 양의 복사가 파이썬의 문자열 구현에서 장면 뒤에서 진행됩니다.

C 또는 일부 유사한 언어에서 링 버퍼는 예상되는 가장 큰 패킷 크기의 분명한 데이터 구조입니다. 그러나 비슷한 Python 구현을 찾지 못했습니다. 누군가 내 코드를 향상시킬 수 있는지 궁금하네요. 이러한 유형의 서버는 어떻게 구현합니까?

답변

1

빠른 제안 : 먼저 packet_size에서 msg_len으로 이름을 바꿀 수 있습니다. TCP 스트림에서 파싱하려고 시도하는 것은 응용 프로그램 수준 프로토콜 메시지이며 TCP 세그먼트 (일명 TCP 패킷)가 아닙니다.

그러나보다 효율적인 방법은 메시지 머리글을받을 때 길이가 인 두 번째 고정 크기 bytearray 버퍼를 할당하는 것입니다. 이것을 사용하여 이후에 읽은 데이터를 저장하십시오.

+0

변수 이름에 대해 점을 찍었으므로 원래 코드가 더 명확하게 업데이트되었습니다. – user3339161

+0

bytearray를 만드는 것이 효율성을 향상시키는 방법을 잘 모르겠다. 'msg'와 바이트 배열 사이에 복사가 필요하지 않겠습니까? 현재의 구현은 오직 복사를 요구하지 않는 프로토콜 파서에만 슬라이스를 전달합니다. – user3339161

+0

예, 그렇습니다. 그러나 핫스팟은 복사본이 아니라'msg'에 추가 할 때 메모리의 재 할당이라고 생각합니다. 여러분이'sock.recv()'의 바이트 수가 크지 않다면 말입니다. 'msg'를 'bytearray'로 만들면, 아마도 타입 변환을하기 위해 통역사를 요구하지 않음으로써 조금 더 많은 것을 얻게 될 것입니다. – cklin