소켓 프로그래밍과 multiplexing

Tasker_Jang·1일 전
post-thumbnail

1. 소켓의 두 종류와 호출 순서

소켓은 애플리케이션과 네트워크 사이의 인터페이스이고, 어떤 transport를 쓰느냐에 따라 TCP 소켓과 UDP 소켓으로 나뉩니다.

TCP 소켓은 연결을 먼저 세워야 하므로 서버와 클라이언트가 호출하는 함수가 다릅니다. 서버는 create, bind, listen, accept 순서로 자리를 잡고 기다리며, 클라이언트는 socket을 만든 뒤 connect로 찾아갑니다. 연결이 되면 양쪽 모두 read와 write로 데이터를 주고받습니다.

주목할 점은 accept()가 새 소켓을 돌려준다는 것입니다. listen하던 소켓은 계속 문지기로 남고, 실제 대화는 클라이언트마다 새로 만들어진 소켓으로 합니다. 이 구조가 뒤의 TCP demultiplexing을 이해하는 열쇠입니다.

2. 코드로 보기

메모대로 코드로 구현할 수 있습니다. 파이썬 표준 라이브러리만으로 충분합니다.

# TCP 에코 서버
import socket

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # create
srv.bind(("0.0.0.0", 9000))                               # bind
srv.listen()                                              # listen
while True:
    conn, addr = srv.accept()        # 클라이언트마다 새 소켓
    data = conn.recv(1024)           # read
    conn.sendall(data)               # write
    conn.close()
# TCP 클라이언트
import socket

cli = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
cli.connect(("127.0.0.1", 9000))
cli.sendall(b"hello")
print(cli.recv(1024))

UDP는 연결이 없으므로 listen, accept, connect가 필요 없습니다. SOCK_DGRAM으로 만들고 bind한 뒤, 보낼 때마다 목적지를 지정하는 sendto()와 받을 때 보낸 쪽 주소를 함께 받는 recvfrom()을 씁니다. 연결이라는 개념이 없으니 매 데이터그램에 주소를 적어야 하는 것입니다.

3. transport 계층: network 계층이 해주지 않는 것

이제 문 너머로 내려갑니다. network 계층은 IP address를 보고 데이터를 호스트까지만 배달합니다. 그 호스트 안에서 어느 프로세스가 받아야 하는지는 모릅니다. transport 계층이 가장 먼저 책임지는 것이 바로 이 마지막 구간, 호스트에서 프로세스까지의 전달입니다.

이를 위해 transport 계층은 애플리케이션의 데이터에 source port와 destination port를 담은 header를 붙입니다. 이렇게 만들어진 덩어리를 segment(세그먼트)라고 부릅니다.

  • multiplexing(다중화): 송신 측에서 여러 소켓의 데이터를 모아 각각 헤더를 붙여 network 계층으로 내려보내는 것
  • demultiplexing(역다중화): 수신 측에서 도착한 segment의 헤더를 보고 올바른 소켓에 넘겨주는 것

4. UDP 헤더

UDP는 multiplexing과 demultiplexing 외에 거의 아무것도 하지 않는 transport입니다. 그래서 헤더도 단출합니다. UDP 헤더는 항상 8바이트이며, 네 필드가 각각 2바이트씩입니다.

필드크기역할
source port16bit답장을 보낼 주소. 수신 측이 응답할 때 destination port가 됨
destination port16bitdemultiplexing의 기준. 어느 소켓으로 보낼지 결정
length16bit헤더와 데이터를 합친 전체 길이
checksum16bit전송 중 비트 오류 검출

checksum 계산에는 UDP 헤더와 데이터뿐 아니라 IP 헤더에서 가져온 출발지·목적지 주소 등의 pseudo header도 포함됩니다. 또 IPv4에서는 checksum이 선택 사항이고, IPv6에서는 필수입니다. 오류를 검출만 할 뿐 고치거나 재전송하지는 않습니다.

TCP 헤더와 IP 헤더도 이후 동작을 이해하는 데 매우 중요합니다. 각각 TCP 편과 IP 편에서 필드별로 정리합니다.

5. UDP demultiplexing과 TCP demultiplexing은 다르다

같은 demultiplexing이지만 소켓을 찾는 기준이 다릅니다.

UDPTCP
식별 키(dest IP, dest port) 2-tuple(src IP, src port, dest IP, dest port) 4-tuple
서버 소켓 수포트당 하나. 모든 클라이언트 공유listen 소켓 하나 + 클라이언트마다 하나
출발지가 달라도같은 소켓으로 들어감다른 소켓으로 들어감
이유연결이 없음연결마다 독립된 상태를 유지

UDP 소켓은 목적지 IP와 목적지 포트만으로 완전히 식별되므로, 출발지가 다른 두 세그먼트도 목적지가 같으면 같은 소켓에 도착합니다. 반면 TCP 소켓은 네 값 전부로 식별되어, 출발지 IP나 포트가 다르면 다른 소켓으로 향합니다. 1절에서 accept()가 새 소켓을 돌려준 이유가 여기서 이어집니다. 연결마다 별도 소켓이 있으니, 도착한 세그먼트가 어느 연결 소속인지 가리려면 출발지 정보까지 봐야 합니다.

profile
ML Engineer 🧠 | AI 모델 개발과 최적화 경험을 기록하며 성장하는 개발자 🚀 The light that burns twice as bright burns half as long ✨

0개의 댓글