본문 바로가기

네트워크/chapter 2. 애플리케이션 계층

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

2.7.0 개요

일반적인 네트워크 애플리케이션은,

클라이언트 프로그램 - 서버 프로그램으로 구성된다

 

프로그램 수행하면,

클라이언트 프로세스 - 서버 프로세스 생성된다.

 

두 프로세스 간 통신은, 두 프로세스가 "소켓으로부터 읽고, 쓰는 것"을 통해 서로 통신한다.

따라서, 개발자의 임무는 클라이언트와 서버 프로그램 모두에 대해 코드를 작성하는 것이다.

 

개발자는, 클라이언트-서버 애플리케이션을 구현하는 코드를 작성하게 된다. 이때 할 일은, 다음과 같다.

 

1. 해당 애플리케이션이 TCP, UDP 중 무엇을 이용할 지를 결정하는 것이다.

- 따라서, TCP, UDP 두가지로 애플리케이션을 구현할 수 있다.

 

2. 프로토콜을 구현할 때, 해당 프로토콜과 연관된 잘 알려진 포트 번호를 사용해야한다.

- 역으로, 독점적인 애플리케이션을 개발할 땐 잘 알려진 포트를 사용하면 안된다

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

두 프로세스 간 통신은, 두 프로세스가 "소켓으로부터 읽고, 쓰는 것"을 통해 서로 통신한다.

각 프로세스 = 집

프로세스의 소켓 = 문


송신 프로세스

- 데이터 패킷을 소켓 문 밖으로 내보내야 한다

- 이전에, 패킷에 "목적지 주소"를 넣어야 함

 

목적지 주소란?

1. 목적지 호스트의 IP 주소

2.소켓의 포트 번호(목적지 호스트 내의 소켓)

 

수신 프로세스

- 서버 프로세스는 메세지를 수신하고 응답할 수 있기 위해, 미리 준비되어야 한다.

- 즉, 클라이언트가 메세지 전송하기 전에 프로세스가 수행되고 있어야 한다는 뜻이다.


UDPClient.py

# 파이썬의 모든 네트워크 통신의 기본을 구성함
# 이를 통해, 소켓을 생성할 수 있다
from socket import *

servername = 'localhost'
serverport = 12000

# 클라이언트 소켓 생성
# - 첫번째 파라미터 : 주소군. (AF_INET : IPv4 이용)
# - 두번째 파라미터 : 소켓의 종류 식별자(UDP or TCP)
# - 소켓 생성 시, 클라이언트 소켓 번호 명시하지 않아도 된다. 운영체제가 알아서 할당해줌
clientSocket = socket(AF_INET, SOCK_DGRAM)

# 클라이언트 측에서 프롬포트를 통해 메세지 입력받음
message = input('input lowercase sentence:')

# 클라이언트 소켓으로 메세지 보내기
# - encode : 메세지를 바이트로 바꾼다. 소켓에 바이트 형태로 보내야 하기 때문
# - (servername, serverport) : 목적지 IP, 목적지 소켓 PORT가 메세지에 붙는다
# - 패킷을 보낸 후, "클라이언트는 서버로부터 데이터 수신을 기다린다"
clientSocket.sendto(message.encode(), (servername, serverport))

# modifiedMessage : 서버로부터 받은 패킷 데이터
# serverAddress : 서버의 IP 주소, 포트번호
# (2048) : 버퍼 크기
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)

# 바이트 -> 문자열 변환 후, 출력
print(modifiedMessage.decode())

clientSocket.close()

UDPServer.py

from socket import *


serverPort = 12000

# 서버 소켓 생성 (IPv4, UDP 소켓)
serverSocket = socket(AF_INET,SOCK_DGRAM)

# 서버 소켓에 포트번호 할당
serverSocket.bind(('',serverPort))

print("The server is ready to receive")

# while문을 통해, 서버는 클라이언트로부터 메세지를 수신할 수 있도록 기다린다
while True:
	
    # 클라이언트로부터 받은 메세지, 클라이언트의 IP 주소 & 포트 번호
    # 주소와 포트번호는 반송 시 사용
    message, clientAddress = serverSocket.recvfrom(2048)
    
    # 받은 메세지 디코딩 후, 대문자로 바꾸기 => "서버가 실행하는 작업"
    modifiedMessage = message.decode().upper()
    
    # 받은 메세지 인코딩 후, 클라이언트 소켓으로 보내기
    serverSocket.sendto(modifiedMessage.encode(),clientAddress)

UDP 통신 그림

 

2.7.2 TCP 소켓 프로그래밍

연결 지향 프로토콜

= 서로가 서로에게 데이터를 보내기 전, TCP 연결을 수행해야 한다.

 

TCP 연결

= 한 쪽은 클라이언트 소켓 / 다른 한 쪽은 서버 소켓

= 클라이언트 소켓 주소(IP 주소 & 포트번호) <-> 서버 소켓 주소(IP 주소 & 포트번호) 간 연관시키기

 (TCP 연결 후, 소켓을 통해 데이터를 보내면 된다)

 

[클라이언트 프로세스가 서버로의 TCP 연결 수행하는 것의 의미]

1. 서버 프로세스는 수행 중이다

2. 클라이언트 프로세스가 TCP 소켓을 생성한다

 2-1. TCP 소켓 생성 시, 서버의 웰컴 소켓의 주소를 명시해야 한다

3. 소켓 생성 후, 3-way handshake 하고 TCP 연결 설정

 

4. 3-way handshake 동안, 서버에서 새로운 출입문 생성. 즉, 해당 클라이언트에게 지정되는 새로운 소켓(connectionSocket)을 생성한다


TCPClient.py

from socket import *

serverName = 'localhost'
serverPort = 12000

# 클라이언트 소켓 생성
# - 첫번째 파라미터 : 주소군(AF_INET : IPv4)
# - 두번째 파라미터 : 소켓 종류 지정(UDP or TCP 소켓)
clientSocket = socket(AF_INET,SOCK_STREAM)

# 서버의 웰컴소켓에 tcp연결 '시작'하기 (tcp 연결은 연결소켓과!!)
# 이 부분이 UDP 소켓 통신과 다름
clientSocket.connect((serverName, serverPort)) 

sentence = input('input lowercase sentence : ')


# 클라이언트 소켓으로 문자열 보내기
# - 프로그램이 패킷을 명시적으로 생성하지 않는다
# - 패킷에 목적지 주소(IP & 포트번호)를 붙이지 않는다
# - 단순히, "sentence의 바이트를 TCP 연결에 제공하는 것"일 뿐이다!!!

# 이 부분이 UDP 소켓 통신과 다름
clientSocket.send(sentence.encode())
 
# 서버로부터 바이트 수신 기다림
modifiedSentence = clientSocket.recv(1024)

print('From server: ', modifiedSentence.decode())

# 클라이언트 소켓 닫음 -> 서버로 TCP 메세지 보내도록 한다
clientSocket.close()

TCPServer.py

from socket import *

serverPort = 12000
# TCP 소켓 만들기
serverSocket = socket(AF_INET,SOCK_STREAM) 

# TCP 소켓에 포트번호 바인딩
serverSocket.bind(('',serverPort)) 

# 해당 TCP 소켓은 대기하는 소켓이 된다(연결 소켓이 아님. 즉, '웰컴 소켓'이라는 뜻)
serverSocket.listen(1)

print('The server is ready to receive')

while True:
	# 월컴소켓으로 연결을 받고, 연결소켓 생성
    connectionSocket, addr = serverSocket.accept() 

    sentence = connectionSocket.recv(1024).decode() # 메세지 받기
    capitalizedSentence = sentence.upper() # 작업

    connectionSocket.send(capitalizedSentence.encode()) # 메세지 전달

    connectionSocket.close()

 

 


TCP 통신 그림