본문 바로가기

C&C++/C

[C/Socket] Network Programming - 패킷, 네트워크, OSI 7계층, 소켓

##### 목차 #####

1. 패킷

2. 네트워크

    (1) 호스트

    (2) 네트워크 구조

    (3) 노드

        1) 허브

        2) 스위치

        3) 브릿지

        4) 라우터

        5) 게이트웨이

3. OSI 7계층

    (1) 개요

    (2) 계층별 설명

    (3) TCP/IP

4. 소켓 프로그래밍

    (1) 개요

    (2) IP

        1) 개념

        2) 분류

        3) 확인법

        4) ARP

    (3) 프로토콜

        1) TCP

        2) UDP

        3) 포트

5. 코드 구현

    (1) C

    (2) Python

 

1. 패킷 Packet

 

인터넷으로 하고자 하는 것은 결국 정보의 전달이다.

단방향이든 양방향이든 정보를 전달해야 하는데,

실제 정보->이진화->전자 신호(Signal)->유,무선 매체 를 거치는 큰 과정에서

규격화, 양산화라는 수요가 있었고

많은 사람들이 정보의 형식을 일정한 방식으로 통일할 필요를 느꼈다.

 

우리가 실제 다루는 정보를 이진화한 후

이 정보를 통일하는 과정에서

정보를 여러 조각으로 잘라내거나 암호화 하는 등의 규격화, 가공 과정을 거치고,

어떤 규격으로 통일하였다는 정보를 표시한다.

 

이렇게 특정 규격임을 표지한 정보를 패킷 Packet 이라고 한다.

 

패킷에 관련한 더 자세한 내용은 PCAP 관련 포스팅을 참조.

링크 : [C/Socket] Network Programming - PCAP

 

 

2. 네트워크 Network

 

(1) 호스트 Host

 

인터넷 연결은 End to End 패킷 전달을 목적으로 한다.

이 때 각 End는 개인 단말나 서버 등이고, 이를 Host라고 부른다.

 

주고 받는 관계가 일방적인 경우도 있고 양방향인 경우도 있는데,

시스템적으론 권한의 문제이기에 엄청난 차이가 있지만

Low-level에서는 어쨌거나 패킷이 오고 간다는 점에서 큰 차이는 없다.

 

패킷의 입장에서 발송자를 Source, 수신자를 Destination 이라고 부른다.

패킷의 종류를 나누는 기준은 여러 가지가 있지만

가장 큰 갈래로 나누면 Request와 Response가 될 텐데,

Request는 Active하게 보내는 패킷이고

Response는 Request를 선행 조건으로 하는(Passive한) 패킷이다.

 

이 때 Request를 하는 Host를 Client라고 부르고

Request를 기다리는 Host를 Server라고 부른다.

일반적으로 Client는 우리가 사용하는 단말기(스마트폰, PC)이고,

서버는 많은 자원을 가용할 수 있도록 만든 일종의 슈퍼컴퓨터다.

 

 

(2) 네트워크 구조 Network

 

 

개념적으로 연결을 도식화한다고 생각했을 때,

로컬 환경에서야 선 하나로 1대1 통신을 보여줄 수 있지만

범국가적인 연결을 고려해보면 연결의 개수도 많아지며, 그 구조도 매우 복잡해진다.

이 개념도가 마치 거미줄과 같아 Network 라고 부른다.

이 때 각 연결을 Link, Edge 등으로 부르고,

그 연결이 모이는 지점인 기기를 Node, Vertice 등으로 부르며,

한 Node에서 원하는 Node까지의 경로를 Path, Context 등으로 부른다.

 

 

End to End Path에서 네트워크적인 관점으로는,

물리적으로 매체(유선, 광통신, 무선 등)나 장비(라우터 등)을 바꾸어 전달 시간을 줄이는지 보다

주어진 매체, 장비 등의 환경에서 어떤 Node와 Path를 몇 번 거치는지가 더 중요하고

그 과정에서 전달 시간이나 망 점유율을 최소화하는 알고리즘을 구현하는 것이 더욱 중요하다.

자료구조나 알고리즘 시간에 배웠던 그래프 구조에서 BFS, Dijkstra 알고리즘 등을 배운게 이 때문이다.

 

매체 관련해서 궁금한 점이 있다면 아래의 다른 포스팅을 참조할 것.

(다른 포스팅 링크 추가할 것)

 

(3) Host 외의 Node

 

현실적으로 End to End를 단일 매체로 구현하는 것은 불가능에 가깝다.

LA에 있는 서버에서 우리 집으로 패킷을 보내려면,

아무리 줄인다고 하더라도 최소한 인공위성 하나 정도는 거쳐야한다.

 

따라서 패킷들을 잠시 저장하고 방향을 정해 보내줄 중간 거점이 필요한데,

스위치, 브릿지, 라우터 등의 장비가 그렇다.

 

 

1) 허브

하나의 패킷 신호를 그대로 다른 포트에 나눠서 보내주는 장비로,

이를 받아들일 지 아닐지는 각자의 터미널이 결정한다.

CPU나 Buffer가 없으므로 위에서 말한 "중간 거점"에 해당되지 않아, Node라고 하기엔 부적절한 점이 있다.

개념적으로 하나의 버스를 실시간으로 n대가 공유하므로 데이터 속도는 1/n대가 된다.

실제로는 cpu, buffer를 넣어서 적절히 스위칭해 1/n 보다는 성능을 높인 허브가 많이 팔리는데,

이는 이론적으로 스위치 라고 할 수 있다.

왜 이걸 허브라고 이름을 붙여 파는지는 잘 모르겠다.

 

2) 스위치

위에서 말한 대로 CPU, Buffer를 넣어 스위칭 해주는 장비로

연결된 장비들의 MAC 주소를 이용해 프레임을 필터링, 특정 장비에만 전달해준다.

 

3) 브릿지

마찬가지의 역할이나, 하드웨어가 아닌 소프트웨어 적으로 해당 역할을 수행한다.

덕분에 속도는 조금 더 느리지만, 전달할 수 있는 포트 수는 많아진다.

 

4) 라우터

하나의 네트워크에서 다른 네트워크로 패킷을 전달하기 위해 사용하는 장비로,

위의 스위치, 브릿지는 같은 네트워크 내에서만 작동하는 것과는 대비된다.

충돌 도메인과 브로드캐스트 도메인을 분리해서 작동하는 방식으로,

세그먼트 연결 작업 뿐 아니라 도메인 분리 작업도 라우팅 속도를 결정짓는다.

 

5) 게이트웨이

장비가 아니라 개념적인 네트워크 간 연결 통로다.

대부분의 경우에는 라우터를 사용하지만,

꼭 라우터는 아니고 스위치나 브릿지가 게이트웨이 역할을 하는 경우도 있고,

장비가 아니라 소프트웨어 적으로 게이트웨이를 구현할 수도 있다.

즉, 장비 종류가 아닌 "역할"에 대한 이름이다.

 

 

 

3. OSI 7계층

 

(1) 개요

 

네트워크 구조상 정말 수 없이 많은 방법으로 End to End 연결이 가능하다.

막말로, 다 씹고 존1나 쎈 전기 신호 보내서 남아공-대한민국을 안테나-안테나 바로 연결할 수도 있는데,

이런 경우 위의 네트워크 장비들은 하나도 필요 없다.

 

그런데 저런 예외는 우주에서나 필요하고,

우리 대부분이 일상생활에서 인터넷 할 때는 공유기에 모뎀써서 기지국에 연결한다.

이런 정형화된 경우를 고려해서 OSI 7계층이란 걸 고안했는데,

IP 포트포워딩 하는 사람이 무선 연결 강도를 고려할 필요 없도록

각 단계별로 고립화 Encapsulation 시킨 것이다.

 

좋은 결과로는

안테나, 스위치, 라우터, 서버, 어플 개발자가

전부 자기 역할만 하면 된다는 거고,

나쁜 결과로는

존1나 쎈 우주 전기 신호 받아서 인터넷하는 등

규격에 없는 방법은 알아서 구현해야 한다는 점이 있다.

 

 

(2) OSI 7계층 설명

OSI 7계층과 Node 별 연결성

 

위 처럼 7계층으로 나누었다.

 

1계층 Physical : 인터넷 선, 무선, 광통신, 리피터, 허브

물리적인 연결

 

2계층 Datalink : 브릿지, 스위치

기기 간 프레임 전달, 기기 주소(MAC) 부여

CRC 에러 검출, 재전송, 흐름 제어

 

3계층 Network : 게이트웨이(라우터 등)

패킷 전달, 주소(IP) 부여, QoS 결정

경로 설정(Route), Segmentation, 오류 제어

 

4계층 Transport : TCP, UDP 등

패킷 생성 및 전송, End to End 제어

연결 기반의 상태 제어, 신뢰성 확보

중복 검사 및 연결, 오류, 흐름 제어

 

5계층 Session : ADSP, SCP, PPTP, SOCKS, H.245 등

논리적 연결, 유효성 검사

어플리케이션에서 실질적으로 사용하는 통신 종단

Duplex, Half-duplex, Full-duplex 결정

OS가 세션 연결, 중단, 재연결 등 실질적 역할

 

6계층 Presentation : JPEG, MPEG 등

표현 방식이 일치하도록 변환

Encoding/Decoding

Compression/Decompression

 

7계층 Application : HTTP, DHCP, DNS, FTP, Telnet 등

사용자가 실제로 사용하는 프로토콜

 

1~3계층은 하드웨어 적인 면이,

4~7계층은 소프트웨어 적인 면이 강조된다.

 

 

(3) TCP/IP

 

 

 

OSI 모델에 따른 7계층은 너무 많다.

TCP/IP는 OSI 계층을 일부 통합해서 4계층으로 줄여놓았다.

실제로 우리가 인터넷에서 가장 많이 사용하는 형식은 TCP/IP다.

인터넷 프로토콜 스위트 (Internet Protocol Suite) 라고도 한다.

 

4계층은 다음과 같다.

Network Interface : 하드웨어가 알아서 해주겠지 ㅎㅎ (+ARP)

Internet : IP 부여

Transport : TCP 형식으로 전송

Application : 니들이 알아서 해라 ㅎㅎ

 

매우 무책임하고 효율적이라서 좋다.

통신 기기를 ARP 스캔한 뒤 MAC주소로 TCP/IP 형식의 데이터를 보내면 Response 데이터가 저절로 굴러들어온다.

실제 app 단에서는 어차피 랜선(이더넷) 아니면 와이파이(무선)이니까 MAC도 필요 없고...

아주 편리하다.

 

 

4. 소켓 프로그래밍

 

(1) 개요

 

소켓 프로그래밍이란 무엇일까?

네트워크 소켓이 정식 명칭이며, 대부분의 네트워크가 인터넷 형식이므로 인터넷 소켓이라고도 부른다.

프로세스간 통신의 종착점으로, Host의 데이터 말단, 즉 End를 의미한다.

소켓 프로그래밍을 해석 하자면 "네트워크 말단끼리 데이터를 주고 받는 방법" 에 관한 프로그래밍이다.

 

OSI 계층상 어느 단계에서도 적용될 수 있으나

일반적으로 소켓프로그래밍이라고 하면 3~4계층에서 벌어지는 일을 말한다.

3보다 내려가면 너무 하드웨어적이고 4보다 올라가면 너무 어플리케이션 적이니

Encapsulated되어 더이상 관리할 필요가 없는 상태를 지향하는 소켓 프로그래밍에서

꾸준한 유지보수를 필요로 하는 1, 2, 5, 6, 7 층은 제외하고 살펴보자.

 

 

 

 

(2) 패킷 구조

위 처럼 잘게 자른 데이터의 앞, 뒤에 헤더, 테일을 붙이는데,

APP -> PHY 로 보내므로 7층->1층 헤더를 차례대로 붙인다.

AH(Application Header) -> TH(Transport layer Header) -> NH(Network layer Header) -> DH(Datalink layer Header) 순으로 붙는다.

자주 사용하는 TCP/IP의 경우 TH로 TCP헤더, NH 로 IP헤더가 차례로 들어간다.

IP의 경우 아직 IPv6가 많이 보급되지 않아 IPv4가 국룰이므로 IPv4 헤더를 아래에 예시로 넣었다.

이후 DH는 랜선으로 보내면 Ethernet Header(IEEE 802.3), 무선으로 보내면 Wi-Fi header(IEEE 802.11) 를 앞에 추가로 붙인다.

 

아래에 자세한 패킷 구조를 도식화한 이미지들을 긁어 모아놓았다.

 

1) TCP/UDP Header

 

TCP/UDP 헤더

 

2) IP Header

 

IPv4 헤더(좌)와 IPv6 헤더(우)

 

 

3) Ethernet/Wifi Header

 

Ethernet/Wifi 헤더

 

EtherNet은 802.3 헤더, Wifi는 802.11 헤더를 사용한다.

보다 자세한 내용은 아래 그림 참조

 

 

 

 

 

3. 통신 절차

 

(1) 개요

 

원하는 통신 종류에 따라 헤더를 붙이거나 해석하는 것은

switch case 구문만으로도 충분히 구현할 수 있다.

심지어 이미 많은 하드웨어와 소프트웨어가 헤더 붙이기, 헤더 해석하기를 구현해 놓아

코드 역시 간편하기 그지없다.

 

그러니까, 헤더에 어떤 정보가 들어있고 어떻게 파싱해서 어떤 함수를 적용할지를

구체적으로 알 필요도 없고, 알더라도 일일히 구현할 필요도 없다.

우리가 알아야할 것은 오직 세 가지 뿐이다.

 

1) Dayalink Layer : 데이터 전송 매체, 랜포트나 랜카드의 MAC주소

2) Network Layer : IP주소

3) Transport Layer : 프로토콜 형식, 포트 번호

 

그러니까,

time.bora.net(IP)에 UDP(프로토콜) 중 NTP 프로토콜 (123번 포트) 으로 유선랜 연결해야지 -> 글로벌 시간을 받아옴

192.168.0.13(IP)에 TCP(프로토콜) 중 SSH 프로토콜 (22번 포트) 으로 와이파이 연결해야지 -> 같은 공유기의 다른 기기에 SSH 접속

하면 되는 것이다.

 

하드웨어는 OS에서 잡아주니까, 우리는 IP/프로토콜 만 신경쓰자.

 

*주의 : 포트 번호는 4계층 Transport Layer에 속하지만, 포트 번호에 해당하는 프로토콜(NTP, SSH) 등은 7계층 Application Layer에 속한다

 

 

(2) IP

 

1) 개념

 

Internet Protocol의 약자인 IP는 각 네트워크 Host를 구별하기 위한 논리적인 주소다.

호스트는 아까 말했는데, 논리적인?

물리적인에 대응되는 말로, 추상적인 이라고 이해하면 된다.

물리적 주소는 OSI 2계층에 존재하는 MAC주소로,

이 MAC 주소를 이용하면 실제 그 디바이스에 접근할 수 있다.

IP주소는 그 디바이스로 갈 수 있는 관문이라고 보면 된다.

 

잘 이해가 안된다면, 네트워크 구조도에서 Edge node는 MAC 주소, Connecting node는 IP주소라고 생각해보자.

실제와는 좀 다르긴 하지만 얼추 이해가 될 것이다.

 

2) 분류

 

IP는 두 종류가 있다.

IPv4 : xxx.xxx.xxx.xxx

IPv6 : xxx.xxx.xxx.xxx.xxx.xxx

 

각 xxx는 0-255의 값을 갖는, 그러니까 \x00부터 \xFF까지의 값을 갖는 1Byte의 크기의 숫자고

이를 4자리 혹은 6자리 연속해서 할당해 놓은 것이 IP다.

 

256^4 하면 4,294,967,296 라는 숫자가 나오는데,

고작 40억의 숫자 가지고는 전세계 인구를 다 커버할 수 없을 뿐더러

늘어나는 IoT 디바이스 수요를 감당할 수 없어

IPv6를 사용하는 것이 권장되고 있다....

라지만 그 얘기가 나온지 10년이 넘었음에도 아직 IPv4를 사용하고 있다.

 

IP의 표기법에는 세 종류가 있다.

10진수 IP : 172.217.25.100

2진수 IP :  10101100 11011001 00011001 01100100

도메인 IP : www.google.com  

 

10진수는 우리가 흔하게 보는 그 IP고,

2진수는 실제로 컴퓨터가 받아들이는 IP이지만, 개념적으로는 10진수를 단순 변환한 것에 지나지 않는다.

 

도메인 주소는 무엇일까?

IP주소를 그냥 통채로 외워야 한다고 생각하면 10진수든 2진수든 머리가 빠개질 것이 분명하다.

인간이 익숙한 텍스트 형태로 일부 변환한 것이 도메인 주소다.

 

도메인 주소를 10진수 혹은 2진수 IP로 바꾸는 방법은 다음과 같다.

 

생활코딩에서 설명하는 도메인 IP

DNS(Domain Namespace Server)는 도메인주소 쿼리를 받아 IP 주소를 응답으로 주는 서버다.

대부분 가정집은 통신사 ISP를 기본 DNS 서버로 이용하고 있다.

 

C에서는 다음과 같은 함수들을 기본으로 <netinet/in.h> 에서 제공한다.

 

참고 사항으로, 대부분 소켓 프로그래밍을 할 때 <arpa/inet.h> 헤더를 함께 include 할 텐데,

arpa/inet.h 헤더에는 이미 netinet/in.h 가 포함되어있어 arpa/inet.h만을 include해도 된다.

단지 의존성이 꼬이는 것을 방지하거나 컴파일 속도 향상, 코드 유지 보수 등을 위해 둘 모두를 include하는 것을 권장할 뿐이다.

 

 

3) 확인법

 

리눅스에서는 다음과 같이 간단한 명령어로 자신의 IP를 확인할 수 있다. (윈도우의 경우 cmd를 열어 ipconfig 를 입력해보자.)

sudo apt install net-tools
ifconfig // showing network device configuration
sudo apt install wireless-tools
iwconfig // wireless device configuration

 

리눅스의 경우 nslookup 으로 외부 도메인의 IP를 확인해볼 수 있다.

 

 

 

4) ARP

 

Address Resolution Protocol의 약자다.

 

OSI 3계층에는 ARP도 있는데,

장비인 라우터 등이 한다는 IP(3계층)-MAC(2계층) 주소 연결 역할을 이 계층에서 한다.

 

MAC 주소는 디바이스의 시리얼 넘버, 물리 주소, 진짜 위치 정도라고 보면 된다.

 

인터넷을 통해 디바이스에 접속하기 위해서 라우터를 거쳐갈 때

외부에서는 (라우터의 IP + 디바이스 찾기위한 포트 번호) 로 접속하고

라우터는 해당 포트번호를 디바이스의 포트번호로 바꿔준다.

이를 포트포워딩이라고 부른다.

포트포워딩

 

이 때 라우터 내부에서 각 디바이스는 각자의 내부 IP주소(공유기의 경우 192.168.x.x)를 가지고 있고,

이는 각 디바이스의 고유 번호인 MAC 주소에 매칭되어있는데

이 [내부 IP주소-MAC 주소] 매칭 테이블을 ARP 테이블이라고 부르며,

이 테이블에 요청하기 위한 프로토콜이 ARP다.

흔한 공유기를 예시로 들기 위한 시나리오였을 뿐,

실제로는 굳이 내부 IP주소일 필요도 없다.

IP주소 <-> MAC 주소 변환 테이블에 요청하는 쿼리 형식을 ARP라고 생각하자.

 

내 컴퓨터가 172.16.119.1 주소로 인터넷에 연결되어있다.

같은 공유기에 물려있는 172.16.119.2 주소를 가진 라즈베리파이와 통신을 하고싶은데,

LAN 환경에서는 IP가 아니라 MAC 주소를 이용해 통신을 하니 라즈베리파이의 MAC 주소를 알아야한다.

이건 누가 가지고 있을까? 컴퓨터에는 없고, 공유기가 가지고 있다.

그러면 공유기한테 라즈베리파이의 MAC 주소를 달라고 요청해야겠네?

이 요청이 ARP 프로토콜로 이루어진다.

 

Wireshark로 감청한 데이터그램

랜카드(172.16.119.1)가 브로드캐스트(255.255.255.255, 아무나)에게 172.16.119.2의 MAC주소를 달라고 ARP 프로토콜을 통해 요청하는 모습이 위 사진에 잘 나타나있다.

 

 

(3) 프로토콜

 

여러가지 있지만 TCP, UDP 정도만 알면 된다.

 

1) TCP

Transmission Control Protocol 의 약자로, 연결지향적인 프로토콜이다.

"연속적인" "신뢰성있는" 정보 전달을 목적으로 한다.

때문에 데이터를 보내거나 받은 뒤 응답하는 것이 거의 필수적인 과정이며,

이를 통해 데이터 전송이 성공했는지 체크하고,

음... 체크까지만 강제고, retry나 pass 같은 건 코드 구현하는 사람 마음이다.

 

소켓에 관해 설명할 때 간단하게 사진을 첨부했지만, 의미를 하나하나 짚어보자.

 

Source port, Destination port는 말 그대로 전송 포트 번호를 의미한다.

포트 번호는 일반 유저가 사용할 때에는 대부분 Session layer 와 함께 엮여서 SSH 등의 프로토콜을 구성하게 되는데,

특히 Destinatioin port같은 경우 방화벽이나 포트포워딩 같은 신경써야 하는 것들이 많다.

각각 16비트씩이니 2^16-1인 65535까지 사용할 수 있다.

 

Sequence number은 두 가지 사용처가 있는데

1) 통신을 시작하는 경우 : 랜덤한 값을 가진다.

2) 통신 계속 중인 경우 : 여태까지 통신한 횟수를 나타낸다.

 

Acknowledgment number 는 항상 직전에 받은 Sequence number에 1을 더하여 보낸다.

 

통신을 시작하는 경우 랜덤한 값을 가지는 이유는, Acknowledgment number의 특성을 이용해

랜덤한 숫자를 Sequence number에 실어 보냈는데, 거기에 1을 더한 값을 Acknowledgment number로 받은 경우

통신이 성공적으로 왕복했음을 알 수 있다.

 

Data offset 은 TCP 헤더 크기를 말하며, 32bit word 단위다.

 

Options가 없는 경우 최솟값인 5(=160/32)를 갖고, Data offset 비트가 1111인 경우 최댓값 15를 가진다.

Reserved 는 미래를 위해 남겨놓은(이라고는 말하지만 쓸 데가 없어 남겨놓은) 필드다. 하드웨어는 N/C 라고 표현하는데 소프트웨어는 Reserved라고 표현한다.

 

나머지 Flags Field는 위키백과를 보자.

주목할만한 것은 SYN 인데,

Sequence/Acknowledgment number field에서 말했듯이 "맨 처음에 송수신 왕복할 때" 를 나타내는 플래그다.

만약 이게 정상적으로 통신하는 도중에 갑자기 1이 된다면?

두 가지 경우가 있는데, 첫 째는 잘못 해석한 것이고, 두 번째는 받는 쪽이 모르는 사이에 보낸 쪽은 통신을 끊었다가 다시 연결한 것이다.

두 경우 모두 통신 라인이 busy한 경우에 발생하므로, 이러한 사태가 발생할 것 같으면/발생하면 congestion notification을 주는데, 해당 필드가 ECE 필드다. 만약 ECE가 수신되었으면 Congestion control mechanism에 따라 처리한 뒤 CWR 플래그를 1로 하여 응답한다.

Congestion control mechanism은 유저가 알아서 구현하면 된다. TCP 소켓 통신 옵션에서 설정하면 유저 함수를 갖다 붙일 수 있다.

 

Window size는 수신 윈도우의 크기인데, 현재 보내는 연결이 수신 가능해야 하므로 ACK sequence number(여태까지 주고받은 량 + 1) 보다는 커야한다.

 

Checksum은 말 그대로 체크섬 비트다. 오류가 없는 지 체크하는 비트들인데, TCP/IP의 경우 CRC 알고리즘을 사용한다.

내용은 길지만 간단하게 결과만 말하자면,

( data<<(key length) | key ) 를 미리 정해진 CRC check bit으로 나눴을 때 나머지가 0이 되도록 하는 CRC 비트를 의미한다.

같은 말로는 ( data<<(key length) ) 를 CRC check bit로 나눈 나머지가 key 라고 할 수 있다.

미리 정해진 CRC check bit가 바로 Checksum bit 이고, 이 경우 16비트이니 CRC-16을 사용할 것이다.

다양한 CRC-16 체크섬 비트들

이렇게 주어진 CRC-16 비트로 data를 나눈 뒤 key를 비교하면 제대로 전송되었는지 확인할 수 있다.

 

URGENT pointer 는 Flag field의 URG가 1인 경우 그 urgent data의 위치를 나타내기 위한 offset 값이다.

 

 

2) UDP

 

UDP는 TCP보다 훨씬 쉽다.

체크하고 그런 거 없이 그냥 보내고 땡.

단순 속도 면에서 TCP보다 훨씬 빠르니 실시간성에서 많은 유리함이있다.

영상/오디오 와 같이 데이터의 크기가 크고, 작은 오차 하나가 전체를 좌우하지 않는 영역에서 많이 사용된다.

헤더 구조도 TCP에 비교하면 매우 간단하다.

주목할 만한 점은 체크섬이 있다는 사실인데,

패킷 하나가 사라지는 손실정도는 감수하지만 오류가 난 데이터를 사용하지는 않는다는 것을 보여준다.

 

 

3) 포트

 

포트는 서비스 번호를 식별하는 논리적인 단위다.

굳이 이렇게 말하는 이유는, TCP/UDP 말고도 포트를 쓰는 곳이 있어서 그렇다.

실제로는 TCP/UDP에서 서비스 기능 별로 번호를 세분화하는 방식으로 쓰인다.

위키백과에서도 "TCP/UDP의 포트" 같은 식으로 언급한다.

 

TCP/UDP 포트는 크게 세 가지가 있는데

Well-known port : 0~1023

Registered port : 1024~49151

Dynamic port : 49152~65535

로 나뉜다.

0-65535의 범위인 이유는 port가 주로 16bit unsigned int 로 표현되기 때문이다.

 

 

https://ko.wikipedia.org/wiki/TCP/UDP%EC%9D%98_%ED%8F%AC%ED%8A%B8_%EB%AA%A9%EB%A1%9D

 

TCP/UDP의 포트 목록 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 둘러보기로 가기 검색하러 가기 알려진 포트(well-known port)는 특정한 쓰임새를 위해서 IANA에서 할당한 TCP 및 UDP 포트 번호의 일부이다. 일반적으로 포트 번호는 �

ko.wikipedia.org

위 위키백과를 보면 엄청나게 많은 수의 포트가 있다는 것을 볼 수 있다.

 

Well known port 몇 가지만 보고 가자.

 

[TCP 20, 21] FTP : 파일 전송을 위한 프로토콜이다. 20번은 데이터, 21번은 제어 포트다.

[TCP 22] SSH : Secure shell 을 위한 프로토콜이다. 외부에서 원격 접속할 때 자주 쓴다. 너무 자주쓰는 나머지 해킹 위험이 높아, 공유기같은 경우 방화벽에서 22번 대신 다른 포트를 대신 열어놓고 내부 포트포워딩해서 쓴다. 자세한 사항은 http://www.openssh.org 참조.

[TCP 23] Telnet : 로컬에서 프로세스간 텍스트 전송을 위해 사용한다. Putty 같은 프로그램을 사용하다보면 언젠가 쓸 날이 있다.

[TCP/UDP 53] DNS : Domain Namespace Server 에 접속하기 위한 포트번호다. 인터넷에 접속할 때 마다 쓴다.

[UDP 69] TFTP : 간단한 파일전송에 사용된다. FTP의 간소화 버전.

[TCP/UDP 80] HTTP : 웹페이지에 사용된다.

[UDP 123] NTP : 서버시간을 가져올 때 사용한다. 부팅때마다 실행되는 편.

[TCP 443] HTTPS : SSL로 보안통신을 적용한 HTTP.

 

그 외에 자주 쓰이는 Registered port는 다음과 같다.

[TCP/UDP 1194] OpenVPN : 그러하다.

[TCP 3306] MySQL : 기본 포트로 설정되어있다.

[TCP 6379] Redis : 기본 포트로 설정되어있다.

[TCP 8888] jupyter : 파이썬 ipynb의 노트북형인 jupyter notebook 로컬 연결에 사용된다.

[TCP/UDP 17500] Dropbox : 드랍박스 파일전송 기본 포트.

 

Dynamic port는 우리가 심심할 때 가지고 놀 때 쓰면 된다.

 

*다시 한 번 말하지만 주의할 점은, 포트 번호는 4계층의 논리적인 식별 번호고, 포트 번호에 해당하는 서비스들은 7계층 Application Layer에 해당한다.

포트 번호와 서비스의 연결은 일반적으로 그렇다는 것이지, 절대적이지 않고 분리되어있다.

예시를 들자면, UDP 123을 꼭 NTP 서버 연결에만 사용할 필요는 없고, 굳이 쓰고 싶다면 마음대로 어플리케이션 연결해서 써도 된다는 뜻이다.

 

4. 코드 구현

(1) C 에서의 구현

 

server.c

#include <arpa/inet.h>
#include <netinet/in.h>

int main() {
	
}

client.c

#include <arpa/inet.h>
#include <netinet/in.h>

int main() {
	
}

 

 

(2) Python 에서의 구현

 

server.py

import socket

ss = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP(STREAM), IPv4
ss.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ss.bind(("127.0.0.1", 65533)) # IP(Localhost), Port
ss.listen() # Allow client to access server
client_socket, client_addr  = ss.accept()

while True:
    data = client_socket.recv(1024)
    if not data:
        break
        
    print(data.decode()) # echo
    
    client_socket.sendall(data) # echo back
    
# close socket
client_socket.close()
ss.close()

 

client.py

import socket

cs = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
cs.connect(("127.0.0.1", 65533))
cs.sendall("hello\n")
data = client_socket.recv(1024)
if not data:
    print("no data")
else :
    print(data.decode())
cs.close()

 

 

'C&C++ > C' 카테고리의 다른 글

[C/C++] 헤더 파일은 어디서 소스를 가져올까?  (1) 2020.10.21