P2P 구조는 항상 켜져있는 인프라스트럭처 서버에 최소한으로 의존하고, 간헐적으로 연결되는 호스트 쌍들(피어, peer)이 서로 직접 통신한다.
- 클라이언트-서버 파일 분배에서 서버는 파일 복사본을 각 클라이언트에게 보내려면 서버에게 커다란 부하를 주고, 많은 양의 서버 대역폭을 소비한다.
- P2P 파일 분배에서 각 피어는 수신한 파일의 임의의 부분을 다른 피어들에게 재분배할 수 있어서 서버의 분배 프로세스를 도울 수 있다.
2020년에 가장 인기 있는 P2P 파일 분배 프로토콜은 비트 토렌트(BitTorrent)다.
서버와 피어들은 접속 링크로 인터넷에 연결되어 있다.
- 서버의 접속 링크 업로드 속도를 u(s)로, i번째 피어의 접속 링크 업로드 속도는 u(i)로,
그리고 i번째 피어의 접속 링크 다운로드 속도는 d(i)로 나타낸다. - 또한, 분배되는 파일의 크기는 F bit로, 파일을 얻고자 하는 피어들의 수는 N으로 나타낸다.
분배 시간은 모든 N개의 피어들이 파일의 복사본을 얻는데 걸리는 시간이다.
다음 분배 시간에 대한 분석에서 클라이언트-서버와 P2P 구조 모두의 경우, 인터넷 코어가 풍부한 대역폭을 갖고 있다는 간단한 가정을 하며,
이는 모든 병목 현상은 다른 네트워크 애플리케이션에 참여하지 않아서 이들의 모든 업로드와 다운로드 접속 대역폭은 이 파일 분배에 모두 사용된다고 가정한다.
- 서버는 파일 복사본을 N개의 피어 각각에게 전송해야 한다. 따라서 서버는 NF 비트를 전송해야 한다.
- 즉, 서버가 파일을 분배하는 시간은 적어도 NF/u(s)이다.
- d(min)이 가장 낮은 다운로드 속도를 가진 피어의 다운로드 속도를 나타낸다고 하자.
- 가장 낮은 속도를 가진 피어는 F/d(min)초보다 적은 시간에 파일의 모든 F 비트를 얻을 수 없다.
- 즉 최소 분배 시간은 F/d(min)이다.
즉, 분배 시간을 D(cs)라고 하면 다음과 같은 수식을 얻을 수 있다.
D(cs) ≧ max{ NF/u(s) , F/d(min)}
위 식에서 충분히 큰 N에 대해 클라이언트-서버 분배 시간은 NF/u(s)로 주어진다는 사실을 알 수 있다. 즉, N에 따라 선형 증가한다.
여기서는 각 피어들이 서버가 파일을 분배하는 데 도움을 줄 수 있다.
특히 한 피어가 파일 데이터 일부를 수신할 때, 피어는 그 데이터를 다른 피어들에게 재분배하는 데 자신의 업로드 용량을 이용할 수 있다.
- 분배가 시작되면 서버만이 파일을 갖고 있다.
- 이 파일이 피어 커뮤니티에 도달할 수 있도록 하기 위해, 서버는 적어도 한 번 접속 링크로 파일의 각 비트를 보내야 한다.
- 따라서 최소 분배 시간은 적어도 F/u(s)다.
(서버가 한 번 보낸 비트는 서버가 다시 보낼 필요가 없는데, 이는 피어들이 그들 사이에서 재분배할 수 있기 때문이다.)
- 클라이언트-서버 구조와 마찬가지로 다운로드 속도가 가장 낮은 피어는 F/d(min)초보다 적은 시간 안에 파일의 모든 F 비트를 얻을 수 없다.
- 따라서 최소 분배시간은 적어도 F/d(min)이다.
- 마지막으로, 시스템의 전체 업로드 용량은 전체적으로 서버의 업로드 속도와 각 피어들의 속도를 더한 것이다. 이를 u(total)이라 하자.
- 시스템은 각 피어들 각각에게 F 비트를 전달해야 한다. 이는 u(total)보다 빠르게 할 수 없다.
- 따라서 최소 분배 시간은 NF/u(total)이다.
즉, 분배시간을 D(p2p)라고 하면 다음과 같은 수식을 얻을 수 있다.
D(p2p) ≧ max {F/u(s) , F/d(min), NF/u(total)}
위 수식들에서 하한값은 서버-클라이언트 구조에서 서버가 전송을 스케줄링 하거나,
P2P 구조에서는 각 피어가 비트를 수신하자마자 그 비트를 재분배할 수 있다고 가정하면(실제로는 chunk가 재분배된다.),
식의 하한값을 최소 분배시간으로 채택할 수 있다.
위 그래프를 통해 임의의 피어 수 N에 대해 클라이언트-서버 구조보다 P2P 구조가 더 시간이 적다는 것을 볼 수 있다.
따라서 P2P 구조를 가진 애플리케이션은 자가 확장성을 갖는다.
비트토렌트(BitTorrent)는 파일 분배를 위한 인기 있는 P2P 프로토콜이다.
비트토렌트 용어로 특정 파일의 분배에 참여하는 모든 피어의 모임을 토렌트(torrent)라고 부른다.
토렌트에 참여하는 피어들은 서로에게서 같은 크기의 청크(chunk)를 다운로드한다. (일반적으로 256KB)
- 처음으로 가입하면 그 피어에는 청크가 없지만, 시간이 지나면 점점 많은 청크를 쌓을 수 있다.
(accumulate chunks over time from other peers) - 피어가 청크를 다운로드할 때 또한 청크를 다른 피어들에게 업로드한다.
- 일단 한 피어가 전체 파일을 얻으면 토렌트를 떠나거나, 토렌트에 남아서 다른 피어들로 청크를 계속해서 업로드할 수 있다.
각 토렌트는 트래커(traker)라고 부르는 인프라스트럭처 노드를 갖고있다.
한 피어가 토렌트에 가입할 때 트래커에 자신을 등록하고 주기적으로 자신이 아직 토렌트에 있음을 알려, 트래커는 토렌트에 있는 피어들을 추적할 수 있다.
위의 그림을 예시로 보자.
새로운 피어 앨리스가 토렌트에 가입하면
트래커는 참여하고 있는 피어 집합에서 임의로 피어들의 부분집합(정확히는 50)을 선택하여 이 50개 피어들의 IP 주소를 앨리스에게 보낸다.
이 피어들의 목록을 얻고 나서, 앨리스는 이 목록에 있는 모든 피어와 동시에 TCP 연결을 맺고,
성공적으로 맺은 피어를 이웃 피어(neighbors)라고 부른다.
The peers fluctuate over time
churn : peers may come and go
시간이 지남에 따라 피어들 중 일부는 떠나고, 다른 피어들이 앨리스와 TCP 연결을 시도하고, 피어의 이웃 피어들은 시간에 따라 변동한다.
requesting chunks: rarest first
어느 임의의 시간 안에 앨리스는 청크의 일부를 가질 것이고, 이웃들이 어느 청크를 가지고 있는지를 알게 될 것이다.
앨리스는 이러한 정보를 바탕으로 다음 2가지 결정을 한다.
- 이웃으로부터 어느 청크를 먼저 요구할 것인가?
- 이웃들 중 어느 피어에게 청크를 요청할 것인가?
이때 rarest first(가장 드문 것 먼저) 기술을 사용한다.
갖고 있지 않은 청크 중에서, 이웃 가운데 가장 드문 청크를 결정하고 이를 먼저 요구하는 것이다.
이 방법을 통해 가장 드문 청크들은 더 빨리 재분배될 수 있어서 각 청크의 복사본 수가 대략적으로 동일해질 수 있다.
sending chunks: tit-for-tat
어느 요청에 응답할지 결정할 때 앨리스가 가장 빠른 속도로 그녀에게 데이터를 제공하는 이웃에게 우선순위를 주는 것이다.
- 특히, 계속해서 비트를 수신하는 속도를 측정하고 가장 빠르게 전송하는 4개의 피어를 결정하고, 이 4개의 피어에게 청크를 보냄으로써 보답한다.
- 이는 10초마다 계산하여 집합을 수정한다.
비트 토렌트 용어로 4개의 피어는 활성화되었다(unchoked)고 한다.
40초마다 위 피어를 제외하고 임의의 피어에게 청크를 보낸다.
비트 토렌트 용어로 이 피어는 낙관적으로 활성화되었다(optimistically unchoked)고 한다.
- 즉, 이제 앨리스는 임의의 피어에게 활성화될 수 있고, 활성화가 된다면 임의의 피어도 앨리스에게 활성화될 수 있다.
- 임의의 선택을 통해 고정된 피어들과만 청크를 교역하는 것이 아니라 여러 피어와 교역할 수 있게 된다.
이러한 5개의 피어 외의 모든 이웃 피어는 비활성화되어 어떤 청크도 교역하지 않는다. (choked by Alice = do not receive chunks from her)
비트 토렌트는 여기서 논의하지 않은 여러 기법들도 갖고 있다.
비디오는 이미지의 연속으로서 일반적으로 초당 24개에서 30개의 이미지로 일정한 속도로 표시된다.
압축되지 않은 디지털 인코딩된 이미지는 픽셀 단위로 구성되며, 각 픽셀은 휘도와 색상을 나타내는 여러 비트들로 인코딩된다.
비디오의 중요한 특징은 압축될 수 있다는 것인데, 비디오 품질과 비트 전송률은 서로 반비례한다.
오늘날의 상용 압축 알고리즘은 근본적으로 원하는 모든 비트 전송률로 비디오를 압축할 수 있다. (비트 전송률이 높을수록 이미지 품질이 좋다.)
네트워킹 측면에서 비디오의 가장 두드러진 특성은 높은 비트 전송률이다.
인터넷 비디오는 일반적으로 고화질 동영상을 스트리밍 하기 위해 100 kbps에서 4 Mbps 이상으로 구성된다.
4K 스트리밍은 10 Mbps 이상의 비트 전송률로 예상된다. 이는 하이엔드 동영상의 경우 트래픽과 스토리지 용량이 엄청나게 필요함을 의미한다.
연속 재생을 제공하기 위해, 네트워크는 압축된 비디오의 전송률 이상의 스트리밍 애플리케이션에 대한 평균 처리량을 제공해야 한다.
→ 압축을 사용하여 동일한 비디오를 여러 버전의 품질로 만들 수 있다.
HTTP 스트리밍에서 비디오는 HTTP 서버 내의 특정 URL을 갖는 일반적인 파일로 저장된다.
- 클라이언트는 서버에게 TCP 연결을 설립하고 해당 URL에 대한 HTTP GET 요청을 발생시킨다.
- 서버는 기본 네트워크 프로토콜 및 트래픽이 허용되는 대로 HTTP 응답 메시지 내에서 비디오 파일을 전송한다.
- 애플리케이션 버퍼에 전송된 바이트가 저장된다.
- 버퍼의 바이트 수가 미리 정해진 임계값을 초과하면 재생을 시작한다.
특히, 버퍼에서 주기적으로 비디오 프레임을 가져와 프레임을 압축 해제한 다음 사용자의 화면에 표시한다.
가용 대역폭이 달라도 똑같이 인코딩된 비디오를 전송 받는다는 문제가 있다.
이 문제로 인한 HTTP 기반 스트리밍인 DASH(Dynamic Adaptive Streaming over HTTP)가 개발되었다.
비디오는 여러가지 버전으로 인코딩 되며, 각 버전은 비트율과 품질 수준이 서로 다르다.
클라이언트는 동적으로 서로 다른 버전의 비디오를 몇 초 분량의 길이를 갖는 비디오 조각 단위로 요청한다.
가용 대역폭이 충분할 때는 높은 비트율의 비디오 버전을 요청하며, 가용 대역폭이 적을 때는 낮은 비트율의 비디오 버전을 요청한다.
즉, 클라이언트는 자신의 상황에 알맞은 비디오 버전을 요청한다.
각 비디오 버전은 HTTP 서버에 서로 다른 URL을 가지고 저장된다.
HTTP 서버는 비트율에 따른 각 버전의 URL을 제공하는 매니페스트(manifest) 파일을 갖고 있다.
클라이언트는 이 매니페스트 파일을 제공받고,
이에 따라 매번 원하는 버전의 비디오 조각 단위를 선택하여 HTTP GET 요청 메시지에 URL과 byte-range를 지정하여 요청한다.
궁극적으로 DASH는 클라이언트가 서로 다른 품질 수준을 자유롭게 변화시킬 수 있도록 허용한다.
단일 거대 데이터 센터를 구축하고 모든 비디오 자료를 데이터 센터에 저장한 뒤, 전 세계의 사용자에게 비디오 스트림을 데이터 센터로부터 직접 전송한다.
- 클라이언트가 데이터 센터로부터 먼 지점에 있는 경우 다양한 통신 링크와 ISP를 거치게 되고,
이 링크들 중 하나라도 비디오 소비율 보다 낮은 전송 용량을 갖는 경우 병목현상이 발생한다. - 인기 있는 비디오는 같은 통신 링크를 통해 여러 번 반복적으로 전송될 것이다. 동일한 바이트를 전송하는 데에 반복 비용을 지불하게 된다.
- 한 번의 장애로 전체 서비스가 중단될 수 있다.
이러한 문제를 해결하기 위해 대부분의 비디오 스트리밍 회사들은 콘텐츠 분배 네트워크(CDN)를 이용한다.
CDN은 다수의 지점에 분산된 서버들을 운영하며, 비디오 및 다른 형태의 웹 콘텐츠 데이터의 복사본을 이러한 분산 서버에 저장한다.
사용자는 최적의 사용자 경험을 제공받을 수 있는 지점의 CDN 서버로 연결된다.
CDN은 구글처럼 사설 CDN일 수도 있으며, 제 3자가 운영하는 CDN을 통해 서비스될 수도 있다.
두 개의 철학 중 하나를 채용한다.
- Enter Deep
- Bring Home
push CDN servers deep into many access networks
Akamai에 의해 주창된 것으로서 서버 클러스터를 세계 곳곳의 접속 네트워크에 구축함으로써 ISP의 접속 네트워크로 깊숙이 들어가는 것이다.
즉, 최대한 서버를 사용자 근처에 위치시켜 링크 및 라우터를 거치는 횟수를 줄여 지연시간 및 처리율을 개선하는 것이다.
smaller number (10’s) of larger clusters in POPs near (but not within) access networks
Limelight와 다른 회사들에 의해 적용된 것으로, 좀 더 적은 수의 핵심 지점에 큰 규모의 서버 클러스터를 구축하여 ISP를 Home으로 가져오는 개념이다.
접속 ISP에 연결하는 대신, 일반적으로 CDN들은 그들의 클러스터를 인터넷 교환 지점(IXP)에 배치한다.
이에 따라 Enter Deep보다 처리율(throughput)은 더 낮고 delay가 더 걸릴 수 있지만, 회사의 입장에서는 유지 보수하기에 편하며, 비용이 적게 든다.
CDN은 콘텐츠의 복사본을 이들 클러스터에 저장하는데 모든 복사본을 유지하지는 않는다.
어떤 비디오는 인기가 거의 없거나 특정 국가에서만 인기가 있을 수 있기 때문이다.
실제로 CDN은 클러스터에 대해 사용자의 요청이 오면 중앙 서버나 다른 클러스터로부터 전송받아 사용자에게 서비스하는 동시에 복사본을 만들어 저장하는 pull 방식을 이용한다.
저장 공간이 가득 차게 되면 자주 사용되지 않는 비디오 데이터는 삭제된다.
- 사용자가 URL을 지정하여 비디오를 요청한다.
- CDN은 그 요청을 가로채 클라이언트에게 가장 적당한 CDN 클러스터를 선택한다.
- 클라이언트의 요청을 해당 클러스터의 서버로 연결한다.
요청을 가로챌 때 CDN은 DNS를 활용한다. 이를 DNS redirection이라고 한다.
- 사용자가 URL을 입력한다.
- 사용자의 호스트는 URL의 host name에 대한 질의를 로컬 DNS로 보낸다.
- 로컬 DNS는 host name의 책임 DNS 서버로 질의를 전달한다.
책임 DNS 서버는 해당 질의를 CDN 서버로 연결하기 위해 CDN 서버의 책임 DNS 서버의 IP를 전달한다. - 로컬 DNS는 CDN 서버의 책임 DNS로 질의를 보내고, CDN 콘텐츠 서버의 IP 주소를 로컬 DNS 서버로 응답한다.
이때 클라이언트가 콘텐츠를 전송받게 될 서버가 결정된다. - 로컬 DNS 서버는 사용자 호스트에게 CDN 서버의 IP 주소를 알려준다.
- 클라이언트는 호스트가 알게된 IP 주소로 HTTP 혹은 DASH 프로토콜을 통해 비디오를 받아온다.
위 CDN이 DNS를 통해 가로채는 과정에서 CDN은 클라이언트의 로컬 DNS 서버의 IP 주소를 알게된다. 이 IP 주소에 기초해 최선의 클러스터를 선택한다.
지리적으로 가장 가까운 클러스터를 할당한다.
사용자 지리정보 데이터베이스를 이용하면 얻은 IP 주소를 지리적으로 매핑할 수 있고, 가장 가까운 클러스터를 선택하는 것이다.
대부분 잘 동작하나, 지리적으로 가까운 클러스터가 네트워크 경로의 길이 홉의 수에 따라 가장 가까운 클러스터가 아닐 수 있고,
가까운 로컬 DNS 서버를 이용하고 있지 않을 수 있기 때문에 잘 동작하지 않을 수 있다.
실시간 측정
클러스터와 클라이언트 간의 지연 및 손실 성능에 대한 주기적인 실시간 측정을 통해 현재 네트워크 상황을 반영하여 최선의 클러스터를 선택하는 것이다.
문제는 많은 로컬 DNS 서버가 이러한 측정에 응답을 하지 않도록 설정되어 있다.
- 콘텐츠 수집 : 영화를 수집하고 처리한다. 영화의 스튜디오 마스터 버전을 받아서 아마존 클라우드 시스템의 호스트에 업로드한다.
- 콘텐츠 처리 : 아마존 클라우드 시스템의 기기에서는 데스크톱 컴퓨터, 스마트폰, TV에 견결된 게임 콘솔 등 고객들의 다양한 플레이어 기기 사양에 적합하도록 각 영화의 여러가지 형식의 비디오를 생성한다. DASH를 이용하여 각 형식별로 다양한 비트율의 여러가지 버전을 생성한다.
- CDN으로 버전 업로드 : 영화의 다양한 버전이 생성되면 아마존 클라우드 시스템의 호스트는 이러한 버전을 CDN으로 업로드할 수 있다.
자체 CDN을 구축하기 위해 넷플릭스는 IXP 및 거주용 ISP 자체에서 서버 랙(rack)을 설치했다.
현재 IXP 위치에 200대 이상의 서버 렉과 서버 랙을 수용하는 수백 개의 ISP 장소도 보유하고 있다.
각각의 랙 서버에는 10 Gbps 이더넷 포트와 100 테라바이트 이상의 스토리지가 있다.
넷플릭스는 푸시 캐싱(push caching)을 사용하여 IXP 및 ISP CDN 서버를 채운다.
전체 라이브러리를 보유할 수 없는 위치의 경우, 매일매일 가장 많이 결정되는 비디오만 푸시한다.
- 사용자가 재생할 영화를 선택한다.
- 아마존 클라우드에서 실행 중인 넷플릭스 소프트웨어가 영화 사본을 갖고 있는 CDN 서버를 결정한다.
- 영화가 있는 서버 중에서 클라이언트 요청에 대한 최적의 서버를 결정한다.
- 일반적으로 로컬 ISP가 CDN 서버 랙(rack)이 있다면 해당 CDN을 사용하거나, 근처 CDN 서버가 있는 IXP를 사용한다.
- 클라이언트는 요청된 영화의 다른 버전에 대한 URL을 가진 메니페스트 파일과 특정 서버의 IP 주소를 보낸다.
- 클라이언트와 해당 CDN 서버는 독점 버전의 DASH를 이용하여 직접 상호작용한다.
넷플릭스는 자체 CDN을 사용하고 있기 때문에 DNS redirection을 사용할 필요가 없다.
대신 아마존 클라우드에서 실행되는 것처럼 넷플릭스 소프트웨어는 클라이언트에게 특정 CDN 서버를 사용하도록 알려준다.
또한 넷플릭스 CDN은 풀 캐싱보다 푸시 캐싱을 사용한다.
콘텐츠는 캐시 미스 중에 동적으로 사용되는 것이 아니라 사용량이 적은 시간 중 예약된 시간에 서버에 푸시한다.
넷플릭스와 마찬가지로 자체 CDN을 사용한다.
구글 역시 사용자를 특정 서버 클러스터와 연결하는 데 DNS를 사용한다.
대부분의 경우 클러스터 선택 정책은 클라이언트와 클러스터간의 RTT가 가장 적은 곳을 선택하는 것이다.
때로는 작업 부하를 위해 더 멀리 있는 CDN을 선택하기도 한다.
유튜브는 HTTP 스트리밍을 채용하여 사용자가 직접 버전을 선택하게 했다.
재생 위치 조정과 조기 종료로 인한 대역폭과 서버 자원의 낭비를 줄이기 위해,
유튜브는 HTTP byte-range 헤더를 이용해 목표한 분량의 선인출 데이터 이후에 추가로 전송되는 데이터 흐름을 제한한다.
네트워크 애플리케이션을 생성할 때는 두 프로그램, 클라이언트와 서버 프로그램을 작성해야 한다.
두 프로그램을 실행하면 프로세스가 생성되고, 두 프로세스가 소켓으로부터 읽고 쓰기를 통해 서로 통신한다.
클라이언트 - 서버 애플리케이션에는 두 가지 형태가 있다.
1️⃣ HTTP 등의 RFC에 정의된 표준 프로토콜을 구현하는 클라이언트-서버 애플리케이션
이 애플리케이션을 구현할 때 그 프로토콜과 연관된 port를 사용하여야 한다.
2️⃣ 개인의 독점적인 네트워크 애플리케이션으로 RFC 또는 다른 곳에 공식적으로 출판되지 않은 애플리케이션 계층 프로토콜을 채택하여 구현하는 애플리케이션
다른 독립 개발자는 이 애플리케이션과 상호작용하는 코드를 개발할 수 없다. 이 애플리케이션을 구현할 때는 잘 알려진 포트번호를 사용하지 않도록 유의해야 한다.
개발자는 TCP, UDP 프로토콜 중 어떤 프로토콜을 사용해야 하는지 각 프로토콜의 특징을 고려하여 선택해야 한다.
UDP를 사용할 때에는 송신 프로세스가 데이터의 패킷을 소켓 문 밖으로 밀어내기 전에, 먼저 패킷에 목적지 주소를 붙여넣어야 한다.
이 패킷이 송신자의 소켓을 통과한 후 인터넷은 이 목적지 주소를 이용하여 그 패킷을 인터넷을 통해 수신 프로세스에 있는 소켓으로 라우트(route)할 것이다.
패킷이 수신 소켓에 도착하면 수신 프로세스는 소켓을 통해 그 패킷을 추출하고, 다음에 패킷의 콘텐츠를 조사하여 적절한 동작을 취한다.
패킷에 목적지 주소를 포함함으로써 인터넷의 라우터는 목적지 호스트로 인터넷을 통해 패킷을 라우트할 수 있다.
호스트는 각자 IP 주소를 식별자로 갖는다.
그러나 호스트는 하나 혹은 그 이상의 소켓을 갖는 많은 네트워크 애플리케이션 프로세스를 수행하고 있을 수 있기 때문에,
목적지 호스트 내의 특정한 소켓을 식별할 필요가 있다.
소켓이 생성될 때 포트 번호라고 하는 프로세스 식별자가 소켓에 할당된다.
즉, 패킷에는 IP 주소와 포트번호로 구성된 목적지 주소가 붙게 되며, 송신자의 출발지 주소(IP와 port)도 붙게 된다.
일반적으로 이렇게 주소를 붙이는 것은 하부 운영체제가 자동으로 실행한다.
위 그림은 UDP 서비스 상에서 통신하는 클라이언트와 서버의 주요 소켓 관련 활동을 나타낸다.
- 클라이언트는 키보드로부터 한 줄의 문자를 읽고 그 데이터를 서버로 보낸다.
- 서버는 그 데이터를 수신하고 문자를 대문자로 변환한다.
- 서버는 수정된 데이터를 클라이언트에게 보낸다.
- 클라이언트는 수정된 데이터를 수신하고 그 줄을 화면에 나타낸다.
위 순서로 작동하는 간단한 클라이언트-서버 애플리케이션을 만들 예정이다.
소켓을 생성할 때는 따로 소켓의 포트 번호를 명시하지 않아도 되며, 운영체제가 이 작업을 대신 수행한다.
# socket module이다. 이 module을 통해 소켓을 생성할 수 있다.
from socket import *
#서버의 IP 혹은 서버의 호스트 이름을 할당한다.
serverName = ’hostname’
# 목적지 port 번호를 나타낸다.
serverPort = 12000
# 클라이언트 소켓을 생성한다. AF_INET은 IPv4를 사용하고 있음을 나타내고, SOCK_DGRAM은 UDP 소켓임을 의미한다.
clientSocket = socket(AF_INET, SOCK_DGRAM)
# 보낼 메시지를 입력 받는다.
message = Input(’Input lowercase sentence:’)
# 소켓으로 바이트 형태를 보내기 위해 먼저 encode()를 통해 바이트 타입으로 변환한다.
# sendTo() 메서드는 목적지 주소를 메시지에 붙이고 그 패킷을 프로세스 소켓인 clientSocket으로 보낸다.
# 클라이언트 주소도 같이 보내지는데 이는 자동으로 수행된다.
clientSocket.sendto(message.encode(),(serverName, serverPort))
# 패킷 데이터는 modifiedMessage에 저장되고, 패킷의 출발지 주소(IP, port)는 serverAddress에 할당된다.
# recvfrom() 메서드는 2048의 버퍼 크기로 받아들인다.
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
# 출력
print(modifiedMessage.decode())
# 소켓 닫기
clientSocket.close()
while 문을 통하여 한 번의 통신 이후에도, 계속 다음 UDP 패킷이 도착하기를 기다린다.
from socket import *
# 포트 번호
serverPort = 12000
# UDP 소켓 생성
serverSocket = socket(AF_INET, SOCK_DGRAM)
# 12000 포트 번호를 소켓에 할당한다. 이를 통해 서버 IP 주소의 12000 포트로 패킷을 보내면 해당 소켓으로 패킷이 전달된다.
serverSocket.bind((’’, serverPort))
print(”The server is ready to receive”)
while True:
# 패킷이 서버에 도착하면 데이터는 메세지에 할당되고 패킷의 출발지 주소는 clientAddress에 저장된다.
# 해당 주소로 서버는 응답을 어디에 보내야할지 알 수 있다.
message, clientAddress = serverSocket.recvfrom(2048)
# 바이트 데이터를 decode()하고 대문자로 변환한다.
modifiedMessage = message.decode().upper()
# 클라이언트 주소를 대문자로 변환된 메시지에 붙이고, 그 결과로 만들어진 패킷을 서버에 보낸다.
# 서버의 주소도 같이 보내지는데 이는 자동으로 수행된다.
serverSocket.sendto(modifiedMessage.encode(), clientAddress)
TCP는 연결 지향 프로토콜로, 서로 데이터를 보내기 전에 먼저 TCP 연결을 설정할 필요가 있다.
TCP 연결을 생성할 때 클라이언트 소켓 주소와 서버 소켓 주소를 연결과 연관시킨다.
연결이 설정된 후 소켓을 통해 데이터를 TCP 연결로 보내면 된다.
서버 프로세스가 실행되면 클라이언트 프로세스는 서버로의 TCP 연결을 시도하는데, 이는 클라이언트 프로그램에서 TCP 소켓을 생성함으로써 가능하다.
TCP 소켓을 생성할 때, 서버에 있는 환영(welcome) 소켓의 주소(IP, port #)를 명시한다.
소켓을 생성한 후 클라이언트는 3-way handshake를 하고 서버와 TCP 연결을 설정한다. (핸드셰이킹은 프로그램에서 전혀 인지 못한다.)
핸드셰이킹 동안 서버는 해당 클라이언트에게 지정되는 새로운 소켓을 생성한다. 이를 연결 소켓이라고 한다.
애플리케이션 관점에서 볼 때 클라이언트의 소켓과 서버의 연결 소켓은 파이프에 의해 직접 연결된다.
파이프를 통해 클라이언트는 자신의 소켓으로 임의의 바이트를 보낼 수 있으며, 서버 프로세스가 그것을 수신하는 것을 TCP가 보장한다. 이는 서버 입장에서도 마찬가지이다.
위 그림은 전형적인 클라이언트-서버 TCP 연결 구조이다.
UDP 프로그램과 똑같은 기능을 하는 프로그램을 작성해보자.
주석은 UDP와 다른 부분을 위주로 작성하였다.
from socket import *
serverName = ’servername’
serverPort = 12000
# 클라이언트 소켓을 의미한다. SOCK_STREAM으로 TCP 소켓임을 명시했다.
# UDP 때와 마찬가지로 따로 출발지 주소를 명시하지 않는다. (운영체제가 대신 해준다.)
clientSocket = socket(AF_INET, SOCK_STREAM)
# 클라이언트가 TCP 소켓을 이용하여 서버로 데이터를 보내기 전에 TCP 연결이 먼저 클라이언트와 서버 사이에 설정되어야 한다.
# 해당 라인으로 TCP 연결을 시작하고, connect() 메서드의 파라미터는 연결의 서버 쪽 주소이다.
# 이 라인이 수행된 후에 3-way handshake가 수행되고 클라이언트와 서버 간에 TCP 연결이 설정된다.
clientSocket.connect((serverName, serverPort))
sentence = raw_input(’Input lowercase sentence:’)
# 클라이언트 소켓을 통해 TCP 연결로 보낸다. UDP 소켓처럼 패킷을 명시적으로 생성하지 않으며 패킷에 목적지 주소를 붙이지 않는다.
# 대신 클라이언트 프로그램은 단순히 문자열에 있는 바이트를 TCP 연결에 제공한다.
clientSocket.send(sentence.encode())
# 서버로부터 바이트를 수신하기를 기다린다.
modifiedSentence = clientSocket.recv(1024)
print(’From Server: ’, modifiedSentence.decode())
# 연결을 닫는다. 이는 클라이언트 TCP가 서버의 TCP에게 TCP 메시지를 보내게 한다.
clientSocket.close()
from socket import *
serverPort = 12000
# TCP 소켓 생성
serverSocket = socket(AF_INET, SOCK_STREAM)
# 서버의 포트 번호를 소켓과 연관시킨다.
serverSocket.bind((’’, serverPort))
# 연관시킨 소켓은 대기하며 클라이언트가 문을 두드리기를 기다린다.
# 큐잉되는 연결의 최대 수를 나타낸다.
serverSocket.listen(1)
print(’The server is ready to receive’)
while True:
# 클라이언트가 TCP 연결 요청을 하면 accept() 메소드를 시작해서 클라이언트를 위한 연결 소켓을 서버에 생성한다.
# 그 뒤 클라이언트와 서버는 핸드셰이킹을 완료해서 클라이언트의 소켓과 연결 소켓 사이의 TCP 연결을 생성한다.
connectionSocket, addr = serverSocket.accept()
sentence = connectionSocket.recv(1024).decode()
capitalizedSentence = sentence.upper()
connectionSocket.send(capitalizedSentence.encode())
# 응답을 보내고 연결 소켓을 닫는다. 그러나 환영소켓인 serverSocket이 열려있어 다른 클라이언트가 서버에 연결을 요청할 수 있다.
connectionSocket.close()
'Computer Science > 컴퓨터네트워크' 카테고리의 다른 글
Chapter 3 - 4. 신뢰적인 데이터 전송의 원리, 5. 연결지향형 트랜스포트: TCP (0) | 2024.07.09 |
---|---|
Chapter 3 - 1. 트랜스포트 계층 서비스 및 개요, 2. 다중화와 역다중화, 3. 비연결형 트랜스포트: UDP (0) | 2024.07.09 |
Chapter 2 - 3. 인터넷 전자메일, 4. DNS: 인터넷의 디렉터리 서비스 (0) | 2024.07.09 |
Chapter 2 - 1. 네트워크 어플리케이션의 원리, 2. 웹과 HTTP (0) | 2024.07.09 |
Chapter 1 - 5. 프로토콜 계층과 서비스 모델, 6. 공격받는 네트워크, 7. 컴퓨터네트워킹과 인터넷의 역사 (0) | 2024.07.09 |