[CS/ Network] 컴퓨터 네트워킹 하향식 접근 8판 2장 애플리케이션 계층 / 2.7 소켓 프로그래밍: 네트워크 애플리케이션 생성

yujeongkwon·2023년 8월 1일
0

CS / Network

목록 보기
10/27

목차
2.7.1 UDP를 이용한 소켓프로그래밍 141
2.7.2 TCP 소켓프로그래밍 147


💻 2.7 소켓 프로그래밍: 네트워크 애플리케이션 생성


  • 네트워크 애플리케이션은 2개의 종단 시스템에 존재하는 클라이언트 + 서버 프로그램으로 구성
    • 프로그램 수행 -> 클라이언트와 서버 프로세스 생성 -> 서로 소켓으로부터 read & write
      => 통신
  • 네트워크 애플리케이션 생성 = 클라이언트와 서버 프로그램 각각 코드를 작성
  • 클라이언트-서버 애플리케이션의 2가지 형태
    • 표준(공개) 프로토콜 사용
      • RFC에 정의된 규칙을 따름 (개방형(open))
      • 동작을 규정하는 규칙들이 공개되어져 있음.
        ex) HTTP 프로토콜의 클라이언트,서버 측 구현
      • 오늘날 대부분은 독립 개발자가 개발한 클라이언트라 서버 프로그램 간의 통신을 포함
        ex) 아파치 웹 서버와 통신하는 구글 크롬 브라우저, 비트토렌트 트래커와 통신하는 비트토렌트 클라이언트 등
    • 개인의 독점적 프로토콜 사용
      • 개발자는 클라이언트-서버 프로그램을 모두 생성 + 코드에 무엇을 사용할지를 완전히 제어
      • 위 코드는 공개된 프로토콜로 구현X -> 다른 것들은 이 애플리케이션과 상호작용X
  • 개발자의 우선 결정 요소 : 애플리케이션의 TCP 또는 UDP 사용여부
    • TCP
      • 연결지향형 서비스
      • 신뢰적 바이트 스트림 채널을 제공
      • 위 채널을 통해 통신
    • UDP
      • 비연결형
      • 한 종단 시스템에서 다른 곳으로 데이터를 독립적인 패킷 생성한 후에 전송
      • 전송 보장X
    • 클라이언트 또는 서버 프로그램이 RFC에 정의된 프로토콜을 구현할 때,그 프로토콜과 연관된 잘 알려진 포트 번호를 사용해야 함
    • 역으로,독점적인 애플리케이션을 개발할 때 개발자는 잘 알려진 포트 번호를 사용하지 않도록 유의

UDP와TCP 소켓 프로그래밍

다음과 같은 간단한 클라이언트-서버 애플리케이션을 이용한다.
1. 클라이언트는 키보드로부터 한 줄의 문자(데이터)를 읽고 그 데이터를 서버로 보낸다.
2. 서버는 그 데이터를 수신하고 문자를 대문자로 변환한다.
3. 서버는 수정된 데이터를 클라이언트에게 보낸다.
4. 클라이언트는 수정된 데이터를 수신하고 그 줄을 화면에 나타낸다.


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


  • 패킷 전달 과정
    • 송신자의 소켓 통과 -> 인터넷은 목적지 주소의 수신 프로세스의 소켓으로 라우트 -> 수신 소켓 도착 -> 수신 프로세스는 소켓을 통해 그 패킷을 추출 -> 패킷의 콘텐츠를 조사 + 동작
  • 패킷에 붙여지는 목적지 주소
    • 목적지 호스트의 IP 주소 : 인터넷의 라우터는 목적지 호스트로 패킷 라우트 그러나 호스트
    • 포트 번호(port number): 하나 이상의 소켓을 갖는 많은 네트워크 애플리케이션 프로세스를 수행하는 목적지 호스트 내의 특정한 소켓 식별자
    • (+) 송신자의 출발지 주소(출발지 호스트의 IP 주소, 출발지 소켓의 포트 번호)
      • UDP 에서는 일반적으로 X 대신에 하부 운영체제가 자동 실행

UDP

그림 2.27 UDP를 이용한 클라이언트-서버 애플리케이션

  • 각 프로그램 뒤에는 상세한 라인별 분석을 제공
  • 서버는 클라이언트의 메시지를 수신하고 응답함 수 있도록,서버는 로세스로 수행되어 있어야함.
  • 클라이언트 프로그램 : UDPClient.py
  • 서버 프로그램 : UDPServer.py
  • 아래는 최소한 코드 제공 (에러 핸들링X)
  • 서버 포트 번호로 12000을 임의로 선택

UDPClient.py

'''
socket 모듈은 파이썬에서 모든 네트워크 통신의 기본을 구성 -> 프로그램 내에 소켓을 생성O
'''
from socket import *

'''
문자열 'hostname’을 변수 serverName에 할당
이때 서버의 IP 주소(ex 128.138.32.126) or 서버의 호스트 이름(ex cis.poly.edu)을 포함하는 문자열 제공 
호스트 이름을 사용하는 경우 IP 주소를 얻기 위해 DNS 검색이 자동으로 수행 됨.
'''
serverName = 'hostname'
''' 정수 변수 serverPort에 1200을 할당 '''
serverPort = 12000

'''
clientSocket이라는 클라이언트의 소켓을 생성
socket(주소군, 소켓 타입)
AF_INET : 하부 네트워크가 IPv4를 사용하고 있음
SOCK_DGRAM : UDP 소켓
UDP 소켓을 생성할 때 클라이언트 소켓의 포트 번호를 명시X -> 운영체제가 이 작업을 대신 수행
'''
clientSocket = socket(AF_INET, SOCK_DGRAM)

'''메시지 생성 : message에 할당되는 값 클라이언트에게 입력받기 '''
message = input ('Input lowercase sentence:')

'''
encode() : 소켓으로 바이트 형태를 보내기 위해 문자열 타입의 메시지를 바이트 타입으로 변환
sendto() : 목적지 주소를 메시지에 붙이고 그 패킷을 프로세스의 소켓인 clientsocket으로 전송
'''
clientSocket.sendto(message.encode(),(serverName,serverPort))

'''
데이터 수신 기다림
'''

'''
변수 modifiedMessage : 패킷이 클라이언트의 소켓에 인터넷으로부터 도착하면 패킷 데이터가 할당됨
변수 serverAddress : 패킷의 출발지 주소가 할당됨. (서버의 IP 주소와 서버의 포트 번호 포함) 
ㄴ 굳이 필요없음 UDP에서는 출발지 주소를 이미 알고 있기 떄문 그냥 굳이굳이 넣었음 ^^
recvfrom(버퍼크기) : 받아들일(2048) 버퍼 크기
'''
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)

'''
decode() : 메시지를 바이트에서 문자열로 변환
pirnt() : 사용자의 화면에 modified Message를 출력
이때 해당 애플리케이션 목적에 맞게 소문자였던 클라이언트의 입력이 대문자로 전환되어야 한다. 
'''
print (modifiedMessage.decode())

''' 소켓 닫기 '''
clientSocket.close()

UDPServer.py

''' 소켓 모둘 사용'''
from socket import *
''' serverPort에 12000 할당'''
serverPort = 12000
''' (UDP 소켓) SOCK_DGRAM 타입의 소켓 생성'''
serverSocket = socket(AF_INET, SOCK_DGRAM)

'''
bind() : 포트 번호 12000을 서버의 소켓에 할당
서버IP 주소의 I2000 포트로 패킷을 보내면 해당 소켓으로 패킷이 전달됨(directed)
'''
serverSocket.bind(('', serverPort))
print ("The server is ready to receive"'''
while루프 : 클라이언트로부터 계속해서 패킷을 수신하고 처리할 수 있도록 함. 패킷 도착 기다림.
'''
while True: # 패킷이 서버의 소켓에 도착하면
    '''
   변수 message : 패킷의 데이터 할당
   변수 clientAddress : 패킷의 출발지 주소 할당, 클라이 언트의 IP 주소와 포트 번호 포함
   ㄴ 일반 우편에서의 반송 주소와 마찬가지로 반송 주소를 제공해야 할 때 사용
    '''
	message, clientAddress = serverSocket.recvfrom(2048)
    
    '''애플리케이션의 주요 부분 : 클라이언트의 입력을 upper()을 사용하여 대문자로 변환'''
	modifiedMessage = message.decode().upper ()
    
    '''
    encode() : 문자열을 바이트로 변환
    sendto() : 패킷(클라이언트의 주소 + 대문자로 변환된 메시지)을 서버의 소켓으로 전송
               (서버 주소 붙이기 = 자동수행)
    그 뒤 인터넷은 패킷을 클라이언트 주소로 전달한다. 
    '''
	serverSocket.sendto(modifiedMessage.encode(), clientAddress)

프로그램 테스트

  1. 한 호스트에서 UDPClient.py를,또 다른 호스트에서 UDPServer.py 수행
  2. UDPClient.py : 적절한 서버의 호스트 이름 혹은 IP 주소가 포함시키기
    UDPServer.py : 컴파일 + 서버 호스트에서 수행 -> 클라이언트의 접촉있을 때 까지 idle 상태
    = 서버 프로세스 생성
  3. UDPClient.py : 컴파일 + 클라이언트에서 수행 = 클라이언트 프로세스가 생성
  4. 마지막으로 클라이언트 애플리케이션을 사용(문장을 입력 후에 리턴(return) 키)

📧 2.7.2 TCP 소켓 프로그래밍


  • 연결지향 프로토콜 : 클라이언트와 서버가 통신 전, 먼저 TCP 연결을 설정
    • UDP 가장 큰차이, UDP : 서버가 패킷을 소켓에 제공하기 전에 패킷에 목적지 주소를 붙임
    • TCP 연결 : 클라이언트, 서버 각 소켓에 연결
    • TCP 연결 생성 : 클라이언트와 서버의 각 소켓 주소(IP 주소와 포트 번호)를 연결과 연관시킴.
    • TCP 연결이 설정된 후에 통신 : 소켓을 통해 데이터를 tcp 연결로 전송
  • 신뢰적(reliable) 서비스를 제공
    • 애플리케이션 관점에서 클라이언트의 소켓과 서버의 연결 소켓은 파이프에 의해 직접 연결
    • 위 그림처럼,클라이언트, 서버 프로세스는 서로 자신의 소켓으로 임의의 바이트를 보낼 수 있고, 순서 보장 + 수신 보장


TCP

동작 순서

  1. 클라이언트는 서버로의 접속을 먼저 시도한다.
  2. 서버는 클라이언트의 초기 접속에 응대할 수 있도록 준비
      1. 클라이언트가 접속을 시도하기 전에 프로세스를 먼저 수행
      1. 임의의 클라이언트로부터의 초기 접속을 처리하는 특별한(welcome) 소켓을 가져야 한다.
      • ex) serverSocket : TCP 소켓 객체, welcome 소켓
      • welcome 소켓 : 서버와 통신하고자 하는 모든 클라이언트가 초기에 접속하는 곳
  3. 클라이언트 프로세스는 서버로의 TCP 연결을 시도한다.
    • 클라이언트 프로그램에서 TCP 소켓을 생성
      • 서버에 있는 welcome 소켓의 주소(IP 주소, 소켓의 포트 번호) 명시
    • 세 방향 핸드세이크를 하고 서버와 TCP 연결을 설정한다. (초기접속)
      • ㄴ트렌스포트 계층에서 작동 -> 클라이언트와 서버 프로그램은 인식 못함.
  4. 서버는 해당 클라이언트에게 지정되는 새로운 소켓을 생성
    • ex) connectionSocket : 연결시도하는 클라이언트에게 지정된 새로 생성된 소켓, 연결소켓
    • 연결 소켓 : 각 클라이언트와 통신을 위해 차후에 서버 쪽에 새롭게 생성되는 소켓

그림 229 TCP를 이용한 클라이언트一서버 애플리케이션
그림 229 TCP를 이용한 클라이언트一서버 애플리케이션


TCPClient.py

from socket import *
serverName = ’servername'
serverPort = 12000

'''
socket(하위네트워크 프로토콜 타입, 소켓 타입) : 클라이언트 소켓 생성 
+) AF_INET = IPv4 사용 , SOCK_STREAM = TCP 소켓 타입 
클라이언트의 소켓을 생성 : 소켓의 포트 번호를 명시 X -> 대신에 운영체제가
'''
clientSocket = socket (AF_INET, SOCK_STREAM)

'''
connect(연결의 서버 쪽 주소) : 통신 전 TCP연결 설정
해당 명령어 이후 세 방향 핸드셰이크가 수행 -> 클라이언트와 서버 간에 TCP 연결이 설정
'''
clientSocket.connect ((serverName, serverPort))

'''
사용자에게 문자열 입력 받기(라인이 리턴 키로 끝날때 까지 받아옴)
'''
sentence = input ('Input lowercase sentence:' )

'''
send(메세지) : 메세지를 클라이언트 소켓을 통해 TCP 연결로 전송. 
패킷 명시적으로 생성X 목적지 주소X -> 대신 단순히 메세지 바이트(sentence.encode())를 TCP연결에게 제공
-> 내 생각 udp와 달리 tcp는 연결이 이루어져 있는 상태이니까 그런듯 ㅇㅇ
'''
clientSocket.send (sentence.encode())

'''
서버로부터 바이트를 수신 대기
'''

'''문자열 modifiedSentence : 서버로부터 온 문자를 모아둠(라인이 리턴 키로 끝날때까지)'''
modifiedSentence = clientSocket.recv(1024)
'''대문자로 변환된 문장을 출력'''
print('From Server: ', modifiedSentence.decode())
'''클라이언트 소켓을 닫기 + TCP 연결 닫기'''
clientSocket.close()

TCPServer.py

from socket import *

serverPort = 12000
'''서버 포트 번호 serverPort를 소켓과 연관'''
serverSocket = socket(AF_INET, SOCK_STREAM)

'''serverSocket : 대기 소켓, 출입문을 설정한 후에 임의의 클라이언트가 초기 접속할 때까지 기다림
-> 내생각 이친구가 welcome 소켓 ㅇㅇ''''
serverSocket.bind((, serverPort))

''' listen(큐잉되는 연결의 최대 수) : 서버가 클라이언트로부터의 TCP 연결 요청 듣기 '''
serverSocket.listen(1)
print('The server is ready to receive')


while True:
	'''
    클라이언트가 초기 접속한 후..
    소켓 생성 - accept() : serverSocket을 위해 해당 클라이언트에게 지정된 새로운 소켓 생성
    핸드셰이킹 - 위 명령어 수행된 후에 TCP 연결 생성 (클라이언트의 clientsocket-서버의 connectionsocket)
    '''
	connectionsocket, addr = serverSocket.accept ()
    
    ''' 클라이언트의 메세지를 받고, 클라이언트에게 수정된 문장을 보냄''' 
	sentence = connectionSocket.recv(1024) .decode ()
	capitalizedSentence = sentence.upper ()
	capitalizedSentence. send (capitalizedSentence.encode())
    '''
    연결 소켓을 닫음
    그러나 serverSocket이 열려 있음 -> 다른 클라이언트가 초기접속 + 메세지 보내기 가능
    '''
	capitalizedSentence.close()
profile
인생 살자.

0개의 댓글

관련 채용 정보