초기 단계는 미국의 국방부를 지원했던 시기였다. 초기에는 일부가 고장나더라도 전체 시스템이 계속해서 동작할 수 있는 안정성을 중요시했었다. 이 당시에는 보안에 대한 고려사항은 없다.
1970년대에는 Alohanet 과 ARPANET 과 같은 초기 네트워크 시스템들이 개발되었고, 이때 인터넷의 기본 모델과 Ethernet 이 처음으로 등장했다. ARPANET 은 인터넷의 초기 형태를 형성하며, 컴퓨터들이 서로 통신할 수 있는 기술적 토대를 마련하였다.
1980년대에는 TCP/IP 프로토콜이 등장하여 네트워크 통신을 위한 기본 프로토콜로 사용되었다. SMTP 는 이메일을 보내고 받는 데 사용되며, DNS 는 도메일 이름을 IP 주소로 변환하는 데 사용된다. FTP 는 파일을 전송하기 위한 프로토콜이고, TCP 혼잡 제어는 네트워크 혼잡을 관리하기 위한 방법으로 개발되었다. 이러한 프로토콜과 기술들은 인터넷의 발전과 확장에 중요한 역할을 하였다.
네트워크 계층은 다른 여러 라우터를 통한 라우팅을 비롯한 패킷 전달을 담당한다. 보스턴에 있는 컴퓨터가 캘리포니아에 있는 서버에 연결하려고 할 때 그 경로는 수백 만 가지다. 이 계층의 라우터가 이 작업을 효율적으로 처리한다.
패킷이란 전송 시 전송 단위를 말하고, 라우팅이란 데이터를 보낼 때 최적의 경로를 선택하는 과정이다. 3계층 장비는 라우터와 L3 스위치가 해당된다.
정리하자면 3계층은 네트워크를 논리적으로 구분하고 연결하는 계층으로 이때는 논리적 주소를 사용한다.
하는 일을 정리하면
1. IP 패킷 송수신
2. IP 패킷 전달
3. IP 주소와 이에 대한 경로 찾는 기술
을 수행한다.
IP 패킷 송수신을 이해하려면 IP 패킷 헤더 구조에 대한 이해가 필요하다.
IP 패킷 전달을 이해하려면 라우터의 패킷 포워딩 기술에 대한 이해가 필요하다.
IP 주소와 이에 대한 경로 찾는 기술을 이해하려면 IP 주소에 대한 목적지까지 경로 찾는 알고리즘 및 자료구조에 대한 이해가 필요하다.
아래 이미지는 Alice 의 컴퓨터에서 Bob 의 컴퓨터로 데이터를 보낼 때 네트워크 계층에서 겪게되는 서비스를 그림으로 나타낸 것이다.
앨리스 네트워크 계층에서 캡슐화하여 라우터로 전송 -> 라우터에서 언캡슐화 후 다시 캡슐화해서 다른 라우터로 전송 전송 전송 후 밥의 컴퓨터로 도착해서 언캡슐화
데이터를 전송할 때는 패킷 단위로 전송하므로 IP 패킷 만들기(Packetizing) 가 필요하다.
아래 과정은 인터넷 상에서 웹 브라우저를 클릭했을 때 겪게되는 과정인데 이때 빨간색으로 표현한 부분이 네트워크 계층에서 하는 일이다. 패킷으로 만드는 것은 캡슐화 과정이다.
1. 웹 브라우저 클릭
2. 클릭에 대한 내용을 HTTP 메시지로 만들기
3. HTTP 메시지 TCP 세그먼트로 만들기
4. TCP 세그먼트를 IP 패킷으로 만들기 (encapsulation)
5. IP 패킷을 이더넷 프레임으로 만들기
6. 이더넷 프레임을 0,1 비트로 만들어서 보내기
7. 비트를 아날로그 신호로 보내기
네트워크 계층의 기능은 크게보면 두 가지로 나눌 수 있다.
- Routing(라우팅) : "길 찾기 기능" // 라우팅 프로토콜 예시 : OSPF, BGP - Forwarding(포워딩) : "앞으로 보내는 기능" // Ex. IP
네트워크 계층을 두 계층으로 나누면 다음과 같다.
●Data Plane// 포워딩과 관련된 계층 [실제 패킷(데이터)를 전달하는 계층]
●Control Plane // 라우팅과 관련된 계층
기존의 라우터는 Data Plane과 Control Plane이 하나의 라우터에 묶여있었다면,
SDN의 컨셉은 Data Plane과 Control Plane을 분리한다.(Decoupling)
라우터는 데이터 패킷의 포워딩에만 집중할 수 있도록 하고,
기존의 Control Plane이 하던 라우팅은 원격의 중앙 집중화된 서버에서 담당하도록 한다.
이 원격 서버를 SDN 컨셉에서는SDN Controller라고 한다.
아래 그림은 네트워크 계층에서의 기능을 그림으로 나타낸 것이다.
네트워크 계층에서는 경로를 선택하므로, 경로 선택 알고리즘들을 가진다. 이 알고리즘들이 routing protocol 과 SDN 컨트롤러에 구현되어 있고, 이 선택된 최적의 경로를 포워딩 테이블(라우팅 테이블)에 저장해둔다. (라우팅이란 라우팅 알고리즘을 이용하여 포워딩 테이블을 만드는 과정으로 이해할 수 있다)
그 후 포워딩이란 과정이 필요한데, 포워딩이란 포워딩테이블에 적힌 목적지 주소에 대응된 출력 포트로 패킷을 이동시키는 작업이다. 이 과정은 스위칭이라고도 한다.
다음은 IP 프로토콜에 대해 알아보자
네트워크 계층에서는 IP 프로토콜을 사용한다.
IPv4 프로토콜
네트워크 상 데이터를 교환하기 위한 프로토콜
데이터가 정확하게 전달되는 것을 보장하지는 않는다. (4계층 TCP에서 보완)
중복된 패킷을 전달하거나 패킷 순서를 잘못 전달할 가능성 존재 (악의적으로 이용시 Dos 공격)
HTTP 는 hyperText Transfer protocol 의 약자로 웹의 응용계층 프로토콜으로 웹의 심장이라고 할 수 있다.
HTTP 는 RFC 1945 , RFC 7230, RFC 7540 에 정의되어있다.
HTTP 는 두가지 프로그램에 구현되어있다. 하나는 클라이언트 프로그램이고 다른 하나는 서버 프로그램이다.
클라이언트와 서버 프로그램은 다른 end system 을 실행하며 서로서로 소통한다. 소통은 HTTp 메시지를 교환함으로써 이루어진다.
HTTP 는 이 메시지의 구조를 정의하고 클라이언트와 서버가 메시지를 교환하는 방법을 정의한다.
HTTP 를 자세하게 설명하기 전에 우리는 웹 terminology 를 살펴봐야한다.
웹 페이지는 객체들로 구성된다. 객체란 파일을 생각하면 된다. 예를들어 HTML 파일이나 JPEG 이미지나 자바스크립트 파일이나 CSS 파일을 의미한다. 그리고 이것들은 하나의 URL 을 통해 접근 가능하다. 대부분의 웹 페이지들은 기본 HTML 파일에 몇개의 참조된 객체들로 구성된다
라는 URL 이 있을때 www.someSchool.edu 란 호스트네임을 의미하고 /someDepartment/picture.gif 는 해당 호스트의 객체에 접근하기위한 path 이다.
웹 관점에서 웹 브라우저(크롬, 사파리)는 HTTP 의 클라이언트 입장이므로 여기서
우리는 브라우저와 클라이언트를 비슷한 개념으로 사용하게 될 것이다.
서버는 아파치나 마이크로소프트 인터넷 이포메이션 서버가 포함될 수 있다.
HTTP 는 어떻게 웹 클라이언트가 웹 페이지를 서버에게 요청하고 어떻게 서버가 웹 페이지를 클라이언트에게 전송하는지에 대해 정의한다.
HTTP 는 TCP 를 사용한다. HTTP 클라이언트는 먼저 서버와 TCP 연결을 생성한다. 한번 연결이 구축되면, 브라우저와 서버프로세스들은 소켓 인터페이스를 통해 TCP에 접근한다.
HTTP with Non-Persistent Connections
TCP 전송 시 three-way handshake 방식을 사용한다.
이 방식은 클라이언트카 작은 TCP 세그먼트를 서버에게 보낸다. 그 서버는 작은 TCP 세그먼트로 받았다는 확인을 보낸다. 그 후 클라이언트는 다시 알았다는 확인을 서버에 보낸다.
처음에 요청 메시지가 서버에 도착하면, 서버는 TCP 연결에 HTML 파일을 보낸다. 이 HTTP 요청/응답은 다른 RTT를 소모한다. 그래서 전체 응답 시간은 2개의 RTT 에 HTML 파일의 서버에서의 전송 시간을 더한 값이다.
HTTP with Persistent Connections
non-persistent 연결은 몇가지 단점을 가진다. 먼저 새로운 연결이 항상 생서되고, 각각의 요청 객체에 대해 유지되어야한다. 이러한 연결들을 위해 TCP 버퍼들은 반드시 할당되고 TCP variable들은 항상 클라이언트와 서버 모두에서 유지되어야만 한다. 수백개의 다른 클라이언트에게 동시에 요청을 수행해야해서 이것은 웹서버에 상당한 부담을 줄 수 있다.
따라서 HTTP/1.1 의 persistent 연결은 서버가 응답을 보낸후 TCP 연결을 연 채로 남겨둔다. 그 후 클라이언트와 서버 사이에서 2차적인 요청과 응답은 같은 연결로 보내질 수 있다. 특히 전체 웹페이지는 하나의 persistent TCP 연결로 전송된다.
HTTP 서버는 만약 특정 시간동안 사용되지 않을 때 연결을 닫는다.
HTTP 디폴트 버전은 파이프라이닝을 통해 persistent 연결은 사용한다.
HTTP 메시지 포맷
RFC 1945,RFC 7230, RFC 7540 에서 HTTP 메시지 포맷의 정의가 포함된다. HTTP 메시지는 요청과 응답 메시지로 구분되는데 둘다 이 정의 아래서 논의되고 있다.
요청 메시지
HTTP 요청 라인, 헤더라인, 공백, 데이터로 구성된다.
우선 요청 메시지는 ASCII 코드로 작성된다. 그리고 메시지는 5개의 라인으로 구성된다. 각각의 라인은 그 후, 캐리지 리턴과 라인피드가 따른다. <CR><LF> \r\n
마지막 라인은 추가적인 캐리지 리턴과 라인피드가 따르는데 이것은 공백을 의미한다.
요청 메시지는 더 많은 라인을 가질 수 있고 더 적을 수 있다. HTTP 요청 메시지의 첫번째라인은 요청 라인으로 불린다. 부수적 라인들은 헤더라인으로 불린다.
요청라인은 세개의 필드를 가진다. method 필드, URL 필드, 그리고 HTTP 버전 필드이다.
메소드 필드는 각각의 다른 값을 가질 수 있다 ( GET, POST, HEAD, PUT , and DELETE)
HTTP 의 가장 다수는 GET 이다. 이것은 브라우저가 객체를 요청할 때 사용한다.
다음은 헤더라인이다. 헤더라인은
호스트, Connections (persistent connection이 끊겼는지) , User-agent, Accept-language 를 포함한다.
위 예시에서는 Host: www.someschool.edu 를 보면 호스트는 www.someschool.edu 이며, Connection: closer 를 보면 서버가 요청된 객체를 보낸 후 연결을 끊길 원한다는 의미를 지닌다. 그 후 User-agent: Mozilla/5.0 을 보면, 브라우저의 타입이 모질라/5.0 으로 파이어폭스 브라우저라는 것을 알 수 있다. 또 Accept-language: fr 로 헤더는 유저가 객체의 프랑스어 버전을 받길 원한다는 것을 나타낸다.
그 다음은 바디가 나온다.
응답 메시지
아래의 전형적인 HTTP 응답 메시지를 보면, 이 응답 메시지는 아까 논한 요청 메시지에 대한 응답이 될 수 있다.
혼잡은 퇴근할때 도로에서 만나는 혼잡이랑 비슷하다. 다만, tcp 에서의 혼잡은 도로가 아닌 인터넷에서 트래픽이 많다면 혼잡이 발생하는 것이다.
웹 서버 터졌다고 할 때
증상은
1. 차타서 고속도로 오래 걸리면 시간도 오래걸린다.(long delays (queueing in router buffers))
2. 가끔씩보면 도저히 난 못참아 하고 이탈할 수도 있다 (packet loss (buffer overflow at routers))
우리가 경험하는 패킷손실의 대부분은 혼잡 때문이다.
체크섬은 받아야지 계산하지만 패킷 손실을 아예 못받아서 체크섬을 계산할 수 없다. 체크섬 계산이 잘못됐다는 건 0.01 퍼센트밖에 안되고 99퍼이상은 패킷 로스이다. 우리가 경험하는 혼잡의 증상 증 많은 경우는 딜레이 증가와 패킷로스이다.
흐름 제어는 다르다. 흐름제어는 수신자에 대한 속도 제어이다. 순전히 수신자의 상태를 보고 판단하는 게 흐름제어고 이것은 네트웤 상태와는 관계가 없다.
혼잡 이유와 비용
혼잡은 라우터에 버퍼가 있는데 중간에 메모리에 들어갔다 나온다. 메모리가 결국 버퍼, 큐이다. 버퍼는 이론적으로 이상적으로는 패킷 로스 없다 계산하지만 현실은 아니다. 결국은 실제 버퍼는 제한되어있어서 패킷 로스가 일어난다.
도로 막혔는데 더 보내는 악순환 : 버퍼 제한 때문에 발생 / 딜레이가 계속발생하다보니 패킷 로스 발생
그래서 굉장히 중요한 문제고 이게 해결되지 않으면 인터넷은 작동하지 않는다. 서버에 스레드 테스트 로드테스트 한다고 치면 누구나 많이 보내려한다. 인터넷 뿐만아니라 실생활도 그렇다. 나는 빨리 받고 싶어서 이기적으로 행동한다. 내가 빨리 받고 싶다. 다른사람도 빠르게 받고싶다. 내가 가해자 혹은 피해자가 될 수도 있는 것이다. 이러한 불공정 문제는 해결되어야한다.
그 문제를 해결하는게 혼잡제어이다.
인터넷에서 혼잡 처리 방식: 탐지와 제어
인터넷에 혼잡제어는 다 되어있다. 이것은 전송계층에 기능이 있다. TCP 프로토콜에 혼잡 제어가 구현되어 있다.
혼잡이 일어났다 판단하는 신호는 패킷로스이다. 보냈는데 사라졌다든가..
재전송 또는 중복된 ACk 이 3개 이상 올 떄 혼잡이 발생했구나 탐지할 수 있다.
혼잡 제어는 이기적으로 행동하지 않고 모두가 양보해야한다. 그래서 혼잡이 일어나면 보내는 양을 줄이는 것이 기본적인 철칙이다.
TCP에서 혼잡제어를 구현하게 된 이유는 혼잡제어를 하지 않아서 망해봤기 때문이다. 실제로 1986년에 사건이 있었다
- Congestion collapse 사건: 1986 NSFNET meltdown (혼잡한 인터넷 경로를 공유하는 많 은 송신자들이 재전송하여 인터넷 이마비된사건)
처음엔 TCP는 라우터로부터 신호를 받는게 없어서 TCP는 수신자의 ACK정보로부터 congestion에 대해서 알아야했다
Congestion Collapse in 1986 • Congestive collapse was identified as a possible problem by 1984
• It was first observed on the early Internet in October 1986, • when the NSFNET phase-I backbone dropped three orders of magnitude
from its capacity of 32 kbit/s to 40 bit/s, • which continued until end nodes started implementing Van
Jacobson and Sally Floyd's congestion control between 1987 and 1988
( 사람들 기억하기 _ 아마 중간고사..?)
When more packets were sent than could be handled by intermediate routers, the intermediate routers discarded many packets, expecting the end points of the network to retransmit the information.
However, early TCP implementations had poor retransmission behavior.
When this packet loss occurred, the endpoints sent extra packets that repeated the information lost, doubling the incoming rate.
라우터로부터 신호받는게 초반에 없었는데 갑자기 들어왔다고 했다. 그게바로 ECN 부터 적용됐다고 생각하면된다
TCP congestion control: AIMD
AIMD 로 구현한 방식을 살펴보자
혼잡 일어나면 절반으로 떨어뜨리겠다.이게 핵심이다. 속도는 단위시간당 보내는 양으로 윈도우 크기임.
혼잡이 일어났다(=패킷로스발생)
두번째 철학이 비동기 알고리즘이다. 모든 사용자는 각각 알아서 독립적으로 작동한다. 이렇게 해야 아까 얘기한대로 공평한 자원을 누릴 수 있고, 그렇게 안정화가 될 수 있다.
혼잡 제어 : 디테일
ACK 받고 보내려고 하는 순간이 표로 되어있다./ 결국은 속도는 cwnd / RTT 이다. (cwnd 는 윈도우 크기이다. tcp 헤더에 보면 리시버 윈도우 크기가 있었다. 그것이 바로 awnd 이다.)
우리가 tcp 헤더에는 리시버 윈도우 크기가 있었다. 그걸 다시 상기하며
아래 사진을 보자.
cwnd는 송신자 윈도우 크기 awnd 는 수신자 윈도우 크기이다.
따라서 윈도우 사이즈는 cwnd 와 awnd 중 더 작은 것을 취한다.
슬로우는 1부터 시작해서 슬로우다. 보내는 단위는 지수함수 단위로 증가한다.
슬로우는 언제 시작될까?
커넥션 시작하면 슬로우 스타트 + 재전송 타이머로 로스가 탐지 될때 이다. 타임아웃이 되면 다시 슬로우 스타트한다.
▪summary: initial rate is slow, but ramps up exponentially fast
(처음엔 느린데 점점 기하급수적으로 빨라짐)
• initially cwnd = 1 MSS ( MSS 보통 1460 바이트)
짧은 플로가 많으면 IW 늘리는 것도 좋은 방법이다.
1로 세팅하면 다시 1 2 4 8 ~ 보내는 양이 어느때부터는 (threshhold) 그떄부터는 슬로우스타트 끝내고 천천히보내~ 하다가 어떤 타이머로 뚝 1로 떨어짐.Tahoe버전이 초창기 버전 그다음 Reno슬로우스타트에는 새로운 ACK 오면 윈도우+MSS / 혼잡피할때 새로운 액 오면 윈도우
블로킹은 프로세스가 시스템콜을 하고 난 후(IO 같은거) 결과가 리턴될때까지 기다리고 있는 상태이다. 블록이 되어있는 상태라고 이해하면된다.
논블로킹은 시스템을 호출한 직후 (IO 같은 것을) 기다리지 않고 바로 다음을 처리하는 것이다. 종료가 되면 그때 이벤트를 받아서 처리한다.
다음 예제에서 블로킹, 논블로킹을 살펴볼 수 있다.
왼쪽에 있는 코드는 서버 프로그램이다.
포트먼트를 아규먼트로 지정해서 바인드, 리슨을 한 후 반복문에서 소켓에 대한 연결을 대기하고 있다. 그리고 데이터를 2048바이트만큼 계속 수신한 후 데이터사이즈를 더해서 출력하는 코드이다.
오른쪽은 데이터를 인코드한 후 보낸다. 데이터는 * 300 * 1024*1024 만큼 반복해서 보내서 거의 3기가바이트의 양이 될 것이다. 이 데이터를 이제 전송을 할 때 socket.send() 함수를 호출해서 보낸다. 이때 대량의 데이터를 보낼때는 send() 가 오류가 날 수 있다. 이것은 블로킹, 논블로킹 둘 다 오류가 날 수 있다. 운영체제에 따라 다를 수도 있다. 실험 결과 맥에서는 숫자가 작게 오류가 나고 윈도우에서는 크게 오류가 난다.
작은데이터는 문제없이 전송이 되지만 대용량 데이터는 오류가 난다.
setblocking(0)== (논블로킹) , setblocking(1)==(블로킹)
블로킹모드는 굉장히 오랜시간 대기를 하면 완료까지 시간이 많이 걸린다. 다만 오류 리턴이 금방될 수 도 있어서 실험 환경에 따라 달라질 수 있다.
이런 경우 운영체제에 소켓 버퍼사이즈가 있어서(디폴트 크기 다름) 굉장히 큰 용량 한번에 전송하면 오류가 날 수 있고, 오류가 났을 때
send () 함수를 sendall() 로 바꾸거나 반복문을 써서 해결할 수 있다.
아래 코드는 정상 작동하는 코드이다. 동일하게 굉장히 큰 용량의 데이터를 전송하고 있다. 아래는 3기가 바이트를 두번에 나누어 실행했다.
이런 블러킹 모드에서 굉장히 큰 데이터를 보낼땐 io 가 길어지고 그럴땐 논블로킹 모드를 사용할 수 있다.
위 예시를 통해 소켓 커널 버퍼사이즈에 따른 오류도 살펴봤다.
다음은 동기 비동기에 관한 문제이다.
특정 작업이 끝나면 다음을 처리하는 순차처리방식이 동기이다. 끝나는 걸 기다렸다가 순서대로 처리하는 것이다.
블뢰킹 논블로킹이랑 헷갈릴 수 있는데 동기화, 비동기화라고 하면 두개의 작업이 클럭이 맞춰진다. 즉, 끝나는 걸 정확히 기다렸다가 다음작업을 처리한다고 보면된다. 비동기는 파이프라이닝 방식이다. 여러 작업이 실행되어야하는 상황이다. 그 작업을 순차적으로 처리하지 않고, 끝나지 않은 상태에서 작업 세개를 동시에 실행하도록하는게 비동기 방식이다. 이것 역시 cpu 실행속도가 다르기 때문에 이 시간에 대한 버퍼링을 하기 위해 이런 방식이 도입된 것이다.
아래 예시
'* 예시 *
process_sync() 함수에서 find_users_sync() 함수를 3개 만ㄷ르어서 실행
함수를 호출해서 순서대로 1번째 2번재 3번째 이렇게 순서대로 for 루프로 실행함.
비동기 관점에서 프로그램한 예제를 살펴보자. 비동기 프로그래밍을 파이썬에할 때는 신택스를 공부해야한다.
asyncio 에 대한 함수가 있기 때문이다.
이걸 살펴보는 이유는 여러 개를 동시에 사용할 때는 스레드 사용이 전통적인 방법이었다. 스레드 사용시 일반적으로 프로세스보단 가볍고 프로그램하는 게 조금 더 간단할 수 있지만 스레드 역시 스레드 safe 프로그래밍이 어렵다는 단점이 있다. 여러 관련된 문제를 해결할 필요가 있다. 그리고 성능이 코어 개수에 민감하다. 이런 스레드를 활용하는 동시 프로그래밍을 대체하는 방식으로 비동기 프로그래밍 방식이 많이 개발되고 있다. 자바스크립트 기반, nginx 와 같은 하나의 쓰레드로 동시 처리하는 비동기 프로그래밍이 그 예시이다.
다음은 웹 서치 예제이다.
다음은 구글로 6개의 키워드를 검색해서 시간을 계산하는 예제이다. 루프를 돌면서 6번을 실행을 하는 것이므로 순서대로 4.8초 정도 나왔다. 이건 순차적으로 된 것이다. 동기화 되어있는 작업6개가 실행된 결과이다.
(*코드*)
이것을 비동기로 실행할 때는 asyncio 모듈을 사용한다. 코루틴을 정의를 하고 이벤트 루프를 만들고, 그 결과를 기다려야한다. 그리고 종료하면 닫기, 그다음에 객체가 다 끝난 결과를 대기하는게 await 키워드이다. 그래서 웹페이지를 동기화된 작업을 실행할 때는 순차적으로 된다. 그걸 비동기화하게되면 어떻게 되는지 살펴보자.
아래 코드를 보자
(*코드*)
아까 본것처럼 구글 사이트에서 6개 검색을 해서 시간을 측정하는데 대략 0.9초로 굉장히 성능이 향상된 것을 확인할 수 있다.
따라서 io 가 굉장히 길때(하드디스크 접근, db접근, io 처리)는 순차적으로 처리하는 게 아니라 비동기화 해서 처리하면 속도가 향상 될 수 있다.
네트워크에서 존재하는 여러 에이전트(클라이언트-서버_) 간 통신 위한 프로그램이 바로 소켓이다. 소켓프로그래밍을 공부한다는 것은 C/C++ 언어로 되어있는 소켓구조체, 관련된 api 의 함수들(socket(), bind(), listen() ) 등을 학습해서 소켓을 만들고 클라이언트와 서버의 통신 프로그래밍을 만드는 것이다.
• 전통적으로 윈도우, 리눅스, 유닉스에서 우리가 쓰는 거의 모든 응용 SW 가 소켓 기반으로 만들어진다.
• 기본적으로 운영체제에서 Network-layer에서 api 를 제공해주기 때문에 응용단에서 프로그래밍을 만들 수 있다.
• 전통적으로는 C/C++를 통해 서버 또는 통신 전용 프로그램를 작성했다.
• 최근에는 응용(웹/앱/서버) 개발시 프레임워크 사용한다.(관련 라이브러리가 잘되어있음)
소켓프로그래밍은 아주 기초적인 함수를 이용하는 방식에서 여러 직군별로 프레임웤이 많이 생겼다. 이걸 이용하면 쉽게 소켓 프로그램을 응용해서 만들 수 있다.
소켓 프로그래밍 발전 트렌드
백엔드, 프론트엔드, 모바일을 보면 전통적으로는 유닉스 네트워크 프로그래밍으로 개발했지만 지금은 자바 스프링, 스크립트, 고 등의 프레임워크로 만든다. 웹쪽은 이전은 윈도우, C++, C#으로 개발했지만 웹기반에서는 자바, 자바스크립트로 개발하게 된다. 웹에서는 자바스크립트가 디폴트가 되어있다(현재) 최근에는 파이썬 계열의 장고, 플라스크, FastAPI 이용하는 것도 많이 사용되고 있다.
서버와 클라이언트가 자바스크립트를 공통으로 이용할 수 있기 때문에 풀스택으로선 최근에 자바스크립트 계열의 Node.js 같은게 주목받고 있다. 이런 건 비동기, 싱글스레드로 구현되어있다.
- 백엔드/서버 • Unix network programming: C/C++
• Application: Java/Spring Framework (JavaScript, Go) + DB
-프론트엔트/클라이언트
• Windows network programming: C++/C#
Web: JavaScript, Java
- Web framework: 백엔드, 풀스택
• Java Spring, Python
Flask/FastAPI/Django
• Node.js: async, single thread
• 프론트엔드/모바일
Java.net.Socket class : https://do cs.oracle.com/javase/7/docs/api/ java/net/Socket.html
1. 네트워크 응용 SW 개발 : 가장 raw 한 소켓을 api 를 호출할 때 뿐만 아니라 프레임워크단에서 개발한다 하더라도 서버, 응용을 할 때 웹 서버, 채팅, 게임 등을 개발하려하면 소켓에 대한 개념이 있어야 쉽게 개발할 수 있다.
2. 분산시스템, 운영체제, 네트워크 작동 방식 이해
3. 고급 통신 프로그램 개발 : 대규모, 고성능 서버를 개발하려하면 여전히 소켓프로그래밍을 이용해서 직접 개발하고 있다
• 게임 서버 (C/ C++ 로 전통적인 방식처럼 개발하고 있다)
4. 대규모 시스템 이해 : 대규모, 고성능 서버를 개발하려하면 여전히 소켓프로그래밍을 이용해서 직접 개발하고 있다
• AI, 클라우드
소켓 프로그래밍 개요
아래는 소켓프로그램의 가장 기본적 구조이다. 소켓이란 응용 프로세스(앱)와 전송 계층 사이의 API 를 지칭하는 것이다.
그림에 보이는 것처럼 우리가 사용하는 웹 브라우저가 프로세스로 떠서 사용자계층에 실행되고 있다. 그 아래부터는 운영체제로 커널에서 제공하는 기능이다. 커널에서 네트워크 사이에 서버-클라이언트 통신 기능을 구현하는데 그 api 가 소켓이다.
구체적이로 이해하자면 소켓은 파일이다.
우리가 파일을 읽고, 쓰고, 종료하는 것처럼 소켓에 데이터를 읽고, 쓰고, 종료하는 것이다.
소켓은 쉽게 생각하면 네트웤을 연결하는 파일이다.
socket(2) — Linux manual page
소켓은 endpoint 를 만드는 것인데 이 endpoint 는 communication 통신을 위한 endpoint 이다. 소켓은 file descripter 를 리턴한다. 통신의 끝단(포트번호를 이용하여 데이터를 읽고 씀) 이것이 파일과 동일하다. 파일 디스크립터로 파일을 읽고 쓰면 네트웤을 통해 데이터를 통신할 수있다. 소켓을 커널에서 제공하기 때문에 소켓함수로 쉽게 통신할 수 있다.
소켓은 전송계층의 대표 TCP, UDP 와 일대일로 매핑이 된다. tcp 이용한 소켓프로그래밍, udp 이용한 소켓프로그래밍 이렇게 나눌 수 있다. tcp 는 신뢰성있는 기능, 연결, 바이트단위 전송, udp 는 신뢰성 x , 연결x , 멀티플렉싱만 제공 /
이 두가지를 커널에서 제공하고 있다.
• 소켓 종류: UDP, TCP
UDP 통신
• 비신뢰성: 패킷 손실, 순서 바뀜 가능 • 연결 없음 • IP 주소와 port 번호 이용하여 송수신
AF_INET: 인터넷, SOCK_DGRAM : UDP / 소켓을 만들면 서버 소켓을 이용해서 패킷을 기다리게됨(연결 x) 받고 다시 소켓 전송
udp 서버 : 몇번 포트가 bind 되는지 api로 호출, recvfrom() 으로 데이터 수신
udp 클라이언트 : 서버 포트번호 명시하여 데이터 전송, sendto로 메시지와 서버네임, 서버포트 알아내서 데이터전송
UDP 예시
TCP연결
•TCP 통신 •신뢰성:패킷 손실 복구,순서 맞춤 제공
•연결 설정 필요 •바이트 단위의 스트림 전송
accept() listen() 과 같은 api : 연결을 만들고 데이터를 전송한다.SOCK_STREAM : TCP 연결임을 의미 / connect() : 서버와 연결 후 전송 / send() recv()
tcp 서버 : 소켓을 만들고 ( SOCK_STREAM: tcp) bind() 연결 만들고 listen() 상태로 들어가고 accept() 로 실제 연결이 완료되는 걸 기다리고 , 연결이 만들어지면 recv() 로 데이터 수신 대기
TCP 예시
TCP 서버 주요함수
소켓만들기
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) • IP주소와 포트번호로 서버 소켓 묶기: 바인드
s.bind((IP_ADDR, TCP_PORT))
대기하기(연결요청 넣을 큐의 크기)
• s.listen(1) 연결 수락하기
• conn, addr = s.accept()
데이터 수신 • data = conn.recv(BUFFER_SIZE)
TCP 클라이언트 주요 함수
소켓 만들기
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
연결
s.connect((IP, TCP_PORT))
데이터전송
s.send(MESSAGE.encode('utf-8'))
여러 개의 클라이언트를 처리하는 서버 IO 구조
1. blocking vs. non-blocking
Blocking : 프로세스가 시스템을 호출 하고나서 결과가 반환되기까지 다음 처리로 넘어가지 않음
Non-blocking : 시스템을 호출한 직후에 프로그램으로 제어가 다시 돌아와서 시스템 호출의 종료를 기다리지 않고 다음 처리로 넘어갈 수 있음(IO 시간이 길다면)
[소켓이 블로킹, 논블로킹 둘 중 어느것으로 작동될지 결정해야함 IO 시간이 길게되면 논블로킹이 효율적이다. ]
2. synchronous vs. asynchronous
Synchronous : 작업을 요청한 후 작업의 결과가 나올 때까지 기다린 후 처리
Asynchronous : 직전 시스템 호출의 종료가 발생하면 그에 따른 처리를 진행 (Android App)
=> 서버클라이언트를 만들때 이런 작동방식을 혼합해서 사용할 수 있다.
여러 개의 클라이언트를 연결하는 소켓 프로그래밍
• multiprocess
: 클라이언트 동시 처리 위해 프로세스를 여러개 시행하는 것이다. 프로세스는 운영체제에서 굉장히 큰 자원을 사용하기 때문에 조금더 작은 단위 실행단위인 스레드를 이용할 수 있다 -> 멀티스레드 ex) fork, multi-process 관리, IPC 통신
• multithread
: 메모리가 조금 사용되더라도 스레드를 이용해서 리소스를 적게 사용할 수 있는 방식. ex) Single process, 메모리 사용 증가, 쓰레드 관리
• Select
: Select 로 소켓을 여러개를 만들고 소켓의 송수신 이벤트 발생했는지 모니터링을 계속해서 확인(싱글스레드로 처리 가능) 모니터링는 계속해야해서 대기하는 것에 따른 cpu 리소스가 증가하는 단점, 대규모로 여러 클라이언트 처리하는 기능 존재 • 여러 Socket I/O 모니터링, single thread, event 처리, 대규모 동시접속/ 서버, CPU 증가, 대기
우리는 프로그래밍할 때 프레임워크를 사용할 가능성이 높다. 웹소켓하고 우리가 방금 배운 소켓은 다르다. 소켓이긴 하지만 이름이 보여주는 것처럼 웹소켓은 웹에서의 소켓이고 그 중에 특별히 구현된 예제가 Socket.io 이다. 우리가 배운건 tcp/udp 단에서 만든 소켓이고 우리가 소켓을 짜게 되면 응용계층에서 소켓을 만드는 것이다. 이게 조금 다르기 때문에 이름이 비슷해도 구분해서 이해해야한다.
만약 웹 소켓이라고 하면 HTTP 위에 소켓이 구현되어있다고 보면 된다.
ex) 배달의민족에서 실시간 통신 할 때는 그냥 소켓 x 웹소켓 o ( RFC6455) / 왜냐면 실시간 양방향 통신이 HTTP 로는 어렵다. -> 별도의 라이브러리(웹 기반) 필요
L4(tcp/udp) vs. L7(응용계층)
Socket programming • TCP, UDP, IP api를 이용한 응용 개발
WebSocket: RFC6455 • HTTP는 서버와 클라이언트 사이의 연결 X • HTTP는 실시간 양방향 통신 구현에 어려움: HTTP polling, Stream / HTML5 표준, HTTP 위에서
Socket.io
• 추상화: WebSocket, FlashSocket, AJAX Long Polling, Ajax Multi Part Streaming, Iframe, JSONP Polling • Node.js 모듈
TCP 는 연결을 만든다. 클라이언트 서버, 서버 클라이언트 사이에 연결을 만들고 데이터를 전송하게 된다
연결을 만드는 과정이 3-way handshake 이다.
3번의 메시지 교환으로 완성이 되기 때문에 이와 같은 이름이 붙었다.
tcp는 흐름제어기능을 제공하고 있다.
센더가 수신자의 버퍼를 버퍼오버플로 하지 않도록 전송량을 조절하는 방법이다.
tcp 연결은 1대1 연결이다. 그래서 여러 수신자가 동시에 받고자 한다 하더래도 연결은 개별적으로 만들어야한다.
tcp 에서 가장 중요한 기능 중 하나는 신뢰성 기능이다.
신뢰성 기능은
1. 오류가 없어야한다.(오류 복구기능)
2. 순서에 맞게 전송해야한다.
연결을 통해 전송하는 것은 바이트가 연속적으로 전송이 된다.
그리고 흐름제어를 통해 tcp 연결이 전송하는 이 메시지는 동시에 이 여러개가 전송이 될 수 있다.
파이프라이닝 기능을 통해 tcp 센더가 수신자로부터 ACK 을 받지 않고 동시에 여러개를 보낼 수 있다.
지난 시간에 go-back-N , Select 예제에서 파이프라이닝을 통한 성능 향상 예제를 살펴본 적있다. 그 기능이 tcp 에 있다고 생각하면 된다.
그리고 tcp 프로토콜은 송신자와 수신자의 버퍼를 가지고 있다. 지난번에 go-back-N 과 Selective 방식을 봤을 때 센더와 리시버의 버퍼 크기가 어떻게 변하는 지 살펴봤었다. tcp 는 센더와 리시버의 버퍼를 동시에 가지고 있다.
TCP 세그먼트 구조
TCP source 와 destination 포트 번호가 각각 1번쨰 2번째 16비트씩 찍혀있다. 그후 순서 번호와 상대편이 전송하는 ACK 번호가 각각 4바이트(32비트)로 있다.
ACK 을 보낼 때는 위 사진을 보면 플래그가 몇개 있는데 그 중 A라고 써있는 비트가 ACK 에 해당한다. 체크섬 기능은 오류 제어를 할 때 비트 오류가 있는지 확인할 때 사용한다(16비트)
그 아래 옵션은 여러가지가 가능하다. 디폴트로는 없이 전송할 수 있지만 옵션이 몇개 추가될 수 있다. 옵션을 뺀 나머지가 전체적으로 보면 20바이트가 되고, 옵션 그리고 어플리케이션 데이터가 전송이 된다.
또 헤더의 길이가 header length 라고 표시되어 있다.
그리고 아까 살펴본 것처럼 tcp 세그먼트 종류가 몇가지가 있는데 여기서 처럼 SYN 은 연결을 설정하는 메시지이고 연결을 종료하는 메시지는 FIN 이다. RST 는 오류가 있을때 재설정하는 필드이다. ( 위에 사진 참고)
윈도우는 버퍼에서 얼마나 남아있나를 표시한다. 16비트짜리 receive window 를 통해 흐름 제어 기능이 수행되고 있다.
그리고 urgent data field 라해서 긴급한 재설정을 할때는 긴급 메시지가 몇번 부터 있다고 알리는 포인트가 있다.
전송 과정
호스트 A 에서 B 에게 데이터 'C' 하나를 전송한다고 할 떄 첫 전송을 보면 순서번호가 적혀있고 ACK 번호가 있다. 여기서 말하는 ACK 은 상대편이 보낸 데이터에 대한 ACK 이다.
첫 전송에서 순서 번호가 42 이고 ACK 이 79이다. 이것은 "내가 보내는 건 42번이고 너가 보낸 거 잘받았으니 다음은 43번으로 보내!" 라는 의미이다. 그 후 두번째 화살표를 보면 순서번호 79이고 ACK 43이다. 이것은 "내가 보내는 건 79이고잘받았으니 다음은 43번으로 보내!"라는 의미이다.
와이어 샤크로 TCP 데이터를 살펴보면 순서넘버가 1번 이렇게 찍혀있는데 이건 상대적인 것이고 실제 번호는 691694465이다.
또 길이가 439라고 찍혀있는데 저 위에 이더넷 프레임쪽을 보면 493바이트라고 나와있다. 이 차이는 왜 생기냐면 헤더의 값을 빼면 실제 tcp 세그먼트의 크기가 나오게 되는 것이다. 예를들어 이더넷헤더가 14바이트 ip헤더가 20바이트 tcp 헤더가 20바이트이므로 총 54바이트를 뺀 것이 439이다. (헤더가 제외된 len)
와이어샤크는 사용자가 보기 편하게 가공해서 보여준다.
아까 데이터를 전송하고 나면 응답이 오는데 ACK 이 적혀있는 것을 볼 수 있다 . 아까 보낼때 439였으므로 440이 ACK 넘버로 적혀있는 걸 확인할 수 있다.
와이어샤크는 위처럼 시각화 기능도 제공한다.
tcp 는 오류제어를 통해 오류가 있으면 복구해준다. 이를 위해선 재전송이 필요하다. 재전송을 하려면 내가 얼마만큼 기다려야할지 기다려야하고 그 값을 이용하여 타이머값을 설정해야한다. TCP 는 왕복시간을 항상 기록하고 있고 그값을 이용해서 타이머값을 세팅한다.
에러제어에서는 재전송이 사용되고 있고 재전송에는 타이머가 필수라 항상 측정이 필요하다.
timeout 값을 예를 들어 RTT 보다 긴값을 설정하면 굉장히 긴 값을 기다려야한다. 그 값만큼 아무 전송도 하지 않으면 성능이 떨어진다.
그렇다고 너무 짧게 잡으면 한창 전송될때 타이머 값이 종료되므로 재전송이 이루어질 수 있어서 불필요한 재전송이 발생할 수 있다. 너무 길지 않고 짧지 않은 타이머값 설정하는 게 굉장히 중요하다. 그 값을 어떻게 설정하려면 지속적으로 RTT 를 측정을 하고 적절한 값으로 정해야한다.
아래 값은 RTT 평균값을 예측하는 것이다.
알파가 0이면 샘플값이 계속 상수로 이용된다. 알파가 1이면 샘플값(측정할 때 마다 변함)을 계속 반영하게 된다. 알파를 1/2로 두면 왼쪽 오른쪽을 반반 반영한다. 보통은 알파를 1/8로 잡는다.
평균 알파가 1/8이므로 위에 식에 따르면 샘플값을 1/8만큼 이전 RTT 값을 7/8 정도의 웨이트를 두게 된다.
RTT 값을 계산하고 난 후는 타이머값을 설정하는 것이다 . RTT 가 예를들어 1초걸렸다고 해서 타이머값을 1초를 설정해서는 안된다. RTT 보다 조금은 더 긴값으로 세팅을 한다. 예를 들면 2초 / 그 값을 시행착오로 겪어보니 아래와 같은 공식이 나왔다.
DevRTT 는 위에서 Moving RTT 식처럼 계산한다. 이전 DevRTT 값과 샘플값과 RTT 값을 사용한다. 베타는 1/4로 두고 DevRTT 값에 3/4 의 웨이트를 두고 Sample RTT, 추정 RTT 의 차이를 1/4 배 해서 더한 것이 RTT 편차 값이 된다.
마지막 식이 타이머 세팅하는 식이다. 위에서 측정한 RTT 값에 RTT 편차*4를 더한 값으로 타이머를 설정하면 좋다. 너무 많이 튀면(편차가 크면) 타이머를 더 크게 잡고 편차가 굉장히 작으면 작은 값으로 타이머를 세팅해야한다.
TCP 는 기본적으로 신뢰성 있는 데이터 전송을 보장한다. TCP 밑에 IP 계층이 있다. IP 계층은 신뢰성이 없다. 그래서 IP 에서는 패킷을 라우터가 보내도 손실이 일어나면 재전송하는 기능이 없다. Tcp 는 우리가 쓰는 스마트폰, 서버, 기기 등 커널에 있어서 손실되면 재전송하기 때문에 신뢰성있는 전송을 보장할 수 있다.
TCP 는 타임아웃 이벤트나 ack 중복 이 두가지 경우에 재전송을 진행한다. 재전송을 하려면 항상 타이머값을 설정해야한다.
TCP 는 차례차례로 데이터를 받아서 세그먼트 단위로 만들고 순서번호를 붙인다. 예를들어 1기가 바이트 파일에서 1000바이트로 짤라서 세그먼트로 보낸다 하면 0~999, 1000~1999 이런식으로 번호를 쪼개서 보낸다 .재전송을 하려면 타이머값을 설정해야한다.
그리고 전송 시 Ack 이 와야하는데 ack 이 안오면 타임아웃 이벤트를 통해 재전송을 하게 된다. ack 이 정상적으로 도착하면 다음으로 넘어가게된다(업데이트) 아직 전송되지 않은 ack 이 오면 타이머값을 유지할 것이다
TCP를 재전송하는 시나리오들이다.
첫번째는 ack 을 손실한 시나리오이다. 이 과정은 데이터 손실과 동일하게 타이머값을 통해 손실을 탐지하고 재전송을 하게 된다.
오른쪽은 타이머값이 너무 작게 설정되어 있는 경우이다. 이 경우는 타이머값에 의해 손실되었다 판단하고 재전송을 하게 되는데 불필요한 전송이 일어난 것이다.
아래 시나리오는 b가 두개의 ack 을 보냈는데 나중의 것이 도착하더라도 첫번째게 손실된 상황에도 불구하고 아무 문제가 생기지 않는다. 이것은 두번째 ack 이 이전에 보낸 ack 을 포함하여 누적된 ack (120번이라는 ACK)이기 때문에 정상적으로 진행되는 것이다.
다음은 수신자에게 발생할 수 있는 이벤트 4개와 그에 따른 TCP 수신자의 액션을 표로 정리한 것이다.
첫번째는 예측대로 순서대로 전송되는 상황이다. 근데 ACK 을 이미 보냈다
첫번째는 예측대로 순서대로 세그먼트가 도착되는 상황이다. 근데 ACK 을 한 번 이미 보냈다는 조건이 있다. TCP 가 이런경우에는 수신자가 ACK 을 보내야하는데 delayed ACK 이라해서 보내는 것을 일단 기다려야한다. ACK 이 너무 많이 발생하는 것을 방지하고자 함이다. 한 개가 정상적으로 오더라도 최대 500ms 기다렸다가 보내야한다.
두번째는 순서대로 도착한 세그먼트에 대해 ACK 을 보내야하는 상황이다. 이때는 즉시 하나의 cumulative 한 ACK 을 보낸다.
세번째는 순서에 맞지 않게 도착한 세그먼트가 도착했을 때이다. 그러면 중간에 비어있는 갭을 탐지하게 된다. 그러면 앸을 알려줘야하는데 앸은 중간에 비어있는걸 알려줘야한다. 앸을 이전에 했던 1, 2 까지 잘 했는데 4번이 도착했다면 2번에 대한 앸을 다시 보내게 된다. 그건 바로 송신자 입장에선 중복된 앸이 된다. 나중에 재전송을 통해 앸이 정상적으로 보낼수 있는 상황이 오면 정상적인 앸을 보내게 된다.
아까전 재전송할 떄 손실을 탐지하려면 tcp 는
타임아웃과 중복된 ack 두가지 경우가 가능하다 했다.
중복 ack 을 통해 전송하는 기능이 Fast Retransmit 기능이다.
타이머를 이용하게 되면 재전송 시간이 굉장히 오래걸리게 된다. 기본적으로 RTT 보다 타이머값이 길기 때문에 결국은 성능이 떨어지게 된다. 이 타임아웃 시간이 길게 나타나는 것을 방지 하기 위해 중복된 ack 이 발생될때는 어떤 세그먼트가 손실됐음을 알 수 있다.
중복 ack 을 이용하면 타임아웃을 기다리지 않고 바로 재전송을 할 수 있다.
송신자가 3개 ack 을 동일하게 수신하면, 해당 세그먼트가 손실되었다고 판단한다.
아래 사진을 보면 ACK 100이 중복하여 4개가 발생했다. 이것은 송신자 A 입장에선 중복된 ACK 이 3개가 발생했으므로( 첫번째는 정상, 두번째부터 4번째까지는 3번 발생한 것이므로 ) 100번 시퀀스 넘버가 손실되었다고 판단할 수 있다.
그리고 이때 재전송을 할 수 있다.
재전송을 하면 타이머가 만료되는 것을 기다리지 않고도 재전송을 할 수 있다.
TCP 는 흐름제어 기능이 있다. 흐름 제어는 TCP 수신자의 버퍼 상태를 관찰하고 있다가 수신자의 버퍼가 오버플로가 발생하지 않도록 전송량을 제어하는 것이다. 수신자의 버퍼가 아래처럼 주어져 있을 때 송신자가 데이터를 전송하려고 수신을 잘 받아서 웹 브라우저가 열심히 읽고 있는 상황이다. 파란색은 비어있는 버퍼크기 이다. (Recieve Window) 그렇다면 이만큼 추가적으로 받을 수 있지 이것보다 많이 도착하게 되면 버퍼 오버플로가 발생할 수 있다. 그래서 송신자는 항상 수신자의 버퍼 크기를 계속적으로 관찰해야하고 그만큼만 최대로 전송해야한다. 그거보다 적게 보내면 성능 저하 많이 보내면 오버플로가 발생할 수 있다.
아까 우리가 헤더를 봤었는데 TCP 구조를 보면 receive window 로 수신자의 버퍼 크기를 알 수 있다.
receive window = (가장 마지막 바이트) - (읽은 바이트)
송신자는 이 receive window 보다 값을 작게 하여 보내야한다.
tcp 는 udp 와 다르게 연결을 하고 나서 데이터를 전송한다.
연결할 때 어떤 일을 하는 지 알아보자
우선 순서번호에 대한 약속이 필요하다.
같은 번호를 항상 쓰진 않고 랜덤하게 쓴다.
초기에 소켓을 만들어 tcp 연결을 만들어야하므로 흐름제어, 버퍼크기에 대한 기능을 수행해야한다.
파이썬, c , 자바 언어에서 보면 소켓을 초기화할때 보면 소켓이 바로 연결을 만들게 된다. 클라이언트에서 서버 상태로 데이터를 보내서 연결을 만들게 되는 단계가 3-way handshake 이다.
처음에 SYN 이라는 세그먼트가 전송되게 되고 여기서는 초기 시퀀스 번호가 있고 데이터는 없다. 두번째 서버에서 클라이언트로 전송할 때는 ACK 이 있다. SYN 에 대한 응답 ACK 이다. 그다음 클라이언트가 받은 SYNACK 을 통해 연결할 수 있다.
연결을 만들게 되면 종료도 실시해야한다. 마지막 타임드 웨이트가 있는데 종료하고 나서도 조금 기다려야하는 시간이다..
다음은 연결관리 상태도이다.
왼쪽그림이 클라이언트에서 SYN 보내고 SYNACK 도착하고 ACK 보내면 연결이 설정된 상태이고 데이터를 열심히 보내고 마지막에 FIN_WAIT 1,2 , TIME_WAIT 로 종료하게 된다.
서버는 listen() 상태에서 대기하다가 SYN 받고 SYNACK 보내고 SYN received 상태가되고 ACK 을 받으면 연결이 설정된다. 여기서 데이터를 받고 CLOSE_WAIT 를 거쳐서 종료하게 된다. CLOSE_WAIT 는 FIN 을 받고 송신하는 상태이다. CLOSE_WAIT 에서 종료를 할 경우에는 서버가 종료하고 나면 FIN 을 보내게 된다. 그래서 LAST_ACK 상태에 들어가는데 이 상태는 서버에서는 과도기 상태이다. 운용이 종료되는 시간을 주어주는 만약 이 상태에서 오류가 있으면 계속 펜딩된 프로세스가 발생할 수 있다.