클라이언트-서버 애플리케이션 개발
- 애플리케이션에
TCP
,UDP
중 어떤 것을 사용할지 결정해야 함TCP
: 연결지향적 서비스- 신뢰적 바이트 스트림 채널 제공
- 해당 채널을 통해 데이터가 두 종단 시스템 사이를 흐름
- 해당 채널을 통해 데이터가 두 종단 시스템 사이를 흐름
- 신뢰적 바이트 스트림 채널 제공
UDP
: 비연결형 서비스- 한 종단 시스템에서 다른 곳으로 데이터를 독립적인 패킷으로 만들어 전송
- 전송에 대한 보장을 하지 않는다.
RFC 표준 프로토콜 vs 개인 독점 프로토콜
- RFC 표준 프로토콜
- 오늘날 대부분의 네트워크 애플리케이션
- 독립 개발자가 개발한 클라이언트와 서버 프로그램 간의 통신을 포함
- RFC에 정의된 프로토콜을 구현할 때,
해당 프로토콜과 잘 알려진 포트 번호를 사용
- 개인 독점 프로토콜
- 공개된 프로토콜을 구현하지 않음
→ 다른 독립 개발자는 이 애플리케이션과 상호작용하는 코드 개발 불가능 - 애플리케이션을 개발할 때 잘 알려진 포트 번호를 사용하지 않도록 해야 함
- 공개된 프로토콜을 구현하지 않음
TCP 소켓 프로그래밍
- 클라이언트와 서버가 데이터를 보내기 전에 먼저 TCP 연결 설정 필요
- TCP 연결의 한쪽은 클라이언트 소켓, 다른 쪽은 서버 소켓과 연결
- TCP 연결의 한쪽은 클라이언트 소켓, 다른 쪽은 서버 소켓과 연결
- TCP를 생성할 때 클라이언트 소켓 주소와 서버 소켓 주소를 연결과 연관시킨다.
- 소켓 주소 = IP 주소 + 포트 번호
- 데이터를 보낼 때 연결된 곳으로 보냄 → UDP 와의 차이점
- UDP는 서버가 패킷을 소켓에 제공하기 전에 패킷에 목적지 주소를 붙여야 함
TCP 클라이언트 서버 상호작용
- 먼저 클라이언트는 서버로의 접속을 시도한다.
→ 서버는 클라이언트의 초기 접속에 응대할 수 있도록 준비해야 함
- 여기에는 2가지 의미가 존재
- TCP 서버는 클라이언트가 접속하기 전에 프로세스를 먼저 수행하고 있어야 함
- 클라이언트로부터의 초기 접속을 처리하는 특별한 출입문(소켓)을 가져야 한다.
- 여기에는 2가지 의미가 존재
- 접속에 대한 시도로 클라이언트 프로그램에서 TCP 소켓을 생성한다.
- TCP 소켓을 생성할 때, 서버에 있는
환영 주소
를 명시한다.환영(welcome) 주소
: 서버의 IP 주소와 소켓의 포트 번호
- TCP 소켓을 생성할 때, 서버에 있는
- 소켓을 생성한 후, 클라이언트는 세 방향 핸드셰이크를 하고 서버와 TCP 연결을 설정
- 세 방향 핸드셰이크는 트랜스포트 계층에서 일어남
→ 클라이언트와 서버 프로그램은 전혀 인식하지 못함
- 세 방향 핸드셰이크는 트랜스포트 계층에서 일어남
- 세 방향 핸드셰이크 동안, 클라이언트 프로세스는 서버 프로세스의 출입문을 두드림
- 서버가 노크를 들으면, 서버는 해당 클라이언트에게 지정된 새로운 소켓을 생성
- 연결을 시도하는 클라이언트에게 새로 지정된 소켓 →
connectionSocket
- 연결을 시도하는 클라이언트에게 새로 지정된 소켓 →
클라이언트 소켓과 서버의 연결
- 애플리케이션 관점에서, 클라이언트 소켓과 서버 연결 소켓은 파이프에 의해 직접 연결됨
- 클라이언트 프로세스는 자신의 소켓으로 임의의 바이트를 보낼 수 있고,
보낸 순서대로 서버 프로세스가 바이트를 수신하도록 TCP가 보장한다.
- 이 과정은 연결 소켓을 통해 일어난다.
- → 따라서 TCP는 클라이언트와 서버 프로세스 간에 신뢰적 서비스를 제공한다.
- 또한, 클라이언트 프로세스는 소켓으로부터 바이트를 수신할 수 있다.
- 서버 프로세스도 소켓으로 바이트를 보낼 수도 있다.
TCP 프로그램 예시
TCP 전송 서비스 통신에서 클라이언트-서버 주요 소켓 동작
프로그램 코드
TCPClient.py
- 애플리케이션 클라이언트 쪽 코드
from socket import *
serverName = 'servername'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName,serverPort))
sentence = input('Input lowercase sentence:')
clientSocket.send(sentence.encode())
modifiedSentence = clientSocket.recv(2048)
clientSocket.close()
from socket import *
- socket 모듈을 가져옴 → 프로그램 내에 소켓 생성 가능
serverName = 'servername'
serverPort = 12000 # 서버 포트 할당
- 호스트 이름은 서버의 IP 주소 혹은 서버 이름을 포함하는 문자열을 제공해야 함
- 호스트 이름을 사용하는 경우 IP 주소를 얻기 위해 DNS 검색이 자동으로 수행
# 클라이언트 소켓 생성
clientSocket = socket(AF_INET, SOCK_STREAM)
AF_INET
: 하위 네트워크가 IPv4를 사용함SOCK_STREAM
: TCP 소켓임을 의미- 클라이언트 소켓을 생성할 때 해당 소켓의 포트 번호를 명시하지 않는다.
- 운영체제가 하도록 내버려 둔다.
- 운영체제가 하도록 내버려 둔다.
# 클라이언트와 서버 간에 TCP 연결 시작
clientSocket.connect((serverName,serverPort))
- 클라이언트가 TCP 소켓을 이용하여 서버로 데이터를 보내기 전에 TCP 연결 설정 필요
(serverName, serverPort)
→ 연결의 서버 쪽 주소- 이후 세 방향 핸드셰이크가 수행되고, 클라이언트와 서버 간에 TCP 연결이 설정됨
# 사용자로부터 문장을 획득
sentence = input('Input lowercase sentence:')
- 사용자가 리턴 키를 입력하여 문장을 마칠 때까지 문자를 계속해서 sentence에 모은다.
# 문자열 sentence를 클라이언트 소켓을 통해 TCP 연결로 보냄
clientSocket.send(sentence.encode())
- 클라이언트 프로그램은 단순히 sentence에 있는 바이트를 TCP 연결에게 제공
- cf ) UDP - 패킷을 생성하고 패킷에 목적지 주소를 붙인다.
→ TCP 방식에서는 전송할 때 패킷을 생성하지 않는다.
- cf ) UDP - 패킷을 생성하고 패킷에 목적지 주소를 붙인다.
- 이후, 클라이언트는 서버로부터 바이트를 수신하기를 기다린다.
# 서버로부터 온 문자를 문자열에 모은다.
modifiedSentence = clientSocket.recv(2048)
- 라인이 리턴 키로 끝날 때까지 modifiedSentence에 문자가 계속해서 쌓임
# 소켓을 닫고 클라이언트와 서버 간의 TCP 연결을 닫는다.
clientSocket.close()
- 이는 클라이언트 TCP가 서버 TCP에게 TCP 메시지를 보내게 한다.
TCPServer.py
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
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()
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
- TCPClient.py 와 같음 → 12000번 포트를 가진 TCP 소켓을 만든다.
serverSocket.bind(('', serverPort))
- 서버 포트 번호 serverPort를 소켓과 연관시킨다.
- TCP에서는 serverSocket(welcomeSocket)이 대기하는 소켓이 된다.
→ 출입문을 설정한 후, 임의의 클라이언트가 이 문을 두드리기를 기다린다.
- TCP에서는 serverSocket(welcomeSocket)이 대기하는 소켓이 된다.
serverSocket.listen(1)
- 서버가 클라이언트로부터 TCP 연결 요청을 듣도록 한다.
- 파라미터는 큐잉 연결의 최대 수(최소 1)
# 요청한 클라이언트에게 새로운 지정된 소켓을 서버에 생성
connectionSocket, addr = serverSocket.accept()
- 클라이언트가 문을 두드리면 프로그램은 serverSocket을 위한
accept()
메서드 실행
→ 클라이언트에게 지정된 connectionSocket이라는 새로운 소켓 서버 생성 - 이후, 클라이언트와 서버는 핸드셰이킹을 완료
→ 클라이언트 소켓과 서버 소켓 간의 TCP 연결 생성
- clientSocket ↔ connectionSocket 간의 TCP 연결
- clientSocket ↔ connectionSocket 간의 TCP 연결
- TCP 연결이 설정되었으므로, 클라이언트와 서버는 서로에게 바이트를 보낼 수 있다.
# 모든 작업 이후, 연결 소켓을 닫는다.
connectionSocket.close()
- 연결 소켓을 닫아도, serverSocket이 열려있기 때문에 다른 클라이언트가 두드릴 수 있음