NetWork
난 아무래도 CS가 부족하기 때문에, 기사 자격증을 최근에 준비하고 책을 많이 보려 하고 있다.
특히, 개인적으로 통신과 보안 분야에 많이 약하다고 생각하기 때문에, 통신 분야에 대해 일단 하나씩 알아가려고 한다.
간단히 내가 모르는 것, 알아야 하는 것 전체적으로 정리해 가려고 한다.
내가 참고한 도서는 다음과 같다.
시스템을 연결하는 네트워크 구조
데이터는 네트워크를 통해 교환된다. 이 구조를 하나씩 알아보도록 하자.
어떤 웹 페이지에 브라우저를 사용해서 접속하는 경우의 통신 흐름을 가능한 상세히 설명해보라
라는 질문을 던져본다.
이는 내가 받았던 면접 문제와 100% 일치한다.
실제 네트워크 구조를 구체적으로 도식화 하기는 어렵다.
우리가 그걸 몰라도 쓸 수 있도록 되어 있기 때문이다.
먼저 계층 구조
를 생각하자.
계층 구조 는 기본적으로 역할 분담
을 말한다.
즉, 데이터나 기능 호출 흐름에 따라 계층 간 역할이 나누어진다는 특징 을 가진다.
또한, 각 층은 상호간에 어떤일을 하는 지는 알지만, 구체적으로는 알지 못한다. 즉, 은폐화 되어 있다.
서로 영향을 주지 않고 독립적으로 동작이 가능하고, 상호 간에 내부 처리를 은폐하고 있기 때문에 인터페이스만 바꾸지 않으면 각 계층의 내부처리는 바꾸어도 문제 없다.
정리하면 각 계층이 독립적으로 취급할 수 있으며 위와 같은 이유로 확장성이나 유연성이 뛰어나게 구축할 수 있다.
단점은 작업 효율을 희생해야 한다는 것
이다.
컴퓨터의 작업 효율은 성능을 의미하는데 하나의 일을 한 명이 하는 것보다 교대로 할때는 작업 인계의 오버헤드
가 발생한다.
그럼 계층을 좀더 자세히 보기전에, 소켓(Socket) 이 무엇인지 알아보자.
소켓(Socket)
간단히 이야기 해보면, 두 컴퓨터가 데이터를 주고받을 수 있도록 하는 것이 네트워크 프로그래밍
인데, 이 떄 필요한 것중 첫번째로 물리적 연결이다.
그러나 오늘날엔 인터넷이라는 거대한 네트워크로 연결되어 있기 때문에 물리적 연결을 신경 쓰지 않고, 이 물리적 연결을 기반으로 하는 소프트웨어적인 데이터 송수신 방법만 고민하면 된다.
그러나 이 것 역시 운영체제에서 소켓(Socket)
이라는 것을 제공해 해결한다.
즉 이는 물리적으로 연결된 네트워크상에서의 데이터 송수신에 사용할 수 있는 소프트웨어적인 장치
를 말한다.
좀더 정리해보면, 애플리케이션에서 TCP나 UDP를 사용할 때에는 운영체제가 갖고 있는 라이브러리를 이용하게 되는데, 이러한 라이브러리를 일반적으로 API(Application Programming Interface) 라고 한다.
TCP나 UDP를 사용하여 통신할 때에는 주로 소켓
이라는 API를 사용하는 것이다.
따라서, 애플리케이션은 소켓을 사용해 통신 상대의 IP주소나 포트 번호를 설정하거나 데이터 송수신 요청을 한다.
그래서 네트워크 프로그래밍을 소켓 프로그래밍이라고도 한다.
그럼 이것은 나중에 다시 말하도록 하고, 계층 구조부터 다시 이야기 해보자.
프로토콜
프로토콜(Protocol) 은 컴퓨터 상호간의 대화에 필요한 통신 규약
이다.
쉽게 말해 약속 이며, 서로 데이터를 주고 받기 위해 정의해 놓은 약속을 말한다.
흔히 네트워크 7계층을 말할 때, OSI 7계층 을 이야기 한다.
OSI 역시 한 때 많은 네트워크 독자 프로토콜들이 나올 때, 국제 표준으로 제정된 프로토콜이다.
그러나 최종적으로는 사장되었고 TCP/IP 프로토콜 이 사실상 표준이 되었다.
사실 TCP/IP 는 4~5계층으로 나누어진다. OSI 1~2계층을 모아 링크 계층, 5~7계층을모아 애플리케이션으로 취급하기도 한다.
링크 계층을 2개로 나누기도 하며, 대략 4계층으로 알고 있으면 된다.
그럼 통신 방식과 전반적인 계층간 흐름을 알아보자.
통신 방식
통신 방식 은 2가지로 나눌 수 있으며, 커넥션형(연결 지향형), 커넥션리스형(비 연결지향형) 으로 나누어 진다.
이런 통신 방식을 소켓에서는 소켓의 데이터 전송 방식
을 의미 한다.
먼저, 커넥션형부터 특징을 보면,
1. 중간에 데이터가 소멸되지 않고 목적지로 전송된다.
2. 전송 순서대로 데이터가 수신된다.
3. 전송되는 데이터의 경계가 존재하지 않는다.
즉, 통신 전후에 커넥션의 확립과 끊기 처리를 할 필요가 있다.
따라서, 상대가 통신할 수 없는 경우는 쓸데없이 데이터를 보내지 않아도 되는 장점이 있다.
또한, 어떤 문제만 없다면 데이터가 소멸되지 않으며, 먼저 보내진 데이터보다 뒤에 보내진 데이터가 일찍 도착할 수 없다.
마지막으로, 데이터의 경계는 다음과 같은 의미를 가진다.
사탕 100개가 여러 번에 걸쳐서 보내졌다. 그러나 받는 사람은 사탕 100개가 쌓인 다음에 이를 한번에 봉지에 담아 갔다.
이런 상황은 write, read 함수로 설명해보면 다음과 같다.
데이터를 전송하는 컴퓨터가 3 번의 write 함수 호출을 통해 총 100바이트를 전송하였다. 그런데 데이터를 수신하는 컴퓨터는 한 번의 read 함수 호출을 통해서 100바이트 전부를 수신하였다.
데이터를 송수신하는 소켓은 내부적으로 버퍼(buffer), 쉽게 말해 바이트 배열을 지니고 있다.
소켓을 통해 전송되는 데이터는 일단 이 배열에 저장된다.
떄문에, 데이터가 수신되었다고 해서 바로 read 함수를 호출해야 하는 것은 아니다.
이 배열의 용량을 초과하지 않는 한, 데이터가 채워진 후에 한번의 read 함수호출을 통해 데이터 전부를 읽을 수도 있고, 반대로 한번의 write 함수호출로 전송된 데이터 전부를 여러 번의 read 함수 호출을 통해 읽을 수도 있다.
따라서, read와 write 함수 호출 횟수는 커넥션형에서 큰 의미가 없다.
따라서 경계가 없다고 할 수 있다.
버퍼가 가득 차면 송신하는 소켓에서 더이상 전송하지 않는다. 이는 추후 제어와 관련되서 자연스럽게 알 수 있다.
그럼 다음으로 커넥션리스 형에 대해 알아보자.
1. 전송된 순서에 상관없이 가장 빠른 전송을 지향한다.
2. 전송된 데이터는 손실의 우려가 있고, 파손의 우려가 있다.
3. 한번에 전송할 수 있는 데이터의 크기가 제한된다.
즉, 커넥션리스 형에는 커넥션 확립과 끊기 처리가 없다.
송신하고 싶은 컴퓨터는 언제든지 데이터를 보낼 수 있다. 반대로 받는 쪽은 언제, 누구한테 받을 지 모른다.
따라서, 커넥션 리스형의 경우는 데이터를 받았는지, 받지 않았는지 항상 확인이 필요하다.
택배 혹은 우편물로 생각해보자.
이는 속도가 생명이며, 서로 다른 경로로 목적지로 향하며 출발 순서와 상관없이 도착한다. 또한 특성상 손실 및 파손의 우려도 있다.
그리고 그 용량 역시 제한된다.
많은 양일 경우 여러번에 나누어서 보내야 하고, 나누어진다면 받는 곳도 나누어진 만큼 여러번 받아야 한다.
따라서, 2번을 보냈으면 2번을 받아야 하는 과정이 필요하므로 경계가 존재한다
라고도 할 수 있다.
이런 성격을 한 문자으로 정리해 보면, 다음과 같다.
신뢰성과 순차적 데이터 전송을 보장하지 않는, 고속의 데이터 전송을 목적으로 하는 소켓
전반적인 흐름
먼저 송신 측과 수신 측으로 나눌 수 있다.
송신 측은 계층 위에서 아래로, 수신 측은 계층 아래서 위로 순서대로 데이터를 받는다.
각 계층에서는 상위층으로부터 받은 데이터에 자신의 계층 프로토콜 처리에 필요한 정보를 헤더 라는 형태로 덧붙인다.
즉, 수신 측에서는 수신한 데이터를 처리해 헤더
와 상위층으로 가는 데이터
를 분리한 후, 상위 층으로 데이터를 전달한다.
예를 들어, 웹 서버에서 사용하는 HTTP 프로토콜로 설명해보자.
HTTP는 애플리케이션 프로토콜이기 때문에 httpd 라는 프로세스를 사용한다.
HTTP 통신 데이터를 상대방에게 보내기 위해 TCP에 데이터를 보내지만, 여기서 이더넷 계층까지는 OS 커널 이 처리한다.
커널 내에서 TCP, IP, 이더넷을 담당하는 기능이 필요한 정보를 데이터에 부여해서 최종적으로 이더넷 프레임이 생성된다.
이것이 NIC(Network Interface Controller) 에 전달되서 이더넷 케이블 등을 통해 인접 노드를 공유해 최종 위치까지 도달한다.
그럼 애플리케이션 계층부터 하나씩 보자.
애플리케이션 계층
애플리케이션이 없으면 통신은 되지 않는다. 이용되는 애플리케이션 중에서 통신과 관련된 부분을 정해 놓고 있으며, 애플리케이션이 사용하는 프로토콜을 모두 애플리케이션 계층 프로토콜 이라 부른다.
해당 계층 프로토콜은 자신이 통신을 하는 것은 아니고, 통신 자체는 모두 OS, 즉 TCP/IP에 맡긴다.
예를 들면, 파일 전송이나 전자메일, 원격 로그인(가상 단말) 등을 실현하기 위한 프로토콜이 있다.
위 내용에 덧붙여, TCP나 IP와 같은 하위층 프로토콜은 애플리케이션의 종류와 상관없이 범용성이 높다.
그러나, 애플리케이션 프로토콜은 실용적인 애플리케이션을 구현하기 위해 각각 특유의 통신 처리
를 한다.
네트워크 애플리케이션은 다양한 개발자와 소프트웨어 제조업체가 만들고 있다.
기능을 구현하기 위해서는 애플리케이션끼리 통신할 때의 규약, 즉 애플리케이션 프로토콜이 필요하다.
애플리케이션끼리 주고받는 정보를 메세지 라고 한다.
애플리케이션 프로토콜은 메시지의 서식과 그것을 사용하여 제어하는 방법을 정해 놓고 있다.
애플리케이션 설계자나 개발자는 구현해야 하는 기능이나 이용 목적에 따라 일반적인 애플리케이션 프로토콜을 사용하기도 하고, 독자적인 애플리케이션 프로토콜을 정의하기도 한다.
즉, 무엇인가를 만드는 과정에서 프로그램의 성격에 따라 클라이언트와 서버간의 데이터 송수신에 대한 약속(규칙)들이 정해지는 데 이를 애플리케이션 프로토콜 이라 한다.
많이 쓰는 HTTP를 토대로 설명을 해보자.
브라우저에 URL을 입력해 요청이 웹 서버에 도달하면 응답으로 HTML 파일이 반환된다.
브라우저는 이 파일을 해석해서 파일 내에 추가 이미지나 스크립트 등이 포함되어 있으면 다시 웹 서버에 이들을 요청한다.
이렇게 몇번의 요청과 응답을 주고받게 된다.
요청에서 중요한 것은 서버에 던지는 명령이다.
HTTP Method를 이용하여 요청하며, 헤더 부분에는 부가 정보가 들어간다.
예를 들면 User-Agent
에는 브라우저 식별 정보(버전이나 IE 등의 브라우저 이름)를 가지며, Cookie
는 세션 식별자로 사용된다.
반면, 응답은 요청에 대한 결과와 그에 대한 상태 정보를 가진다. 또한 메시지 바디에 실제 데이터를 저장한다.
소켓과 연결해서 생각해보자.
애플리케이션 계층 프로토콜은 필요한 데이터를 소켓에 기록만 하고, 통신은 모두 TCP/IP에 일임한다.
소켓에 기록된 데이터는 다른 한쪽의 소켓으로 전달되며, 애플리케이션 자체가 통신 구조를 가지지 않고서도 원격지에 있는 서버 애플리케이션과 통신할 수 있다.
애플리케이션 프로세스가 네트워크 통신을 하는 경우, 커널에 TCP/IP로 통신하고 싶으니 상대방 애플리케이션과 통신할 수 있는 회선을 열어줘
라고 의뢰한다.
물론, 의뢰 방법은 시스템 콜 이다. 이때 접속 대상 서버의 IP 주소
와 TCP 포트
두가지 정도가 필요하다.
이 두가지 정보가 IP 도착 위치, TCP 도착 위치이다.
의뢰를 받은 커널이 소켓을 만들어 준다. 즉, 소켓을 만든 것 뿐으로 데이터를 보내기 위해 구멍을 만든 것이다.
이 후는 TCP를 사용하기 때문에 TCP를 사용한다는 것과 IP 주소 및 포트 번호 정보를 시스템 콜 경유로 커널에 전달하면, 접속 대상 서버와의 연결이 생성된다.
이때 상대방 서버에서도 소켓이 만들어지며, 상대 서버와의 사이에 가상 경로(버츄얼 서킷)가 생성된다.
실제 데이터는 물리적인 통신 케이블을 통해 긴 여정을 거쳐 겨우 상대방에게 전달 되지만, 프로세스 관점에서는 소켓이라는 구멍에 넣은(기록한) 데이터가 가상 경로를 통해 상대 통신 소켓 구멍으로 나오는 것이다.
간단해 보이지만, 커널이 열심히 여러가지 처리를 해 주고 있다.
TCP/IP 애플리케이션 층에서는 OSI 5, 6, 7계층을 망라하고 있다..
통신 커넥션 관리를 하는 세션층의 기능이나, 데이터 포맷을 교환하는 프레젠테이션층의 기능, 상대방 호스트와 데이터를 주고 받는 애플리케이션층의 기능은 모두 애플리케이션 프로그램이 담당한다.
프레젠테이션 계층
애플리케이션이 취급하는 정보를 통신에 적합한 데이터 형식으로 만들거나 하위층으로부터 올라온 데이터를 상위층이 처리할 수 있는 데이터 형식으로 변환하는 등 데이터 형식과 관련된 책임을 지고 있다.
좀더 구체적으로 말하면, 기기 고유의 데이터 표현 형식(데이터 포맷) 등을 네트워크 공통의 데이터 형식으로 변환하는 역할
을 한다. 동일한 비트열이라도 기기가 다르면 다른 의미로 해석될 가능성이 있는데, 이러한 무결성 을 책임진다.
데이터 표시 형식은 컴퓨터 시스템의 종류에 따라 다르다. 그 예시로 빅 엔디안, 리틀 엔디안 이 있다.
만약 시스템 고유 형식의 데이터를 전송한다 할 때, 먼저 네트워크 전체에서 공통된 표현 방식으로 바꾸게 되고, 수신측에 도착하면 다시 해당 시스템 고유 형식으로 바꿔 해석한다.
또 다른 말로 네트워크 전체에서 통일된 표현 방식
과 컴퓨터나 소프트웨어에 최적화된 표현 방식
을 서로 변환해주는 계층이라고도 할 수 있다.
예를 들면, 한글 인코딩 방식도 UTF-8, EUC-KR 등 다양한 부호화 방식이 있다. 이를 해결하는 것도 프레젠테이션 계층이다.
프레젠테이션 계층에서도 프레젠테이션 계층 사이에서 데이터의 부호화 방식을 식별하기 위해 헤더가 붙어진다.
그리고 실제로 데이터를 전송하는 처리는 세션층 이하에게 맡긴다.
한가지 예를 들어보면,
WWW(World Wide Web) 은 인터넷이 일반에게 보급될 수 있었던 원동력이 된 애플리케이션이다.
이 때 웹 브라우저라는 소프트웨어를 통해 네트워크 속을 여행할 수 있다.
이때 사용하는 프로토콜이 HTTP(HyperText Transfer Protocol) 이고, 송신에 사용되는 주요한 데이터 포맷은 HTML(HyperText Markup Language) 이다.
즉, WWW에서 HTTP는 OSI 참조 모델의 애플리케이션층의 프로토콜
, HTML은 프레젠테이션 계층의 프로토콜
이라 할 수 있다.
빅 엔디안과 리틀 엔디안
CPU에 따라서 4바이트 정수 1을 메모리 공간에 저장하는 방식이 달라질 수 있다.
4바이트 정수 1을 2진수로 표현하면 다음과 같다.
00000000 00000000 00000000 00000001
그러나 CPU에 따라 이를 반대로 저장할 수도 있다.
00000001 00000000 00000000 00000000
따라서 이를 고려하지 않으면, 송수신에 문제가 발생할 수 있다.
1. 빅 엔디안(Big Endian) - 상위 바이트의 값을 작은 번지수에 저장하는 방식
2. 리틀 엔디안(Little Endian) - 상위 바이트의 값을 큰 번지수에 저장하는 방식
예시로 알아보자.
0x20 번지를 시작으로 4바이트 int 형 정수 0x12345678을 저장한다 하자.
빅 엔디안 방식의 CPU에서는 다음과 같다.
0x20번지 | 0x21번지 | 0x22번지 | 0x23번지 |
---|---|---|---|
0x12 | 0x34 | 0x56 | 0x78 |
정수 0x12345678 중에서 0x12가 최상위 바이트, 0x78이 최하위 바이트다.
그러나 리틀 엔디안 방식에서는 다음과 같다.
0x20번지 | 0x21번지 | 0x22번지 | 0x23번지 |
---|---|---|---|
0x78 | 0x56 | 0x34 | 0x12 |
일반적으로 인텔계열 CPU는 리틀 엔디안을 사용한다.
따라서, 네트워크 바이트 순서(Network Byte Order) 로, 네트워크를 통해 전송할 때는 통일된 기준으로 데이터를 전송한다.
이 때의 약속은 간단히, 빅 엔디안을 사용
이다.
세션 층
커넥션의 확립과 끊기, 전송할 데이터의 분량을 설정하는 등과 같은 데이터 전송과 관련된 역할을 한다.
양쪽 호스트의 세션층에서는 데이터를 어떻게 보내면 효율적으로 주고받을 수 있을 것인지, 데이터는 어떤 방식으로 송신할 것인지와 같은 논의가 이어진다.
A가 B로 전자메일 5통을 보낸다고 할 때, 송신하는 데에는 여러가지가 있다.
먼저 메일을 하나 송신할 때마다 커넥션('접속'이라고도 하며, 가상 통신 경로를 말한다)
을 확립하고 끊는 방법이 있다.
또 다른 하나는 하나의 커넥션을 이용해 5통의 메일을 순서대로 송신하는 방법이 있다. 그 밖에 동시에 5가지 커넥션을 확립하고 5통을 병렬로 보내는 방법도 있다.
이러한 것을 판단하고 제어하는 것이 세션층의 역할이다.
또한, 세션층도 애플리케이션 층이나 프레젠테이션층과 같이 태그나 헤더가 붙어져 하위층으로 전달된다.
이 태그나 헤더에는 데이터를 어떤 방법으로 전달할 것인지 기록되어 있다.
트랜스포트(전송) 층
앞서 설명한 계층을 보면, 애플리케이션 층에서 작성한 데이터는 프레젠테이션층에서 부호화되고, 세션층에서 판단한 방법을 이용해 데이터가 전송된다.
그런데 세션층은 커넥션을 확립할 타이밍이나 데이터를 전송할 타이밍을 관리할 뿐 실제로 데이터를 전송하는 기능은 없다.
즉, 실제로 네트워크를 사용하여 데이터를 송신 처리하는 존재가 지금부터 시작할 전송계층과 그 하위 계층이다.
소켓에 기록된 애플리케이션 데이터(TCP/IP 기준, OSI로 5, 6, 7계층)는 커널 내에서 통신 대상에게 전달하기 위한 준비를 시작한다.
제일 먼저 임무를 수행하는 것이 전송 계층 프로토콜 인 TCP 이다.
호스트 A는 호스트 B로 가는 통신로를 확보하고 데이터를 전송할 준비를 하는데 이게 커넥션의 확립
이다.
이 통신로를 사용해 A에서 B안의 전자메일을 처리하는 프로그램까지 데이터를 전달할 수 있게 된다.
또한 통신이 끝나면 확립한 커넥션을 끊어야 한다.
이렇게 커넥션의 확립이나 끊기를 처리(세션층의 역할은 언제 수행할 것인지 이다.)하고 호스트 간의 논리적인 통신 수단을 만드는 것이 전송 계층의 역할이다.
또한, 데이터를 확실히 상대방에게 전달하기 위해 통신하는 컴퓨터 간에 데이터가 제대로 전달되었는지 확인하고, 전달되지 않았으면 재전송한다.
전송 계층의 가장 중요한 역할은 애플리케이션 프로그램 간의 통신을 구현하는 것이다.
컴퓨터 내부에서는 여러 프로그램이 동시에 작동하므로, 어떤 프로그램과 어떤 프로그램이 통신하고 있는지 식별할 필요가 있다.
이 때 애플리케이션 프로그램의 식별에는 포트 번호 라는 식별자를 사용한다.
전송 계층에는 크게 2가지 프로토콜이 있다.
TCP 와 UDP 이다.
TCP
TCP(Transmission Control Protocol) 는 명칭 그대로 전송을 제어하는 프로토콜로, 신뢰도가 높은 데이터 전송을 가리킨다.
그 역할을 간단히 말하면 애플리케이션이 보낸 데이터를 그 형태 그대로 상대방에게 확실히 전달하는 것이다. 단, 가능한 한 주변에는 민폐를 끼치지 않는다
라고 할 수 있다.
원래 신뢰도가 낮은 인터넷에서 사용하기 위해 만들어졌기 때문에 이런 역할이 주어졌다고 할 수 있다.
TCP가 담당하는 것은 어디까지나 서버가 송신할 때
와 서버가 수신한 후 애플리케이션에게 전달할 때
로, 상대 서버까지 전송하는 부분은 하위 계층인 IP에 모두 위임한다.
물론, TCP에 의존하지 않고 IP만으로 통신할 수 있지만, IP에는 데이터가 상대방에게 확실히 전달됐는지 확인하는 기능
이나 도착한 순서를 확인하는 기능이 없다.
TCP는 커넥션 형
으로, 신뢰성이 있는 스트림형(끊어짐이 없는) 전송 계층 프로토콜
이기 때문에, 양쪽 끝의 호스트 간에 데이터가 무사히 도착하는 것을 보증한다.
또한, 네트워크의 대역폭을 효과적으로 이용하는 장치나 네트워크의 혼잡을 완화시켜주는 장치같이 다양한 기능이 있어 신뢰성 향상을 꾀할 수 있다.
다만, 커넥션 확립 및 끊기 제어만을 위해 패킷을 7번이나 주고받으므로, 전송할 데이터의 총량이 적으면 쓸데없는 데이터가 늘어난다.
또한, 네트워크의 이용 효율을 향상시키기 위한 복잡한 장치를 이것저것 많이 가지고 있기 때문에, 비디오 회의의 음성이나 영상 데이터 같이 일정 간격으로 정해진 양의 데이터를 전송하는 통신에는 적합하지 않다.
중요 기능(서비스)만 정리하면 다음과 같다.
1. 포트 번호를 이용해 데이터 전송
2. 연결 생성
3. 데이터 보증과 재전송 제어
4. 흐름 제어와 폭주 제어
TCP로 (IP 데이터그램) 신뢰성 있는 통신을 하려면 많은 것을 고려해야 한다.
예를 들어, 데이터의 파손, 패킷 분실, 중복, 순서 바뀜 등과 같은 문제다.
따라서 체크섬, 시퀀스 번호, 확인 응답, 재전송 제어, 커넥션 관리, 윈도 제어 같은 기능으로 신뢰성 있는 통신을 실현한다.
TCP 소켓의 데이터 송수신은 경계가 없다. 따라서 서버가 한번의 write 함수호출을 통해 40바이트를 전송해도 클라이언트는 4번의 read 함수호출을 통해 10바이트씩 데이터를 수신하는 것이 가능하다.
그런데, 서버는 데이터를 한번에 40바이트를 전송했는데, 클라이언트가 여유 있게 수신할 수 있는 이유는 뭘까? 30바이트는 어디에 있는 걸까?
사실 write 함수가 호출되는 순간이 데이터가 전송되는 순간이 아니고, read 함수가 호출되는 순간이 데이터가 수신되는 순간이 아니다.
정확히 말하면 write 함수가 호출되는 순간 데이터는 출력버퍼로 이동하고, read함수가 호출되는 순간 입력버퍼에 저장된 데이터를 읽어 들인다.
즉, write 함수가 호출되면 출력버퍼라는 곳에 데이터가 전달되어 상황에 맞게 적절히(끊어서 보내든 한번에 보내든) 데이터를 상대방 입력 버퍼로 전송한다.
그러면 상대방은 read 함수호출을 통해 입력버퍼에 저장된 데이터를 읽어드린다.
그럼 입출력 버퍼의 특성을 정리해보자.
- 입출력 버퍼는 TCP 소켓 각각에 대해 별도로 존재한다.
- 입출력 버퍼는 소켓생성시 자동으로 생성된다.
- 소켓을 닫아도 출력버퍼에 남아있는 데이터는 계속해서 전송이 이루어 진다.
- 소켓을 닫으면 입력버퍼에 남아있는 데이터는 소멸되어 버린다.
그럼 클라이언트의 입력버퍼 크기가 50바이트인데, 서버에서 100바이트를 보내면 어떻게 될까?
이는, 입력버퍼의 크기를 초과하는 분량의 데이터 전송은 발생하지 않는다.
라고 할 수 있다.
그 이유는 TCP가 데이터의 흐름까지 제어하기 때문이다.
TCP에는 슬라이딩 윈도우(Sliding Window)
라는 프로토콜이 있는데 이는 잠시후에 다시 보도록 하자.
흐름을 다시한번 정리하면, 소켓에 기록된 애플리케이션 데이터는 소켓의 큐를 경유하여 소켓 버퍼 라 불리는 메모리 영역에서 처리된다.
소켓 버퍼 는 소켓별로 준비된 전용 메모리 영역
으로, 이후 계속되는 IP나 이더넷까지의 일련의 처리도 소켓 버퍼 내에서 이루어진다.
TCP는 세그먼트(Segment) 라고하는 단위로 데이터를 관리한다.
이 때문에 애플리케이션 데이터에 TCP 헤더를 붙여서 TCP 세그먼트를 작성한다.
헤더에는 도착 지점 포트 번호를 포함해서 TCP 기능을 표현하기 위한 정보가 기록된다.
하나의 TCP 세그먼트로 전송할 수 있는 최대 데이터 크기
를 MSS(Maximum Segment Size) 라고 한다.
최종적으로 링크 계층을 사용해 데이터를 전송하기 때문에 MSS는 링크 계층에서 전송할 수 있는 최대 크기에 의존하고, 환경이나 설정에 따라 달라진다.
이후에 다시 보겠지만, 링크 계층에서 전송할 수 있는 최대 데이터 크기
를 MTU(Maximum Transfer Unit) 이라고 한다.
예를 들어, 2000바이트의 데이터가 애플리케이션에 의해 소켓에 기록되었다고 하자.
대부분의 환경에서는 MSS가 1460 바이트(뒤에서 설명한다)이기 때문에 여기서도 1460바이트라고 가정한다.
그러면 당연히 2000바이트의 데이터는 1460바이트와 540바이트의 데이터로 분할한다.
즉, 각각의 TCP 헤더가 붙어서 두개의 TCP 세그먼트가 만들어진다.
포트 번호
데이터 링크나 IP에는 MAC 주소와 IP 주소가 있다. MAC 주소는 동일한 데이터 링크에 연결된 컴퓨터를 식별하기 위한 것이고, IP 주소는 TCP/IP 네트워크상에 연결되어 있는 호스트나 라우터를 식별하기 위한 것이다.
즉, 전송 계층에도 이와 같은 포트 번호 가 있다.
이 포트 번호는 동일한 컴퓨터 안에서 통신하고 있는 프로그램을 식별할 때 사용
한다.
즉 프로그램의 주소 다.
통신은 수신처의 포트 번호만으로 식별할 수는 없다.
두 컴퓨터 사이에 일어나더라도 수신처의 포트 번호는 80으로 똑같을 수 있다.
클라이언트에서 웹 브라우저 화면 2개를 열고, 동일한 서버상에 있는 다른 페이지를 동시에 보려고 한다고 할 때, 통신을 제대로 식별해야 하기 때문에 송신처의 포트 번호 로 각각 다른 통신임을 식별한다.
또한, IP주소와 포트 번호가 똑같더라도 TCP나 UDP를 나타내는 프로토콜 번호만 다른 경우
도 있다.
이 역시 다른 통신으로 취급한다.
송신처 IP 주소 | 수신처 IP 주소 | TCP | 송신처 포트 번호 | 수신처 포트 번호 | 데이터 |
---|---|---|---|---|---|
127.20.100.34 | 127.20.100.32 | 6 | 2001 | 80 |
송신처 IP 주소 | 수신처 IP 주소 | TCP | 송신처 포트 번호 | 수신처 포트 번호 | 데이터 |
---|---|---|---|---|---|
127.20.100.34 | 127.20.100.32 | 6 | 2002 | 80 |
송신처 IP 주소 | 수신처 IP 주소 | TCP | 송신처 포트 번호 | 수신처 포트 번호 | 데이터 |
---|---|---|---|---|---|
127.20.100.33 | 127.20.100.32 | 6 | 2001 | 80 |
포트번호를 결정하는 방법은 2가지가 있다.
1. 표준으로 정해져 있는 번호
2. 동적 할당법
기본적으로 정해진 포트번호가 있다.
이런 번호는 다른 용도로 사용하면 혼란을 야기하므로 사용하지 않는 것이 좋다.
그리고 잘 알려진 포트 번호이외에도 정식으로 등록된 포트번호가 있는데 이는 1024 ~ 49151 까지의 번호가 있다.
그러나 이 번호들은 다른 용도로 사용해도 큰 문제 없다.
두번째는 동적으로 할당하는 것
이다.
서비스를 제공하는 측(서버)은 포트 번호가 정해져 있어야 하지만, 서비스를 받는 측(클라이언트)
은 포트 번호가 반드시 정해지지 않아도 된다.
따라서 클라이언트 애플리케이션은 자신의 포트 번호를 결정하지 않고 운영체제에 일임
할 수 있다.
운영체제는 애플리케이션 별로 동일한 값이 되지 않도록 제어하면서 포트번호를 할당한다.
동적으로 할당하는 포트 번호에는 49152 ~ 65535 까지의 정수를 사용한다.
다음은 TCP 내부 동작원리로 상대 소켓과의 연결을 어떻게 하는지 알아보자.
TCP 소켓의 생성에서 소멸의 과정까지의 일을 나누면 3가지로 나누어 진다.
1. 상대 소켓과의 연결
2. 상대 소켓과의 데이터 송수신
3. 상대 소켓과의 연결종료
커넥션 생성
TCP는 소켓을 생성할 때도 매우 중요한 역할을 한다.
소켓은 전 이중(Full-duplex) 방식으로 동작하므로 양방향으로 데이터를 주고 받을 수 있다.
이를 위해 앞서도 말했지만, TCP는 연결형 프로토콜로 연결(connection) 이라 불리는 가상 경로(버추얼 서킷)를 생성한다.
즉, TCP가 통신을 시작하면서 통신 상대에게 지금부터 통신한다
라고 연락한 후 OK
사인을 받으면 그때 처음 생성된다.
물론, 통신을 받으려면 통신을 받는 애플리케이션 측(서버)이 미리 통신 준비를 하고 있어야 한다.
서버 프로세스는 OS에게 포트 번호 XXX 에게 통신 의뢰가 오면 나에게 연결해
라고 말한다.
이렇게 서버 측 소켓은 자신이 지정한 포트 번호에 통신이 오는지를 기다렸다가 받는다.
이 상태를 리슨(LISTEN) 하고 있다고 한다.
순서대로 정리해보면,
1. 통신을 시작하고 싶은 프로세스(클라이언트)는 커널에게 통신 개시를 의뢰한다.
2. 이 커널은 가상 경로를 생성하기 위해 상대방과 대화를 시작한다.
3. 먼저, 통신 상대인 서버 측 OS에게 가상 경로를 열도록 의뢰한다. (1)
4. 서버 측에서는 리슨하고 있는 포트 번호로 통신 요구가 온다. 서버는 문제 없으면 열어도 된다고 응답한다. (2)
5. 클라이언트 측도 확인했다는 메세지를 보내며, 이때 처음으로 통신용 가상 경로가 열린다. (3)
6. 이를 3-way handshaking 이라고 한다.
이 연결은 애플리케이션, 즉 두 개의 소켓 사이에 전용 회선이 있는 것처럼 통신한다.
구체적으로 어떻게 동작하는지 알아보자. (면접 관련해서 이전 포스팅에서도 정리한 적 있다.)
TCP 통신을 시작할 때 상대 서버에 포트 번호와 연결을 열어 달라
고 부탁만 할 뿐 다른 특별한 일을 하지는 않는다.
데이터 전송 자체도 IP에 위임하기 때문에 실제 물리적인 경로가 막히거나 통신 상대 서버가 갑작스런 장애로 전원이 꺼져도 가상적인 경로인 TCP 연결이 끊어지진 않는다.
이런 상태에서는 데이터가 전달되진 않지만
, 기본적으로 애플리케이션이 OS에게 연결 절단 의뢰
를 하거나 통신 대상이 에러를 보내
오지 않는 이상 TCP 연결 자체가 유지
되기 때문에 주의가 필요하다.
TCP에는 KeepAlive 타이머라는 설정이 있어서 여기서 설정한 시간동안 통신이 없으면 통신 상대가 존재하는지 확인하는 데이터를 여러 번 전송한다.
반응이 있으면 연결을 유지하지만, 반응이 전혀 없다면 연결을 닫아버린다.
이부분에 대한 참고는 다음을 보자.
https://beyondj2ee.wordpress.com/2014/01/27/tcp-keep-alive-%ED%80%B5-%EC%A0%95%EB%A6%AC/
그럼 좀더 알아보자.
[SYN] SEQ: 1000, ACK: -
이는 SEQ가 1000, ACK가 비어있음을 말한다.
여기서 SEQ 1000의 의미는 다음과 같다.
내가 지금 보내는 이 패킷에 1000이라는 번호를 부여하니, 잘 받았다면 다음에는 1001번 패킷을 전달하라고 내게 말해달라
이는 처음 연결 요청에 사용되는 메시지이기 때문에 SYN 이라 한다.
그리고 SYN은 Synchronization의 줄임말
로써, 데이터 송수신에 앞서 전송되는 동기화 메시지
라는 의미를 담고 있다.
그러면 B에서 A로 메세지를 보낸다.
[SYN+ACK] SEQ: 2000, ACK: 1001
이는 SEQ가 2000, ACK가 1001임을 뜻하는데, 여기서 SEQ 2000이 의미하는 바는 다음과 같다.
내가 지금 보내는 패킷에 2000이라는 번호를 부여하니, 잘 받았다면 다음에는 2001번 패킷을 전달하라고 내게 말해달라
그리고 ACK 1001이 말하는 것은 다음과 같다.
좀 전에 전송한 SEQ가 1000인 패킷은 잘 받았으니, 다음 번에는 SEQ가 1001인 패킷을 전송하기 바란다
즉, 처음 호스트 A가 전송한 패킷에 대한 응답 메시지(ACK 1001)
와 함께 호스트 B의 데이터 전송을 위한 동기화 메세지(SEQ 2000)
를 함께 묶어서 보내고 있다.
따라서 SYN+ACK 라고 한다.
이렇게 데이터 송수신에 앞서, 송수신에 사용되는 패킷에 번호를 부여하고, 이 번호정보를 상대방에게 알리는 이유는 데이터의 손실을 막기 위함
이다.
이렇게 패킷에 번호를 부여해 확인하는 절차를 거치기 때문에, 손실된 데이터의 확인 및 재전송이 가능한 것이고, 때문에 TCP는 손실 없는 데이터의 전송을 보장하는 것이다.
그럼 마지막 메세지를 알아보자.
[ACK] SEQ: 1001, ACK: 2001
앞서 보낸 한차례씩 송수신한 패킷에서 보았듯, TCP의 연결과정에서 패킷을 보낼 때에는 항상 번호를 부여한다.
그래서 SEQ 1001이 부여되었다.
앞서 보낸 패킷의 SEQ가 1000이었으니, 이번에는 이보다 1이 증가한 1001이 부여된 것이다.
그리고 이 패킷은 다음의 메시지 전달을 목적으로 전송되었다.
좀 전에 전송한 SEQ가 2000인 패킷은 잘 받았으니, 다음 번에는 SEQ가 2001인 패킷을 전송하길 바란다.
때문에 ACK 2001이 추가된 형태의 ACK 메시지가 전송되었다.
이로써 모든 연결 설정을 완료하였다.
데이터 송수신
데이터 송수신을 보며, 데이터 보증 과 재전송 제어 도 같이 보도록 하자.
먼저, 데이터 손실을 방지하는 기능을 보자.
이것이 확인 응답과 재전송에 의해 구현된다.
수신 측에 TCP 세그먼트가 도착하면 수신 측은 송신 측에게 도착했다는 것을 알린다.
이 때 반환하는 것이 ACK라고 하며, TCP 헤더에 ACK 관련 정보를 넣은 TCP 세그먼트를 반환한다.
즉, 하나의 TCP 세그먼트로 데이터 전송과 함께 앞서 도착한 데이터의 ACK를 동시에 반환한다.
이를 통해 왕복 횟수를 줄인다.
송신 측은 ACK가 돌아오는 것을 보고 전송한 세그먼트가 무사히 도착했다는 것을 알 수 있다.
ACK가 오지 않으면 전송한 TCP 세그먼트가 어떤 이유로 사라졌을 가능성이 있다.
이 때문에 언제든지 재전송이 가능하도록 전송이 끝난 TCP 세그먼트라도 ACK가 돌아오기까지는 소켓 버퍼에 남겨둘 필요
가 있다.
또한, 긍정이 아닌 부정 확인 응답(NACK(Negative Acknowlegement)) 을 보내, 모르겠다거나 못 들었다고 응답할 수도 있다.
위와 같이 ACK는 데이터 도착의 신뢰성을 실현한다.
또한, 데이터 순서를 보증 하기 위해서, 각 TCP 세그먼트에 시퀀스(sequence) 번호 라고 하는 숫자를 붙여 구현한다.
시퀀스 번호도 TCP 헤더에 기록되며, 해당 TCP 세그먼트가 가지고 있는 데이터가 전송 데이터 전체 중 몇 바이트째부터 시작하는 부분인지를 가리키고 있다.
예시를 들어보면,
3000 바이트
의 데이터를 보낼 때, 1460 바이트
, 1460 바이트
, 80 바이트
의 세가지 TCP 세그먼트로 분할했다고 하자.
첫 번째 세그먼트의 시퀀스 번호를 1, 두 번째를 1461, 세번째를 2921이라고 한다.
수신 측은 이 시퀀스 번호를 사용해서 원래 순서대로 조립한다.
이런 순차적 조합을 위해 수신 측은 ACK를 반환할 때 다음에 필요한 TCP 세그먼트의 시퀀스 번호도 ACK 번호로 전달한다.
이 때, 각 ACK번호는 다음과 같을 것이다.
ACK 번호 -> SEQ 번호 + 전송된 바이트 크기 + 1
그 예시는 다음과 같다. (앞선 예시의 데이터 값은 다르다)
열혈 TCP 프로그래밍 도서의 경우 위와 같이 표현해 두었지만, 개인적으로 학부수업으로 배울 때, 또 마스터링 TCP/IP 도서에는
ACK 번호 -> SEQ 번호 + 전송된 바이트 크기
로 나와 있다.
아마 하단의 내용이 맞을 듯 하다.
ACK가 오지 않으면 재전송하는데, 그 시점이 따로 있다.
첫번째는 타임아웃 이다.
일정 시간 내에 ACK가 돌아오지 않으면 재전송한다.
보내는 데이터가 분실 되었을 때 혹은 데이터는 도착했지만, ACK가 분실되었을 때가 있을 수 있고, 데이터를 재전송하게 된다.
데이터가 분실 되었을 때, 동일한 ACK가 계속 수신될 것이다.
예를 들어 SEQ 2921의 TCP 세그먼트가 경로 도중에 사라졌다면, 수신 측에서는 그 다음 TCP 세그먼트가 도착하지만, 도중에 사라진 세그먼트를 계속 요구하기 때문에 ACK에서는 SEQ 2921을 보내달라고 계속 응답한다.
따라서, 송신 측에서는 2번 연속으로 동일한 ACK가 왔을 때, 해당 번호의 TCP 세그먼트가 도착하지 않았다고 간주하고 재전송한다.
이를 중복 ACK(Duplicate ACK) 라고 한다.
1회 중복 ACK로 바로 재전송하지 않고, 3회까지 기다리는 데 이는 가끔 해당 세그먼트만 경로 도중에 지연돼서 도착 순서가 바뀔 수 있기 때문
이다.
데이터가 도착했는데 ACK가 분실된 경우, 수신측에서는 동일한 데이터를 중복하여 수신할 수 있다.
따라서 상위층 애플리케이션에 대해 데이터 통신의 신뢰성을 제공하기 위해 중복으로 수신한 데이터를 파기
해야 한다.
따라서 수신이 완료된 데이터를 식별하여 필요한지, 아닌지를 판단하는 장치가 필요하다.
이와 같은 확인 응답 처리, 재전송 처리, 중복 제어 등은 모두 시퀀스 번호
를 사용해 처리한다.
TCP는 세그먼트 단위로 데이터를 전송한다.
즉, 최대 세그먼트 길이(MSS) 단위를 이용한다.
이 최대 세그먼트 길이는 IP에서 분할 처리되지 않는 최대 데이터 길이
다.
TCP에서는 대량의 데이터를 송신할 때에 이 MSS값별로 데이터를 구분하여 송신한다.
또한, 재전송 처리도 기본적으로 MSS 단위로 이루어진다.
MSS는 3-Way Handshake 할 때 송수신 호스트가 서로 결정
한다.
각 호스트는 커넥션 확립 요청을 보낼 때에 TCP 헤더에 MSS 옵션을 붙여 자신의 인터페이스에 적합한 MSS를 통지한다.
이 때 양 쪽의 값 중 적은 쪽의 값이 MSS로 사용된다.
이제 흐름 제어 와 혼잡 제어 에 대해 알아보자.
데이터를 보증할 때 편리하게 하는 것이 SEQ와 ACK지만, 데이터를 보내고 ACK를 기다리는 것은 시간이 많이 걸리게 된다.
즉, 이와 같이 주고받는 것은 동기적
으로 처리하는 방법이다.
동기적 통신은 효율이 나쁘기 때문에 ACK를 기다리지 않고 전송하는 것이 좋다.
앞서, 중복 ACK 예시 이미지에서도 보듯이, ACK가 3번까지 반복될 때 계속해서 Segment를 보내는 것
을 볼 수 있다.
즉, TCP는 어느 정도 세그먼트 수라면 ACK를 기다리지 않고 전송
하는 윈도우 라는 개념을 가지고 있고, ACK를 기다리지 않고 전송 가능한 데이터의 크기
를 윈도우 크기 라고 한다.
윈도우에는 수신 측의 수신 윈도우
와 송신 측의 폭주(송신) 윈도우
두가지가 있다.
기본적으로 수신 측이 폭주 윈도우 크기를 조정해서 폭주 윈도우와 수신 윈도우 중 작은 쪽을 송신 윈도우로 채택하며, 이 범위 내에서는 ACK를 기다리지 않고 전송한다.
세그먼트가 분실된 경우에는 재전송 해야하므로, 송신 쪽에서는 확인 응답이 끝날 때까지 버퍼에 저장해 두어야 한다.
그러나, ACK가 오면 해당 TCP 세그먼트는 재전송할 필요가 없기 때문에 송신용 소켓 버퍼를 삭제하고 송신 윈도우를 다음으로 이동한다.
이와 같이 윈도우를 이동해 가는 방식을 슬라이딩 윈도우(Sliding Window) 라고 한다.
만약 윈도우 제어 중 세그먼트를 분실
하면, 어떻게 될까?
조심해야 할 것은 윈도우 크기 내에서 ACK가 오지 않더라도 보내는 것이지, ACK가 아예 오지 않는 것
이 아니다.
즉, ACK가 오는 것을 윈도우 크기만큼은 확인하지 않고 전부 보내는 것이다.
수신은 되었으나, ACK가 분실된 경우 윈도우 제어를 하지 않는다면, 재전송이 필요 없음애도 매번 재전송을 해야 했지만, 윈도우 제어를 한다면 조금 분실되어도 재전송할 필요가 없다.
그 이유는 다음 ACK 로 확인이 가능하기 때문이다.
상단의 이미지로 보면, 초록색 부분 내에서는 병렬로 모두 값들을 보낸다.
다만, 그 이상을 보내려면 ACK가 도착해야 보내며, 이때 마지막 세그먼트에 대한 ACK만 오면 넘어갈 수 있다.
따라서 제대로 온 ACK값 이전의 ACK는 오지 않더라도 제대로 도착했다는 것을 알 수 있다.
만약 송신할 때 세그먼트가 분실되면 어떻게 될까?
이는 간단히 앞서 말한 대로 3개의 중복 ACK를 받으면, 해당하는 세그먼트를 다시 보낸다.
그사이에 보낸 데이터들도 있을 수 있는데, 분실된 세그먼트에 대한 수신이 확인되면, 이미 보낸 것들에 대한 것도 다 계산해 ACK를 보낸다.
1~1000
, 1001~2000
을 보냈는데, 1001에 대한 세그먼트가 분실되었다고 하자.
그럼, 3번동안 ACK 1001
을 계속 보내게 된다.
그 사이에 2001~3000
, 3001~4000
, 4001~5000
, 5001~6000
, 6001~7000
까지 해당하는 세그먼트들을 보냈다고 하면, (3번동안 ACK를 보냈다하더라도, 시간 상 그 이상의 ACK를 보낼 수도 있다. 즉, 위에 모든 세그먼트 ACK는 1001이 되고, 송신측에서 3번을 확인한 세그먼트는 4001~5000
일 것이다.)
이후 다시 1001~2000
을 보냈을 때, 제대로 도착하면 ACK는 7001
이 되는 것이다.
이전의 값들은 다 제대로 받았기 때문이다.
흐름제어
앞서, 슬라이딩 윈도우
에 대한 개념을 설명 했다.
그에 이어서 설명 하자면,
수신 측은 수신용 소켓 버퍼가 넘쳐서 더 이상 수신이 불가능하게 되면 수신 윈도우를 작게 만들고 이 사실을 송신 측에 알린다.
송신 측은 수신 윈도우 크기 이상의 데이터는 ACK 없이 보낼 수 없게 된다.
이것이 TCP 흐름 제어(유량 제어) 다.
즉, 다른 처리에 시간을 뺏기거나 부하가 높을 때 데이터를 모두 수신받지 못할 수 있다.
만약 송신한 데이터를 수신 호스트가 모두 받지 못하면 해당 데이터를 재전송해야 하는데 이는 쓸데없는 통신이 될 수 있다.
따라서, 위와 같이 흐름 제어 를 하게 된다.
여기서 윈도 프로브(Window Probe) 라는 개념이 소개된다.
수신 버퍼가 가득차서 데이터 전송을 정지했을 때, 윈도 갱신 통지 패킷을 송신하여 통신을 재개한다.
이 윈도 통지 패킷이 도중에 분실되면 통신을 재개할 수 없을 가능성이 있으므로, 이 문제를 해결하기 위해 송신 호스트는 윈도 프로브(Window Probe)라는 1바이트의 데이터만을 포함한 세그먼트를 때때로 송신하여 윈도 사이즈의 최신 정보를 구한다.
혼잡제어
앞서, 윈도우 제어에 의해 1세그먼트 마다 확인 응답을 하지 않아도 대량의 데이터를 연속적으로 송신할 수 있었다.
하지만 통신을 시작할 때에 갑자기 대량의 패킷을 송신하면 다른 문제가 발생할 수 있다.
컴퓨터 네트워크는 서로 공유하는 것이 일반적이므로, 다른 통신에 의해 네트워크가 이미 혼잡해 있을 가능성이 있다.
혼잡한 네트워크에 갑자기 대량의 데이터를 송신하면 네트워크가 혼잡하여 제삼자에게 피해를 줄 수 있다.
TCP에서는 이를 방지하기 위해 통신 시작시에 슬로 스타트(Slow Start) 라는 알고리즘에 따라 데이터의 송신량을 제어한다.
즉, 흐름 제어 때, 폭주 윈도우 크기를 2 세그먼트, 4세그먼트 식으로 지수 함수적으로 늘려 나갈 수 있다.
연결 해제
연결 해제는 4-Way-Handshaking 을 이용한다.
그냥 연결을 끊어버리면, 상대방이 전송할 데이터가 남아있을 때 문제가 되기 때문에 상호간에 연결종료의 합의과정을 거치게 된다.
소켓A : 전 연결을 끊고자 합니다
소켓B : 아! 잠시만요
소켓B : 저도 준비 끝났습니다. 그럼 연결 끊으시지요
소켓A : 네 그동안 즐거웠습니다.
FIN 은 종료를 알리는 메세지를 뜻한다.
즉, 상호간에 FIN 메세지를 한번씩 주고 받고 연결이 종료되는데, 이 과정이 네 단계에 걸쳐서 진행되기 때문에 이를 가리켜 4-Way-Handshaking 라고 한다.
그리고 SEQ와 ACK의 의미는 앞 내용과 다르지 않다.
다만 위 그림에서 ACK 5001이 두번 전송되는 것을 볼 수 있다.
이는 FIN 메세지에 포함된 ACK 5001은 앞서 전송한 ACK 메세지가 수신된 이후로 데이터 수신이 없었으므로
재전송 된것이다.
이렇게 TCP 통신을 하기위해서는 최소 7번(연결 시 3번, 해제시 4번) 이상의 패킷을 주고받게 된다.
UDP
UDP(User Datagram Protocol) 은 TCP와 달리 커넥션리스형
으로, 신뢰성이 없는 전송층 프로토콜
이다.
UDP는 송신한 데이터가 상대에게 도달했는지, 아닌지를 확인하지 않는다.
패킷이 상대방에게 도착했는지나 상대방 컴퓨터가 제대로 네트워크에 연결되어 있는지 등의 확인이 필요한 경우에는 애플리케이션 프로그램
이 수행하도록 되어 있다.
애플리케이션 프로그램이 수행하는 특징 때문에 애플리케이션을 만든 사용자가 말하는 대로 하는 프로토콜
이라고도 한다.
UDP는 패킷 수가 적은 통신이나 브로드캐스트, 멀티캐스트 통신, 비디오나 음성 등의 멀티미디어 통신에 적합하다.
UDP는 편지로 생각하면 편하다. 편지에 보통 봉투에 보내는 사람과 받는 사람 주소정보를 쓴다.
그리고 우표를 붙여 우체통에 넣으면 끝이다.
다만 상대방의 수신여부를 확인할 길이 없다. 물론 전송도중에 편지가 분실될 확률도 있다.
UDP는 TCP에 비해 훨씬 간결하다.
ACK와 같은 응답 메시지를 보내는 일도 없고, SEQ와 같이 패킷에 번호를 부여하는 일도 없다. 따라서 TCP보다 성능면에서 우수하다.
또한, 생각보다 데이터 손실이 적으며, 신뢰성보다 성능을 생각할 때는 UDP를 자주 사용한다.
다만, TCP는 신뢰성이 없는 IP를 기반으로 신뢰성이 있는 데이터의 송수신을 위해 흐름 제어(Flow Control)을 한다 했는데, 이 흐름제어가 UDP에 없다.
즉, 혼잡 제어, 재전송 처리, 도착 순서 모두 하지 않고 단지 체크섬 을 이용해 데이터의 신뢰성을 제공한다.
네트워크 계층부터는 다음 포스팅에 하도록 하겠다.