- Principles of network
- Web & HTTP
- E-mail, SMTP, IMAP
- DNS
- P2P applications
- Video streaming and CDNs
- Socket programming with UDP & TCP
앞서 p2p가 무엇인지 배웠었다. 이제 file distribution 측면에서 client-server 구조와 p2p 구조를 비교해보자.
문제는 위 그림처럼 충분한 대역폭을 가지는 네트워크를 둔 상황에서, peer의 업로드, 다운로드 용량이 제한된 자원으로 존재하는 상황이다.
=> 최종 시간 : Dc-s max{NF/s,F/dmin}
즉, 업로드 속도와 다운로드 속도를 비교해서 결정되고, peer의 수가 늘어날수록 업로드에 많은 시간이 소요되어 총 걸리는 시간이 늘어나게 된다.
=> 최종 시간 : DP2P max{F/us, F/dmin, NF/(us + ui)}
즉, peer의 수가 늘어남에 따라 업로드 시간이 늘어나긴 하지만, 각 peer가 서비스에 필요한 capacity를 충당해주며 전체 소요시간이 상대적으로 느리게 증가하게 될 것을 예측할 수 있다.
Tit-for-tat은 좋은 파트너 peer를 남기고 selfish peer를 버리기 위한 전략이다. (같은 peer만 계속 이용할 경우, 모든 파일을 얻어 떠날 수 있기 때문)
그래서 index도 분할시켜 관리한다면 tracker가 필요하지 않을 수 있기에, DHT라는 방식이 등장했다. DHT에선 각 peer가 자기가 소유하고있는 pointer들에 따른 table을 관리하고 있다.
비디오 스트리밍 트래픽은 인터넷 대역폭의 큰 부분을 차지한다. 넷플릭스, 유튜브, 아마존 프라임은 2020년 기준 전체 residential ISP traffic의 80%를 차지하였으니, 그 규모를 실감할 수 있다.
그렇다면, 이렇게 많은 트래픽이 발생하는데 1억명에 가까운 사용자들을 수용할 수 있는걸까? Single mega-video server만으로는 이를 다 수용할 수 없다. 뿐만 아니라 사용자마다 다른 조건(e.g. wired vs mobile, bandwith rich vs bandwith poor)을 가지고 있기에 heterogeneity를 해결하는 것도 중요하다. 이때 제시된 해결책이 바로 distributed, application-level infrastructure이다.
비디오는 여러 이미지들의 연속이 일정한 속도로 보여지는 것이고, 각 디지털 이미지는 픽셀들의 배열로 각 픽셀은 비트들로 표현된다. 이때, 이미지 인코딩에 사용되는 비트를 줄이기 위해 이미지 내부 및 이미지 간 중복되는 부분을 활용하는 coding이 존재한다.
비디오 스트리미잉은 서버 상에 저장되어있는 영상을 인터넷을 통해 전달해 클라이언트에서 보는 방식인데, 서버와 클라이언트 사이의 대역폭이 시간에 따라 달라지기 때문에 어려움이 생긴다.(네트워크 conjestion level이 바뀜. house/access network/network core/video server) 이런 conjestion에 의해 발생하는 packet loss나 delay는 영상 재생에 delay를 주어 낮은 퀄리티의 영상으로 남게 되는 것이다.
이상적인 방식이라면 위 그림에서 보이듯이 비디오가 서버에 저장되는 속도, 네트워크를 통해 보내지는 속도, 클라이언트에서 재생되는 속도가 일정하다는 것을 확인할 수 있다. 그래서 network delay가 일정하게 나타나기에, 클라이언트 측에서 스트리밍할 때 아무런 문제가 발생하지 않게 된다.
하지만 실제로 영상을 연속적으로 보내는 것은 어려움이 있다. Client가 영상을 재생한 순간부터, 이후 재생되는 영상들이 기존 영상과 같은 속도를 유지해야만 끊끼지 않고 볼 수 있다. (흔히 우리가 보는 로딩중 화면 같은 것은 네트워크 딜레이로 인해 클라이언트 측 버퍼에서 데이터가 올때까지 기다리는 상태이다.) 이 문제 외에도 client interactivity를 위해 pause, fast-forward, rewind, jump 등의 동작이 가능해야 하고, video packet이 손실될 경우 retransmit도 필요하게 된다는 점 등이 어려움으로 존재한다.
실제 비디오를 스트리밍할 때는 playout buffering이 발생한다. 서버에서 영상을 저장하는 속도와 클라이언트에서 영상을 재생하는 속도는 일정해야 한다. 이때, 영상을 전송하는 네트워크의 딜레이가 일정하지 않고 변수처럼 동작하게 된다. 네트워크가 불안정할수록 클라이언트 측이 가진 버퍼의 양이 많아야 원래 의도대로 영상을 재생할 수 있게 되는 것이다. 또한, 서버와 클라이언트 사이의 물리적 거리가 짧아지면 네트워크 딜레이를 줄일 수 있기에, 여러 대의 서버를 활용하는 CDN 기술이 중요해지는 것이다.
첫 번째 선택지 - 하나의 큰 mega-server를 사용하자
두 번째 선택지 - 비디오 복사본들을 여러 개의 기하학적으로 분산된 위치에 저장하고 전송하자 (CDN)
CDN은 컨텐츠 복사본들을 CDN node에 저장하고 있고, subscriber가 CDN으로부터 컨텐츠를 요청하면 가까운 node로부터 컨텐츠를 꺼내온다. 이때, 네트워크 경로가 congest되어 있으면 다른 복사본을 고를 수도 있다.
Socket programming은 client/server application에서 socket을 이용해서 communicate하는 방식이다. 여기서 Socket은 application process와 end-end-transport protocol 사이의 문과 같은 역할을 하며, user level에서 kernel의 network를 쓸 수 있게 해주는 interface이다.
Socket programming은 두 가지 방식의 transport service가 있으며, 각각의 서비스에 따른 socket type이 존재한다.
UDP : no "connection" between client and server
UDP는 데이터 전송 전에 handshaking 과정이 존재하지 않는다. 이로 인해 sender가 IP destination address & port를 각 packet에 직접적으로 명시해서 사용한다. 마찬가지로 receiver도 packet으로부터 sender의 IP address & port를 추출해서 알아낸다.
UDP : transmitted data may be lost or received out-of-order
데이터가 도착할지, 올바른 순서대로 도착할 지 보장할 수 없어, unreliable하게 되는 것이다.
1. client와 server에서 socket을 생성함.
2. client에서 server IP와 port를 포함해서 datagram을 만듬. clientSocket을 전송.
3. serverSocket에서 datagram을 읽음.
4. serverSocket에서 client address와 port를 포함하여 답변하느 datagram 만듬.
5. clientSocket에서 datagram을 읽음.
6. clientSocket을 닫음.
TCP의 경우 client가 server와 반드시 접촉해야 하기 때문에 server process가 항상 실행되어 있어야 하고, client가 접촉할 수 있는 socket을 만들어 둬야 한다.
Client는 TCP socket을 접촉하고자하는 server의 IP 주소와 port를 포함해서 만들고, client가 socket을 생성할 때 server TCP와 connection이 만들어진다.
Client에 의해 접촉될 때 server TCP는 해당 특정 client와 소통하기 위한 server process의 새로운 socket을 만들게 된다. 매번 새로운 소켓을 만들기에 서버는 여러 클라이언트를 둘 수 있고, 같은 서버는 같은 IP 주소를 가지기에 port 번호를 통해 구분한다.
TCP는 reliable하며 in-order를 보장하는 byte-stream transfer("pipe")가 가능하다.
1. 서버에서 추후 들어올 request를 읽기 위해 port번호를 명시하여 serverSocket을 만들어둔다.
2. 서버의 소켓은 connection request를 기다리고 있다.
3. 클라이언트가 host id와 port를 명시해 clientSocket을 생성하고, 이때 tcp connection이 이루어지며 connectionSocket을 통해 소통하게 된다.
4. clientSocket을 통해 request 전송
5. connectionSocket에서 request를 읽는다.
6. reply를 작성해 clientSocket으로 보내고, 여기서 읽는다.
7. connectionSocket과 clientSocket을 닫는다.
UDP와 달리 메세지를 전송할 때 서버의 이름과 포트를 명시할 필요 없음.
한 번 연결해두면 끝.
여기선 listen의 과정이 추가되었으며, accept를 통해 connection을 생성하고 connectionSocket을 닫을 뿐 serverSocket은 내버려둔다.