brunch

You can make anything
by writing

C.S.Lewis

by Younggi Seo Aug 21. 2021

소켓의 개념과 객체 생성 review-2

tip2. TCP와 UDP의 특징





표준 소켓 통신 코딩뿐만 아니라 하위 수준(low level)의 네트워크 동작원리 중 TCP/IP 프로토콜을 이해하는 것은 지루하다. 일전에 KISA 사이버 보안 인재 교육 센터에서 CAN 통신을 배울 때, Facebook 채팅에 사용되는 ZeroMQ와 같은 것을 사용해서 라즈베리 통신을 시도했던 게 재밌었지만, 어쨌든 클라이언트-서버의 데이터 교환 프로그램에서 소켓을 이용한 TCP/UDP 접속이 ZeroMQ 소켓에서도 기본 원리다.



저번 섹션에서 작성한 파이썬 코드 중 클라이언트와 서버 코드에서 server_address는 (주소, 포트)의 튜플*이다. 주소는 이름 혹은 IP 주소의 문자열이 될 수 있다. 같은 컴퓨터로 통신을 했기 때문에 'localhost'을 사용하거나 같은 의미인 '127.0.0.1' 주소를 사용했다.



이번에는 한 프로세스에서 다른 프로세스로 작은 데이터를 전송한 후, 데이터를 다시 보낸 사람에게 반환해본다. 먼저 서버 프로그램을 작성한 뒤, 클라이언트 프로그램을 작성한다. 각 프로그램은 현재 날짜와 시각을 출력하고 소켓을 연다. 서버는 소켓 연결을 청취하고, 클라이언트는 서버에 전송할 소켓을 작성한다.



먼저 아래와 같이 UDP 소켓 접속을 위한 서버 프로그램을 작성한다.


현재 시각 및 날짜를 출력하기 위한 라이브러리와 소켓 패키지를 가져오는 것을 시작으로 소켓 패키지에서 두 메서드(socket.socket과 bind)를 통해 네트워킹을 설정했다. socket.socket 메서드는 소켓을 생성하고 bind 메서드는 소켓에 바인딩한다(해당 IP주소와 포트에 도달하는 모든 데이터를 청취한다). AF_INET은 인터넷(IP) 소켓을 생성한다는 것을 의미한다(또 다른 타입의 유닉스 도메인 소켓이 있지만, 로컬에서만 동작한다). SOCK_DGRAM은 저번 섹센에서 말했듯이 데이터그램을 송수신하겠다는, 즉 UDP를 사용하겠다는 의미다.


data, client = server.recvfrom(max_size)


이 시점에서 서버는 들어오는 데이터그램(UPD 데이터)을 기다린다(recvfrom). 데이터그램이 도착하면 서버는 깨어나서 클라이언트에 대한 데이터와 정보를 얻는다(역시 원서에서 묘사하는 통신과정의 내용은 있는 그대로 표현하기에 생동감이 넘친다!) 그리고 client 변수는 클라이언트에 접근하기 위한 주소와 포트가 결합된 데이터를 포함한다. 마지막으로 서버는 sendto 메서드를 통해 응답을 전송하고 close() 함수를 통해 커넥션을 종료한다.




UDP 클라이언트 프로그램을 다음과 같다.


클라이언트는 서버와 거의 같은 메서드를 가지고 있다(bind()  메서드 제외). 클라이언트는 데이터를 전송하고 나서 데이터를 받는다. 반면 서버는 데이터를 먼저 받는다.



먼저 한 창에서 서버를 실행하고 서버가 시작된 시간을 출력한 후, 클라이언트에서 데이터 보내기를 기다린다. 또 다른 창에서 클라이언트를 실행하면 데이터를 서버로 보내고, 응답받은 데이터를 출력한 후 종료한 뒤 서버는 아래 문장을 출력한 후 종료하는 건데, UDP는 한 패킷 혹은 청크(chunk)로 데이터를 보내기 때문에, 데이터 전송을 보장하지 않는다. 그래서 아래와 같이 데이터 전송이 안될 수도 있다.




UDP를 통해 여러 메시지를 보낼 경우, 순서 없이 도착하거나 모두 도착하지 않을 수 있다. UDP는 빠르고, 가볍고, 신뢰할 수 없고, 비연결형(connectionless)이다.





그래서 TCP(Transmission Control Protocol)을 선호한다. TCP는 웹과 같은 수명이 긴 커넥션에 사용한다. TCP는 데이터를 보낸 순서대로 전달한다. 전송에 문제가 생기면 다시 보낸다(빌, 2017). TCP를 통해 클라이언트에서 서버로 패킷을 전달해보자.


이전 UDP 클라이언트의 코드처럼 한 문자열을 서버로 보내나 소켓을 호출하는 부분은 약간 다르다. 


스트리밍 프로토콜(TCP)을 얻기 위해 SOCK_DGRAM을 SOCK_STREAM으로 바꿨다. 또한 스트림을 설정하기 위해 connect() 호출을 추가했다. 인터넷에서 각 데이터그램은 '야생' 그 자체이기 때문에 UDP에 대한 스트림은 필요 없다.




TCP 서버 프로그램 또한 UDP와 다르다.


server.listen(5)는 대기 중인 커넥션의 최대 접속 클라이언트 수를 다섯 개로 지정했다. server.accept()는 도착한 첫 번째 유효한 메시지를 얻는다. client.recv(1000) 최대 허용 메시지의 길이를 1,000 바이트로 제한한 거다.




UDP처럼 서버를 먼저 실행하면,



다음에는 클라이언트를 실행하면, 서버에 메시지를 전송하고, 응답을 받은 후 종료하게 된다.



서버는 메시지를 모아서 아래와 같이 이를 출력하고, 응답을 한 후 종료한다.




TCP 서버는 client.sendall() 함수를 호출하고, 이전 UDP 서버는 client.sendto() 함수를 호출하여 응답한다. TCP는 소켓의 여러 호출을 통해 클라이언트와 서버의 커넥션을 유지(established)하고, 클라이언트의 IP주소를 기억한다.


만약 좀 더 복잡한 코드를 작성한다면 저수준의 소켓이 실제로 어떻게 동작하는지 볼 수 있다(Socket Programming HOWTO — Python 3.9.6 documentation). 아래에 몇 가지 TCP와 UDP의 특징을 기술했다(빌, 2017).




UDP는 메시지를 전송하는 데 크기의 제한이 있다. 그리고 메시지가 목적지까지 도달하는 것을 보장하지 않음.

TCP는 메시지가 아닌 바이트 스트림을 전송한다. 시스템의 각 송수신 호출에서 얼마나 많은 바이트가 전달되는지 알 수 없음.

TCP를 통해 전체 메시지를 전달하기 위해 세그먼트로부터 전체 메시지를 재구성하기 위한 몇 가지 추가 정보가 필요함(고정 메시지 크기(바이트), 메시지 전체 크기, 구분자).

메시지는 유니코드 텍스트 문자열이 아닌 바이트이기 때문에 파이썬 바이트 타입을 사용해야 한다. 유니코드와 바이트 타입에 대한 자세한 사항은 다음에 한 번 다뤄볼 예정임.








* 튜플(tuple) : 쉽게 말해 변하지 않는 상수값이다.


발췌

빌 루바노빅. (2017). 처음 시작하는 파이썬 (pp. 365-370). n.p.: 한빛미디어.
매거진의 이전글 소켓의 개념과 객체 생성 review-1
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari