tcp udp 차이점과 프로그래밍에서 주의해야할 부분

2021. 1. 16. 10:00 컴퓨터/프로그래밍

tcp/ip와 udp/ip의 다른 점은?

소켓 통신 tcp/ip와 udp/ip를 학습을 통해 어떤 프로토콜인지는 알지만, 정확한 차이점과 특성을 모르면 잘못된 코딩을 하거나 실수하는 경우가 많습니다. 통신은 외부 업체와 협업하거나 장비를 이용해야 해서 어느 한쪽이 엉뚱하게 프로그램을 만들면 간단한 일도 힘든 작업이 될 수 있는데요, tcp/ip와 udp/ip의 차이점과 각 프로토콜을 사용했을 때의 주의해야 할 내용을 알아보겠습니다.

우선 tcp/ip는 연결 지향성이고 udp/ip는 비연결 지향성이라고 합니다. tcp/ip는 스트림 데이터이고 udp/ip는 패킷 단위로 보낸다, 또는 tcp/ip는 데이터 경계가 없지만, udp/ip는 데이터 경계가 있다는 등의 생소한 단어로 어렵게 느껴집니다. 그러나 알고 보면 말만 거창하다는 것을 알게 되는데요, 그렇게 지을만하다 이해되기도 합니다.

udp/ip 퀴즈

tcp/ip와 udp/ip의 특성 차이를 정확히 아시나요? 그렇다면 문제를 내겠습니다. 상대가 udp/ip로 2k 데이터를 보냈습니다. 그런데 나의 수신 버퍼 크기가 1k라서 절반만 받았습니다. 그렇다면 두 번째 수신으로 나머지 1k를 받을 수 있을까요?

어? 글쎄? 하시면 udp/ip의 특성을 정확히 알고 계시는 것이 못 됩니다. 이것을 알고 있지만, 코딩에 반영하지 않았다면 실행 중에 엉뚱한 작동이 발생할 수 있습니다.

답부터 말씀드리면 udp/ip에서는 나머지 데이터를 받을 수 없습니다. 받아서도 안 됩니다. 그래서 수신 버퍼를 충분히 크게 잡거나 상대의 프로그램이 보내는 최대 데이터 길이를 미리 확인 또는 협의해야 합니다. tcp/ip는 당연히 두 번, 세 번 그리고 이후로 여러 번 수신으로 나머지 데이터를 받을 수 있습니다.

왜 udp/ip는 남은 데이터를 받지 못할까요?

 

연결 지향성, 비연결 지향성

소켓 통신을 학습하다 보면 연결 지향성·비연결 지향성이라면서 뭔가 대단한 것처럼 보이는 말이 나옵니다. 연결을 지향한다? 어차피 네트워크는 구성되어 있는데 뭘 또 연결을 지향해? 누가 만들었는지 말만 거창합니다만, 앞서도 얘기했듯이 이해되면 뭐 틀린 말이 아니라는 것을 알게 됩니다. 좀 더 편한 말이 뭘까 생각하면 딱히 떠 오르는 것도 없고요.

tcp/ip는 상대와 통신하기 위해서는 먼저 연결을 시도하고 상대의 허락을 구해야 합니다. 즉, 통신한다는 것을 나도 알고 상대도 알고 있어서 주고받는 준비가 된 상태에서 데이터를 송수신합니다. tcp/ip에서는 데이터를 보내는 write() 함수에 목적지 IP를 넘겨줄 필요가 없습니다. 처음 연결하는 connect() 함수에서만 누구와 연결해야 하는지만 IP 주소를 인자로 알려줍니다.

이렇게 상대에게 연결을 요청하고 상대가 허락하면 이후로 나와 상대방 사이에서 tcp가 흐름 제어를 계속하면서 연결을 유지합니다. 연결이 유지되므로 더 이상 주고받을 데이터가 없다면 연결 끊기를 합니다. 이런 tcp의 역할로 tcp/ip를 연결 지향성 프로토콜이라고 합니다.

tcp/ip는 연결 상태만 잘 관리하면 매우 편리한 프로토콜입니다. 예를 들어 1G짜리 큰 파일을 상대방에게 전송한다고 하면 파일 내용을 읽어서 소켓으로 write()하기만 하면 tcp가 흐름 제어하면서 빠짐없이 전송하고 상대방은 수신합니다. 중간에 상대방이 수신을 못하면 그 부분을 다시 전송하는 등 tcp가 중간에서 열심히 일합니다.

그렇다면 udp/ip는? 상대방에게 미리 허락을 구하지 않습니다. 그냥 보냅니다. 상대방 시스템이 켜져 있는지 조차 모릅니다. 그러니 제대로 전달이 될지 보장이 없습니다.

또한, tcp처럼 상대방과의 흐름 제어가 없어서 데이터를 보내면 udp는 수신하는 쪽에서 한 번에 모두 받을 수 있도록 처리합니다. 그래서 udp 프로토콜의 이름이 user datagram protocol입니다. 사용자가 보내는 데이터 패킷 그대로 상대방이 받을 수 있게 하는 프로토콜이죠.

▲ tcp/ip를 택배 회사에서 컨베이어 벨트로 물건을 전달하는 모습으로 비교할 수 있습니다. 트럭 안에 물건이 얼마나 들어 있는지 모르지만, 하나씩 꺼내서 컨베이어 벨트에 올려놓으면 상대방에게 전달됩니다. 상대방이 초보라서 굼뜬다면 그 속도에 맞추어 전송자는 컨베이어 벨트에 물건을 올려놓습니다. 만일 떨어진 물건이 있다면 들어서 주거나 상대방이 챙길 때까지 기다리기도 합니다.

이렇게 tcp/ip는 송신자와 수신자가 서로 협업하듯이 전송 흐름을 확인하면서 데이터를 주고받습니다.

▲ 이에 비해 udp/ip는 짐을 몽땅 실고 한 번에 고객을 찾아가는 오토바이 택배로 생각하면 이해하기 쉽습니다. 집에 사람이 있는지 없는지 모릅니다. 일단 출발하고 받는 사람이 있으면 넘겨주고, 없으면 문 밖에 놓습니다. 당연히 오토바이에 실은 것( write 한 번)을 모두 전달 (read 한 번)합니다. 치안이 좋지 못해서 수신자가 없으면 도둑이 훔쳐갑니다.

스트림과 데이터 경계

tcp/ip에서 보내는 데이터를 특히, 스트림 데이터라고 하는데, 프로그래밍 초보자는 처음 보는 단어가 익숙지 않아서 이해가 어렵습니다. 저도 처음에는 뭔 소리인가 했는데요, 통신은 계속 데이터를 주고받는데 굳이 스트림(stream, 개울)이라고 할까?

그러나 개울을 보면 이해가 됩니다. 개울에 물이 흐르고 있는데, 언제부터 시작했는지 알 수 없습니다. 또 언제까지 흐를지도 모릅니다. 계속 흘러나오는데, 바가지로 물을 뜨면 물줄기가 가늘 때는 적게 담아지고 갑자기 콸콸 흐르면 바가지를 넘칩니다.

▲ tcp/ip는 개울물과 같아서 전송 데이터의 길이를 알지 못합니다. 더 정확히 말씀드리면, 전송하는 쪽에서 한 번에 write()한 데이터의 길이를 모릅니다.

그래서 전송 측에서 write() 한 번한 데이터가 수신 측에서는 한 번의 read()로 모두 읽거나 또는 세 번·네 번에 나누어 읽어야 모두 받을 수 있습니다. 반대로 송신 측에서 여러 번 write() 한 데이터가 수신 측에서 한 번의 read()로 모두 수신할 수도 있습니다.

문제는 수신 측에서 data A의 시작은 알겠는데, 어디까지가 data A이고 어디부터 data B의 시작인지 알 수 없다는 것입니다.

▲ tcp/ip로 이렇게 송수신이 되기도 합니다. 이렇게 되면 수신 측은 정말 data A와 data B 구분이 어렵습니다. data가 A와 B 두 개인지도 모르고요.

tcp/ip 데이터 통신

이럴 수밖에 없는 것이 tcp/ip는 수신 측 시스템에게 데이터를 안전하고 흘림 없이 보내주기는 해도 애플리케이션이 읽어 가는 것까지 확인하지는 않습니다. 송신 측에서 데이터를 전송하면 수신 측 시스템의 수신 버퍼가 비어 있을 때까지 계속 퍼다 나릅니다. 개울물을 흘리듯이.

즉, 전송하는 쪽에서 데이터 길이를 알려 주지 않으면 수신하는 쪽에서는 언제까지 계속 read() 해야 하는지 모릅니다. 다음 데이터가 또 언제 올지 모르는데 마냥 기다릴 수 없고.

▲ 그러므로 tcp/ip를 이용할 때는 데이터를 보내기 전에 미리 데이터 길이를 알려 주어야 합니다. 수신하는 쪽은 길이를 먼저 확인하고 그만큼 올 때까지 기다리면서 read() 합니다. 중간에 흘려버리는 바이트가 있으면 어떻게 하지요? tcp가 대신 흘린 것을 다 챙겨줍니다. 안심하고 상대방이 알려 준만큼 수신합니다. 물론 연결이 끊길 수 있으므로 time-out 체크는 해야 합니다.

▲ 또는 한 번 접속 요청에 하나의 파일을 보내는 방법이 있습니다. tcp/ip는 네트워크의 상태가 정상임에도 끊어지는 경우가 있습니다. 때로 자주 발생해서 프로그래머를 힘들게 하는데요, 그래서 한 번 접속에 한 번 전송 방법도 많이 사용합니다.

이처럼 데이터의 시작과 끝이 어디인지 모르는 것을 데이터 경계가 없다고 합니다.

udp/ip 데이터 통신

▲ 반면에 udp/ip는 오토바이 택배 그림처럼 송수신이 한 번에 완료됩니다. 오토바이 택배 가방 안에 있는 것이 수신할 모든 데이터입니다. 즉, 송신 쪽에서 한 번 write()한 데이터는 수신 쪽에서도 한 번의 read()로 모두 받을 수 있습니다.

이와 같이 데이터의 시작과 길이를 알 수 있는 udp/ip 통신을 데이터 경계가 있다고 합니다.

udp/ip에서 흘린 데이터를 못 받는 이유는?

그렇다면 처음 드린 문제에서 udp/ip 통신에서 수신 버퍼가 작아서 모두 받지 못했다면 왜 두 번째 read()로 나머지 데이터를 받지 못할까요? 그리고 받아서는 안 되는 이유가 무엇일까요?

만일 두 번째 read()에서 나머지 데이터를 받는 다면 데이터 경계가 모호해지기 때문입니다.

예를 들겠습니다. 수신 버퍼가 1024바이트입니다. 이번에 read() 했더니 버퍼 크기만큼 딱 1024 바이트입니다. 그리고 또 읽었더니 20바이트를 더 받았습니다. 그렇다면 상대방이 몇 번 write()했을까요?

udp/ip는 데이터 경계가 있고 한 번의 write()는 한 번의 read()로 모두 읽을 수 있다는 특성이 있어서 데이터 길이를 알려 줄 필요가 없다고 했습니다. 그런데 만일 프로세스 모두 가지고 가지 않았다고 나머지 데이터를 읽게 한다면 이 규칙은 깨지게 됩니다.

편하기는 udp, 안전한 것은 tcp

▲ tcp/ip는 마치 컨베이어 벨트처럼 송신자와 수신자가 서로 얘기를 나누면서 전송 속도를 맞추어 가며 데이터를 주고받습니다. 만일 흘린 물건이 있으면 서로 주워 주고요. 이렇게 서로 흐름 제어를 하면서 통신하기 때문에 데이터 순서가 흐트러지지 않습니다. 초록색·주황색·노란색 순서로 데이터를 전송하면 수신자도 그 순서대로 차례로 받습니다.

▲ 그러나 udp/ip는 여러 개의 데이터를 오토바이 택배에 맡긴 것입니다. 과연 오토바이가 출발한 순서대로 차례로 목적지에 도착한다고 보장할 수 있을까요? 제일 늦게 출발한 3번째 오토바이가 길을 잘 알아서 먼저 도착할 수 있습니다. 어쩌면 2번 오토바이는 사고로 도착하지 못할 수 있고요.

파일을 전송하려고 하는데 크기가 작아서 한 번에 write()하고 한 번에 read()할 수 있다면 udp/ip가 편하겠지만, 파일이 너무 커서 여러 개로 나누어서 write() 해야 한다면 여러 가지 신경을 써야 합니다. 파일의 조각이 차례로 전달될 수 있도록 파일 조각마다 번호를 붙여서 보내 주어야 하고, 수신자는 다음 번호가 아닌 조각을 받으면 버퍼링 하면서 받아야 할 조각을 기다려야 합니다. 기다려도 오지 않으면 송신자에게 요청해야 하고요. 또 못 받으면 다시 요청해야 하는데 이전에 받은 다른 조각은 파일에 저장하지 못하고 가지고 있어야 합니다. 아니면 버리고 못 받은 부분부터 다시 받거나요.

udp/ip로 파일 전송 프로그램을 만들면 tcp/ip가 얼마나 고마운지 깨닫게 됩니다. udp/ip는 사용 방법이 편해도 이런저런 상황을 고려하다 보면 프로그램이 매우 복잡해질 수 있습니다. 프로그램이 복잡해지는 만큼 디버깅 작업과 기간이 늘어납니다.

간편한 통신은 udp/ip가 편하고 빠르지만, 반드시 전송해야 하는 데이터, 꼭 순서를 맞추어 주어야 하는 데이터는 tcp/ip를 사용하는 것이 안전합니다.

이 댓글을 비밀 댓글로

티스토리 로그인이 풀리면 여기를 클릭하세요.

error: Content is protected !!