호우동의 개발일지

Today :

article thumbnail

UDP를 이용한 소켓 프로그래밍


UDP 소켓 프로그래밍 과정

  1. 송신 프로세스가 데이터 패킷을 소켓 밖으로 내기 전에 먼저 패킷에 목적지 주소를 붙여 넣음
  2. 패킷이 송신자의 소켓을 통과
  3. 해당 소켓을 인터넷을 통해 수신 프로세스에 있는 소켓으로 라우트함
  4. 패킷이 수신 소켓에 도착
  5. 수신 프로세스는 소켓을 통해 해당 패킷을 추출하고 다음에 패킷의 콘텐츠를 조사 및 동작

 


UDP 패킷 구성

  • 목적지 주소
    • 목적지 주소 = 목적지 호스트 IP 주소 + 소켓 포트 번호
    • 목적지 호스트 IP 주소
      → 이를 통해 인터넷의 라우터는 목적지 호스트로 인터넷을 통해 패킷을 라우트 가능

    • 소켓 포트 번호
      • 포트 번호(port number) : 소켓이 생성될 때 소켓에 할당되는 식별자
        • 호스트는 여러 개의 소켓을 갖는 많은 네트워크 애플리케이션 프로세스 수행 가능
          → 소켓 식별이 필요

 

 


UDP 소켓 프로그래밍 예시


임의의 클라이언트-서버 애플리케이션 사용

  1. 클라이언트는 키보드로부터 한 줄의 데이터를 읽고 그 데이터를 서버로 보냄
  2. 서버는 그 데이터를 수신하고 문자를 대문자로 변환
  3. 서버는 수정된 데이터를 클라이언트에게 보냄
  4. 클라이언트는 수정된 데이터를 수신하고 그 줄을 화면에 나타냄

 


UDP 전송 서비스상에서 통신하는 클라이언트-서버의 주요 소켓

서버 클라이언트 udp 소켓 통신 과정

  1. 간단한 애플리케이션 레벨 메시지를 서버에게 보내는 UDP 클라이언트로 시작
  2. 서버가 클라이언트의 메시지를 수신하고 응답할 수 있도록,
    서버는 준비 및 수행되고 있어야 함
    → 즉 클라이언트가 메시지를 전송하기 전에 프로세스를 수행하고 있어야 한다.

 


프로그램 예시

  • 가정
    • 클라이언트 프로그램 → UDPClient.py
    • 서버 프로그램 → UDPServer.py
    • 서버 포트 번호는 12000으로 가정
    • 주요 쟁점을 강조하기 위해 최소한의 코드만을 제공
      • 오류 같은 것들은 배제

UDPClient.py

from socket import *
serverName = 'hostname'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
message = input('Input lowercase sentence:')
clientSocket.sendto(message.encode(),(serverName,serverPort))
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print(modifiedMessage.decode())
clientSocket.close()
from socket import *
  • socket 모듈을 가져옴 → 프로그램 내에 소켓 생성 가능
serverName = 'hostname'
serverPort = 12000 # 서버 포트 할당

 

  • 호스트 이름은 서버의 IP 주소 혹은 서브의 호스트 이름을 포함하는 문자열을 제공해야 함
  • 호스트 이름을 사용하는 경우 IP 주소를 얻기 위해 DNS 검색이 자동으로 수행
clientSocket = socket (AF_INET, SOCK_DGRAM)
  • 클라이언트 소켓 할당 <- 클라이언트의 포트 번호를 명시 X
    • 운영체제가 클라이언트 포트 번호 명시 작업을 대신해 줌

  • AF_INET : 하부 네트워크가 IPv4를 사용(첫 번째 파라미터 : 주소군(family))
  • SOCK_DGRAM : UDP 소켓임을 의미
# 메시지 생성
message = input ('Input lowercase sentence:')
  • 사용자는 키보드를 이용하여 변수 message에 할당되는 라인을 입력
# 메시지를 소켓을 통해 목적지 호스트로 보냄
clientSocket.sendto(message.encode(),(serverName,serverPort))
  • encode() : 문자열 타입의 메시지를 바이트 타입으로 변환
  • sendto() :
    • 목적지 주소(serverName, serverPort)를 메시지에 붙이고,
      패킷을 프로세스의 클라이언트 소켓에 전송

  • 클라이언트는 패킷을 보낸 후, 서버로부터의 데이터 수신을 기다린다.
modifiedMessage, serverAddress = clientSocket.recvfrom (2048)
  • 인터넷에서 클라이언트 소켓으로 패킷이 반환되면 패킷 데이터는 modifiedMessage에 할당
  • 동시에 패킷의 출발지 주소는 serverAddress에 할당
    • serverAddress는 (서버 IP 주소와 서버 포트 번호 포함)

  • recvfrom 메서드 또한 2048을 버퍼 크기로 받아들임
print(modifiedMessage.decode())
clientSocket.close() # 소켓을 닫는다. -> 프로세스가 종료됨
  • 메시지를 바이트에서 문자열로 변환 후 사용자 화면에 modifiedMessage 출력

UDPServer.py

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
print("The server is ready to receive")
while True:
        message, clientAddress = serverSocket.recvfrom(2048)
        modifiedmessage = message.decode().upper()
        serverSocket.sendto(modifiedMessage.encode(),clientAddress)
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
  • UDPClient.py 과정과 동일
  • 소켓 모듈 사용, 12000 할당, SOCK_DGRAM 타입 소켓 생성

serverSocket.bind(('', serverPort))
print("The server is ready to receive")
  • bind : 포트 번호 12000을 서버의 소켓에 할당한다.
    → UDPServer의 코드는 명시적으로 포트 번호를 소켓에 할당한다.

while True:
  • 이제 서버 IP 주소의 12000 포트로 패킷을 보내면 해당 소켓으로 패킷이 전달된다.(directed)
  • UDPServer는 계속 패킷을 수신하고 처리할 수 있도록 while 루프로 들어감
    → while 루프를 통해 UDPServer는 패킷이 도착하기를 기다린다.

        message, clientAddress = serverSocket.recvfrom(2048)
  • 패킷이 서버에 도착하면 패킷의 데이터는 message에 할당
  • 패킷의 출발지 주소는 clientAddress에 할당
    • clientAddress는 클라이언트 IP 주소와 클라이언트 포트 번호를 포함
    • 반송 주소를 제공해야 할 때 이 주소 정보를 사용한다.
    • 출발지 주소 정보를 통해 서버는 응답을 어디로 보내야 할지 알 수 있게 됨

        modifiedmessage = message.decode().upper()
  • 클라이언트로부터의 라인을 받아서 upper()을 이용하여 대문자로 변환
        serverSocket.sendto(modifiedMessage.encode(),clientAddress)
  • 클라이언트 주소(IP주소와 포트 번호)를 대문자로 변환된 메시지에 붙이고,
    그 결과로 만들어진 패킷을 서버의 소켓에 보낸다.
  • 그 뒤 인터넷 패킷을 클라이언트 주소로 전달한다.
  • 서버는 패킷을 보낸 후 while 루프에 머물며 다른 UDP 패킷이 도착하기를 기다린다.