| 목차 |

2 소켓이란 무엇인가?

여러분은 “소켓”이란 단어를 자주 들을 것입니다. 그리고 어쩌면 그것이 정확히 무슨 뜻인지 궁금하시겠지요. 그것은 표준 유닉스 파일 설명자를 통해서 다른 프로그램과 이야기하는 방법을 의미합니다.

무슨 말이냐고요?

좋습니다. 아마 어떤 유닉스 해커들이 “이런, 유닉스에선 모든 것이 파일이야!” 라고 말하는 것을 들어보셨을 것입니다. 그들이 말하는 바는 유닉스 프로그램이 어떤 종류의 입출력을 하든, 파일 설명자에서 읽거나 파일 설명자에 쓰는 방식으로 동작한다는 의미입니다. 파일 설명자는 단순히 열려 있는 파일과 연관된 정수입니다. 그러나 (이 부분이 중요합니다.) 그 파일은 네트워크 연결이나 선입선출, 파이프, 터미널, 디스크에 있는 진짜 파일이나 다른 어떤 것이든 될 수 있습니다. 유닉스의 모든 것 은 파일입니다! 그러니 인터넷 너머의 다른 프로그램과 통신하고 싶다면 파일 설명자를 통해서 하는 것이 당연합니다.

“잘난 척은 알겠고, 그래서 이 네트워크 통신을 위한 파일 설명자를 어디에서 구하죠?” 라고 아마 지금쯤 생각하고 계실 것입니다. 그래도 답을 드리자면 socket() 시스템 콜을 호출하면 된다는 것입니다. 그것은 소켓 설명자를 반환하고, 여러분은 특화된 send()recv() (man send, man recv) 소켓 함수를 써서 그 소켓을 통해 통신합니다.

“그런데 잠시만요!” 아마 다른 의문이 생길 것입니다. “그게 그냥 파일 설명자라면, 어째서 그냥 평범한 read()write() 함수를 사용해서 소켓 통신을 하면 안 되나요?” 짧은 답은 “그래도 됩니다!”입니다. 긴 답은 “그래도 되지만, send()recv()가 데이터 전송을 더 많이 조정할 수 있게 해 줍니다”가 되겠습니다.

다음이 궁금하신가요? 세상에는 온갖 종류의 소켓이 있습니다. DARPA 인터넷 주소(인터넷 소켓), 로컬 노드의 경로(유닉스 소켓), CCITT X.25 주소(무시해도 상관없는 X.25 소켓), 그리고 여러분이 실행 중인 유닉스의 종류에 따라 다른 많은 종류의 소켓이 있습니다. 이 문서는 첫 번째 것에 대해서만 다룹니다. 바로 인터넷 소켓입니다.

2.1 두 종류의 인터넷 소켓

인터넷 소켓이 두 종류라니 무슨 소리냐고요? 사실 거짓말입니다. 더 많은 종류가 있습니다. 그러나 여러분을 겁주기가 싫었습니다. 여기서는 두 종류에 대해서만 이야기하겠습니다. raw 소켓도 있으며 아주 강력하니 여러분도 한 번쯤 살펴보시길 권한다는 지금 이 문장을 제외하고 말입니다.

이쯤 하고, 그 두 종류가 무엇인지 이야기하자면 하나는 스트림 소켓이고 다른 하나는 데이터그램 소켓입니다. 앞으로 이 둘을 각각 SOCK_STREAMSOCK_DGRAM으로 칭하겠습니다. 데이터그램 소켓은 때때로 비연결형 소켓이라고 불립니다. (그러나 그것도 정말로 필요하다면 connect() 함수를 사용할 수 있습니다. 아래의 connect()를 참고하세요.)

스트림 소켓은 신뢰성 있는 양방향 연결 통신 스트림입니다. 이 소켓으로 데이터 12를 차례로 보내면, 상대편에서도 같은 순서로 받습니다. 또한 전달되는 데이터에는 오류가 없습니다. 이것은 정말로 확실한 내용이어서, 저는 다른 사람들이 이것에 대해서 반박한다면 귀를 막고 노래를 불러서라도 무시하겠습니다.

스트림 소켓의 사용처가 궁금한가요? telnet이나 ssh 응용프로그램에 대해서 들어보셨나요? 그것들이 스트림 소켓을 사용합니다. 입력한 모든 문자가 입력한 순서 그대로 도착해야 합니다. 또한, 웹브라우저가 쓰는 하이퍼텍스트 전송 프로토콜 (HTTP)도 스트림 소켓을 사용합니다. 실제로, 어떤 웹사이트의 80번 포트에 텔넷으로 연결한 후 “GET / HTTP/1.0”을 입력하고 엔터를 두 번 치면 HTML을 돌려줄 것입니다.

여러분이 telnet을 설치하지 않았고 설치하고 싶지 않거나, 설치된 telnet이 클라이언트에 연결하는 것에 대해 까다롭게 군다면 이 안내서는 telnet과 유사한 프로그램인 telnot8과 같이 제공됩니다. 이 안내서에서 필요한 일은 다 할 수 있을 것입니다. 텔넷이 사실은 명세된 네트워크 통신 프로토콜9이며, telnot 은 이 프로토콜을 전혀 구현하지 않는다는 사실을 기억하세요.

스트림 소켓은 어떻게 데이터를 이렇게 신뢰성 있게 전송할까요? 이를 위해 스트림 소켓은 전송 제어 프로토콜(Transmission Control Protocol), 곧 TCP (TCP에 대한 지나치게 자세한 정보는 RFC 79310를 참고하세요) 라고 불리는 프로토콜을 사용합니다. TCP는 여러분의 데이터가 순서대로 도착하고 손상되지 않도록 보장합니다. TCP를 TCP/IP의 절반으로 이미 들어보셨을 것입니다. IP는 인터넷 프로토콜(Internet Protocol)의 약어입니다. (RFC 79111을 살펴보세요) IP는 주로 인터넷 라우팅을 맡으며 데이터 무결성에는 보통 책임이 없습니다.

좋습니다. 그럼 데이터그램 소켓은 뭘까요? 왜 비연결형일까요? 뭐가 다를까요? 왜 신뢰성이 없는 것일까요? 대답은 아래와 같습니다. 데이터그램을 전송하면 도착할 수도, 도착하지 않을 수도 있습니다. 도착은 하되 순서대로 도착하지 않을 수도 있습니다. 도착한다면, 패킷 안에 있는 데이터에는 오류가 없습니다.

데이터그램 소켓도 라우팅을 위해서 IP를 사용할 것입니다. 그러나 TCP를 사용하지는 않습니다. 데이터그램 소켓은 “User Datagram Protocol” 또는 “UDP”를 사용합니다. (RFC 76812를 참고하세요)

왜 비연결형인지에 대해서 간단히 말하자면 스트림 소켓과 달리 열린 연결을 유지할 필요가 없기 때문입니다. 패킷을 만들고, 목적지 정보를 담은 IP 헤더를 붙이고 보냅니다. 연결은 필요 없습니다. 데이터그램 소켓은 일반적으로 TCP 스택을 사용할 수 없거나 패킷 몇 개가 유실되어도 큰일이 나지는 않는 상황에서 쓰입니다. 몇몇 예제 프로그램은 다음과 같습니다. tftp (trivial file transfer protocol, FTP의 동생), dhcpcd (DHCP 클라이언트), 다중 사용자 게임, 오디오 스트리밍, 화상 회의 등등

“잠시만요! tftpdhcpcd는 하나의 호스트에서 다른 호스트로 이진 응용프로그램을 전송하기 위해 쓰이잖아요! 응용프로그램이 도착지에서 제대로 동작하길 바라면 데이터가 유실되면 안 되는 것 아닌가요? 데이터를 제대로 보내기 위해서 흑마법이라도 쓰나요?”

여러분, tftp와 유사한 프로그램들은 UDP 위에서 자신만의 프로토콜을 구현합니다. 예를 들어 tftp 프로토콜은 그들이 보내는 모든 패킷에 대해서 수신자가 “잘 받았습니다”라고 말하는 패킷(“ACK” 패킷)을 돌려줄 것을 요구합니다. 원본 패킷의 송신자가 일정 시간, 가령 5초 안에 응답을 받지 못한다면 송신자는 ACK 응답을 받을 때까지 패킷을 재전송합니다. 이 확인 절차는 아주 중요해서 신뢰할 수 있는 SOCK_DGRAM 응용프로그램을 만들 때 반드시 필요합니다.

게임이나 오디오, 비디오처럼 어느 정도 패킷 유실을 허용하는 응용프로그램은 유실된 패킷을 그냥 무시하거나 혹은 그 영향을 적절히 보정하려고 합니다. (퀘이크 사용자들은 이런 유실로 인한 영향을 전문 용어로 짜증나는 렉이라고 부릅니다. 여기에서 쓰인 “짜증나는”은 아주 극단적인 욕설을 대체한 표현입니다.)

신뢰할 수 없는 기반 프로토콜을 왜 사용하는지 궁금한가요? 이유는 단 하나, 속도입니다. 보내고 잊어버리는(fire-and-forget) 방식이 어떤 데이터가 안전하게 도착했는지 추적하고 데이터가 올바른 순서로 왔는지 등을 확인하는 것보다 빠릅니다. 대화 메시지를 보낸다면 TCP는 훌륭한 선택입니다. 게임 세상 안에 있는 각각의 사용자에 관한 초당 40번의 위치 정보 갱신을 전송한다면 한두 개의 정보가 유실되어도 상관없습니다. 그렇다면 UDP가 좋은 선택입니다.

2.2 저수준 넌센스와 네트워크 이론

프로토콜의 계층구조에 대해서 이야기했으니 네트워크가 실제로 어떻게 동작하는지 말씀드릴 차례입니다. SOCK_DGRAM 패킷이 어떻게 만들어지는지 약간의 예제를 보여드리겠습니다. 실용적으로는 이 절을 생략해도 좋습니다. 그러나 좋은 배경지식입니다.

자, 여러분, 데이터 캡슐화에 대해 배울 차례입니다. 이것은 아주 중요합니다. 너무 중요해서 치코 캘리포니아 주립대에서 네트워크 수업을 듣는다면 이것에 대해 배우게 될 수도 있습니다. 간단히 말하자면 이렇습니다. 패킷이 태어나면 패킷은 첫 번째 프로토콜(예를 들어 TFTP 프로토콜)에 의해 헤더로 감싸집니다(“캡슐화”) (드물게 푸터로도 감싸집니다). 그리고 (TFTP의 헤더가 포함되는) 전체가 다시 다음 프로토콜에 의해 감싸집니다 (예를 들어 UDP). 그리고 다시 IP로 감싸지고, 마지막으로 하드웨어(물리) 계층의 프로토콜(예를 들어 이더넷)으로 감싸집니다.

다른 컴퓨터가 패킷을 받으면 하드웨어는 이더넷 헤더를 벗겨내고, 커널이 IP와 UDP 헤더를 벗겨냅니다. 그리고 TFTP 프로그램이 TFTP 헤더를 벗겨내고, 마침내 데이터를 가지게 됩니다.

드디어 악명 높은 계층화 네트워크 모델(Layered Network Model) 에 대해서 이야기할 수 있습니다. 이 네트워크 모델은 네트워크 기능의 체계(system)를 묘사하며 다른 모델에 비해서 많은 장점을 가지고 있습니다. 예를 들어 여러분은 물리적으로 데이터가 어떻게 전송되는지(직렬통신(Serial), Thin Ethernet(얇은 동축케이블을 쓰는 이더넷의 변형), AUI(Attachment Unit Interface), 등등)(역자 주: 여기에 언급되는 물리적 통신 단자들은 대개 현재는 특수한 산업현장이 아니면 쓰이지 않습니다. 여러분이 이런 것에 대해서 모르신다고 해도 전혀 지장이 없다는 의미입니다.)에 대해서 전혀 신경 쓰지 않고 소켓 프로그램을 완전히 똑같은 모습으로 짤 수 있습니다. 그 이유는 저수준에 있는 프로그램들이 그것을 자동으로 처리해 주기 때문입니다. 실제 네트워크 하드웨어와 네트워크 구성 방식(topology)은 소켓 프로그래머에게 투명(역자 주: 알 필요가 없거나 알 수 없음)합니다.

길게 말하지 않고 이제 전체 모델의 계층을 제시하겠습니다. 네트워크 과목 시험을 위해서 이것을 기억하세요.

물리 계층은 하드웨어입니다(직렬통신, 이더넷 등). 응용 계층은 여러분이 상상할 수 있는 한 가장 물리 계층에서 먼 것입니다. 사용자가 실제로 네트워크와 상호작용하는 부분을 의미합니다.

사실 이 모델은 너무나 일반적이어서 정말로 원한다면 자동차 정비 안내서에도 쓸 수 있을 것입니다. 유닉스와 좀 더 일치하는 계층화 모델은 이렇습니다.

이 시점까지 오면 여러분은 아마도 이 계층들이 원본 데이터의 캡슐화에 어떻게 대응하는지 아실 수 있을 듯합니다.

간단한 패킷을 만드는 데 얼마나 많은 작업이 필요한지 아시겠나요? 그리고 패킷 헤더를 “cat” 명령으로 하나하나 직접 입력해야 합니다! 농담입니다. 스트림 소켓을 위해서 할 일은 그저 send() 로 데이터를 보내는 것뿐입니다. 데이터그램 소켓을 위해서 할 일은 여러분이 원하는 방식으로 패킷을 캡슐화하고 sendto() 로 내보내는 일뿐입니다. 커널이 여러분을 위해서 전송 계층과 인터넷 계층을 만들어주고 하드웨어가 네트워크 접근 계층을 만들어줄 것입니다. 현대 기술은 정말 멋지지요!

네트워크 이론에 대한 우리의 짧은 공부는 이렇게 끝납니다. 아, 라우팅에 대해서 말씀드리는 것을 잊었습니다. 그러나 라우팅에 대해서는 아무것도 다루지 않을 생각입니다. 라우터(router)가 IP 헤더만 남기고 패킷을 벗겨낸 뒤, 라우팅 테이블 (routing table)을 조회하고, 어쩌고 저쩌고 같은 내용은 하나도 이야기하지 않을 것입니다. 정말로 진짜로 알고 싶다면 IP RFC13을 참고하세요. 평생 모르고 살아도 문제는 없습니다.


| 목차 |