2014년 10월 12일 일요일

파이썬 소켓(socket) 모듈을 사용한 네트워크 프로그래밍 - Python 도서 리뷰

오전 1:49 Posted by jonnung , 8 comments


네트워크 프로그래밍(2/2)

 이전 포스트에서 '클라이언트/서버 아키텍처와 소켓 (socket)' 부분에 대해 정리했다. 이번에는 이 개념을 파이썬에 도입해서 프로그램을 작성 해보자.

일단 책에 나오는 TCP 서버/클라이언트, UDP 서버/클라이언트를 예제를 통해서 주로 사용하는 socket 모듈에 대한 구조를 파악해봤고, 이번에는 연습문제 중에 하나인 '전이중(full-duplex) 채팅 프로그램'을 만들어 보겠다.

책에는 나오지 않지만 검색을 해보던 중에 소켓 프로그램의 주요 이슈중 하나인 블럭킹(Blocking)에 대해 알게 되었는데 블럭킹을 해결하기 위한 방법중에 하나인 select 모듈을 채팅 프로그램에 적용해 보았다.

소스코드를 살펴 보기 전에 당연히 이 블럭킹(Blocking) 이라는 녀석이 무엇인지 살펴 보는 것이 순서겠다.


블럭킹(Blocking)

 어떤 일이 일어나기를 기다리면서 멍하니 있는 상태를 말한다. 예를 들어 소켓 서버에서 recv() 를 호출해 놓고, 클라이언트가 보내는 데이터를 기다리는 경우, 소켓 서버는 읽기 상태에서 블럭킹 되어 있다고 말한다고 한다.
 서버나 클라이언트나 차례대로 서로 데이터를 주고 받으면 이것은 크게 문제가 되지 않는다. 하지만 만약 서로가 데이터를 주기를 기다리는 상태가 된다면 어떻게 될까?

 이런 블럭킹 상태를 피하는것이 가장 좋겠지만, 그렇지 못할 수도 있기 때문에 일정 시간 이후 타임아웃이 걸려서 이 상태를 벗어나는 것도 좋은 방법이 될 수 있다.

그래서 채팅 프로그램 예제에서는 이 블럭킹 문제를 해결하기 위해 select 모듈을 사용한 것이다.
현재 수준에서 select 모듈을 더 파보는 것은 조금 무리가 있기 때문에 간단한 사용법만 익혀서 바로 적용했다. 하하;;


TCP 서버/클라이언트 채팅 프로그램

일단 전체 소스코드는 아래와 같다. 

상대적으로 클라이언트(tcpChatClient.py)가 쉽다(?)고 느껴지기 때문에 공을 더 들인 부분은 서버측 스크립트(tcpChatServer.py) 이다.

select 모 듈의 select 메소드는 양쪽 모두에서 사용되었다. 네번째 인자로 전달된 값이 '블럭킹' 단락에서 언급한 그 타임아웃 시간이다. 
10초라는 의미의 값을 전달 했기 때문에 읽어 들일 객체(첫번째 인자에 리스트 형태로 전달한 소켓들)가 없으면 비어있는 리스트를 반환한다.

38 ~39번 라인의 # 새로운 접속 부분에서 select() 가 반환한 read_socket 리스트의 값중에 서버소켓(serverSocket)과  같은 객체를 체크해서 새로운 접속인지를 판단한다.
하지만 아직 서버는 클라이언트의 연결이 받아들여진 상태는 아니다. listen() 메소드에서 일단 클라이언트의 접속이 되면, accept() 를 호출해서 별도의 소켓에 넘겨주고 통신을 진행하는 방식이 TCP 통신의 특징이고 UDP 와의 큰 차이인 것이다.

프로그램을 종료 하려면 강제로 종료시키는 방법 밖에 없는데 이때 KeyboardInterrupt 예외가 발생한다.
연습 삼아 만든 프로그램이지만 보기 안좋은 부분을 보완하기 위해서 try ~ except로 예외를 잡아 소켓을 닫고, 프로그램을 종료 시켰다. (책에서는 이 부분에 대한 코드는 없지만 '부드럽게 종료하기' 라는 참고사항으로 권장하고 있다.)


참 고

댓글 8개:

  1. 클라이언트측에서 read_socket error가 발생하는데 어떻게 해결해야하나요?

    답글삭제
  2. >>> python client.py

    채팅 서버(127.0.0.1:56789)에 연결 되었습니다.
    Traceback (most recent call last):
    File "client.py", line 36, in
    read_socket, write_socket, error_socket = select(connection_list, [], [], 10)
    OSError: [WinError 10038] 소켓 이외의 개체에 작업을 시도했습니다

    ㅠㅠ......?

    답글삭제
  3. 답글
    1. 오 안녕하세요 Jimin님! 코무그룹의 unixforever입니다 ㅎㅎ

      삭제
  4. 작성자가 댓글을 삭제했습니다.

    답글삭제
  5. 서버 소스코드에서 line 58에 !=가 에러가 나네요. =가 invalid syntax라는데 뭐가 잘못된걸까요?

    답글삭제
  6. 채팅 서버(127.0.0.1:56789)에 연결 되었습니다.

    Traceback (most recent call last):
    File "C:\Users\user\Desktop\server.py", line 44, in
    read_socket, write_socket, error_socket = select(connection_list, [], [], 10)
    UnsupportedOperation: fileno
    >>>

    이렇게 뜨는데 뭐가문제인가요 ㅜ

    답글삭제
  7. 채팅 서버(127.0.0.1:56789)에 연결 되었습니다.
    Traceback (most recent call last):
    File "C:/Users/Park_SangHyeon/PycharmProjects/untitled1/test(main).py", line 71, in
    read_socket, write_socket, error_socket = select(connection_list, [], [], 10)
    OSError: [WinError 10038] 소켓 이외의 개체에 작업을 시도했습니다

    답글삭제