Hyun's Wonderwall

[정보통신공학] Chap 2.(2) HTTP, SMTP, Socket Programming 본문

Subjects/정보통신공학

[정보통신공학] Chap 2.(2) HTTP, SMTP, Socket Programming

Hyun_! 2024. 4. 19. 14:56

~ week 3-1, 3-2 중반까지 ~

 

Web and HTTP

웹 페이지는 objects로 이루어져있다. 객체는 HTML 파일, 이미지 등이 될 수 있다.
웹 페이지는 several referenced objects??, each addressable by a URL를 포함하는 base HTML-file로 구성되어있다.
host name(호스트 이름. 주소) / path name(계층적 구조)

 

HTTP overview

HTTP: Hypertext Transfer Protocol. 웹의 5계층 프로토콜. client-server model

  • client: 브라우저. HTTP 요청 보내며, 받고 웹 객체를 보여줌.
  • server: 웹 서버. HTTP 프로토콜을 사용해 객체를 응답으로 보냄.

HTTP의 특징

1. HTTP는 TCP를 사용한다.

클라이언트가 TCP 연결을 시작(초기화)한다. (port:80)

서버가 TCP 연결을 수락한다.

HTTP 메세지들이 브라우저와 웹 서버 간에 교환된다.

TCP 연결이 종료된다.

2. HTTP는 stateless하다.

서버는 과거 클라이언트 요청에 대한 정보를 보관하지 않는다. (state를 유지하는 프로토콜은 불일치 문제가 발생할 수 있기 때문에 복잡함)

 

HTTP 메시지

  • 두 가지: request, response
  • HTTP 요청 메세지: ASCII (human-readable format)
  • general format:
    • request line: method sp URL sp version \r\n
    • header lines: header_field_name value \r\n 이 여러줄
      (end of header line): \r\n
    • body: entity body
  • HTTP request methods: POST, GET, HEAD(body없이 헤더만 보내달라고 하는것), PUT(파일을 아예 교체)

HTTP Response Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 400 Bad Request
  • 404 Not Found
  • 505 HTTP Version Not Supported

Maintaining user/server state: Cookies

  • HTTP는 stateless해서, 트랜잭션 간에 state를 유지하기 위해 클라이언트는 cookies를 사용한다.
  • 4가지 구성요소:
    1) cookie header line of HTTP 응답 메시지
    2) cookie header line of HTTP 요청 메시지
    3) cookie file kept on user's host, managed by user's browser
    4) back-end database at Web site

ex. 첫 HTTP 요청-> 사이트는 unique ID(쿠키)와 entry in backend database for ID를 만든다.

HTTP 요청은 cookie ID value를 포함할 것이고 사이트가 유저를 식별하게 된다.

 

client가 보내는 요청 헤더에 cookie가 없으면 server가 ID를 발급해서 response로 준다 (set-cookie:1678) backend database에 create entry.
이후로 client는 요청 헤더에 (cookie: 1678)로 쿠키 헤더를 담아서 보내고 cookie-specific action 후 backend database에 access한다

 

HTTP cookies: comments

  • 쿠키가 어디 쓰이나?
  • 어떻게 state를 유지할것인가
  • cookies and privacy

Web caches (proxy servers)

  • Web cache: cache에 있으면 응답. 없으면 client가 되어 origin server에 요청 후 cache로 save.
  • Web cachesms client 처럼도 server 처럼도 행동한다.
  • cache는 ISP에 의해 설치된다.
    왜 Web caching? 빨리 응답. 트래픽 감소시킴. -> 인터넷에 캐시 많음

Email

  • user agents. main servers.
  • protocol:SMTP, IMAP
    User Agent: 메일 읽는 사람.
    Mail Server:
  • mailbox: 받는 메일들 저장하는 곳
  • message queue: 보낼 메일들을 저장하는 큐
  • SMTP protocol: client-server.

Email: the RFC(5321)

  • TCP. port 25.
  • direct transfer
  • 전송 3단계: (1)handshaking (2)transfer of messages (3)closure
  • command/response interaction
    • commands: ASCII text
    • response: status code and phrase
  • 메시지들은 7bit ASCII여야 한다.

Scenario: Alice가 Bob에게 메시지를 보낸다

1) Alice가 자신의 메일 서버에 메시지 보냄
2) 메시지가 "message queue"에 보관됨
3) SMTP의 client 부분이 Bob의 메일 서버와 TCP 연결
4) SMTP client가 Alice의 메시지를 TCP 연결 너머로 보냄
5) Bob의 메일 서버가 메시지를 Bob의 "mailbox"에 보관함
6) Bob이 자신의 user agent로 메시지를 읽음

sample SMTP interaction

SMTP: closing observations

  • client 입장에서, HTTP는 pull이고(서버에서 정보 채워줌) SMTP는 push이다(보내는 쪽이 정보 채움)
  • HTTP는 각각의 오브젝트들이 encapsulated.
  • SMTP는 multiple objects가 multipart 메시지들로 보내진다.
  • SMTP는 persistent 연결을 사용한다.
  • SMTP는 header와 body 메시지를 7-bit ASCII를 요구한다.
  • Mail message format
  • Mail access protocols
    • 메일 받을 때는 IMAP (+ POP)

Socket Programming

client-server 애플리케이션이 socket을 통해 통신하도록 프로그래밍.
Socket: 애플리케이션 프로세스 사이의 door, end-end-transport protocol

두 Socket type: UDP, TCP

 

UDP Socket programming

UDP: client, server 연결 안맺음. (handshaking 안함.) 패킷에 항상 알려져있는 서버 ip, port를 달고 간다.

receiver는 sender IP address와 port번호를 받은 패킷에서 뽑아낸다.

UDP: 전송된 데이터는 순서 안맞거나 lost될 수 있다.

UDP는 unreliable하게 groups of bytes(datagrams)를 전송한다

 

client-server socket interaction: UDP

- client, server 둘 다 UDP를 위한 소켓을 만든다. (AF_INET, SOCK_DGRAM)

- client가 server 주소(ip, port)로 datagram을 clientSocket을 통해 보낸다.

- server가 serverSocket에서 datagram을 읽는다. 

- server가 serverSocket에다 client의 주소(ip, port)를 명시해 reply한다.

- client가 clientSocket에서 datagram을 읽는다.

- clientSocket을 종료한다.

그러나 서버 소켓은 종료하면 안 된다!! 항상 열고 기다려야 하기 때문에.

 

예시 앱: UDP client

보통은 server별로 clientSocket을 만들어서 사용한다.

# 1. 파이썬 소켓 라이브러리 임포트
from socket import *
# 2. server를 위한 UDP socket 생성
serverName = 'hostname'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DRGAM) # IPv4, UDP
# 3. 사용자의 키보드 입력을 받음
message = raw_input("Input lowercase sentence.")
# 4. 메시지를 서버로 전송 (인코딩 후)
clientSocket.sendto(message.encode(), (serverName,serverPort))
# 5. clientSocket에서 서버의 응답과 주소를 받는다
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
# - recvfrom()이 데이터가 도착할 때까지 blocking된다.
# - 최대 2048byte를 문자열로 읽는다.(개수 단위가 char)
# 6. 받은 문자열을 디코딩래 출력하고 clientSocket을 닫는다.
print ('From server:', modifiedMessage.decode())
clientSocket.close()

 

예시 앱: UDP server

# 1. 소켓 라이브러리 임포트
from socket import *
# 2. server 포트번호 지정
serverPort = 12000
# 3. server UDP 소켓 생성
serverSocket = socket(AF_INET, SOCK_DGRAM)
# 4. 소켓을 local 포트번호 12000에 바인드
serverSocket.bind(('', serverPort)) # 튜플이 인자 (ip주소, port번호)
print ("The server is ready to receive")
# 5. 무한 루프 시키지
while True:
    # 6. UDP 소켓에서 message를 읽고 client 주소(IP,Port) 받기
    message, clientAddress = serverSocket.recvfrom(2048)
    # 7. 디코딩 후 애플리케이션 기능 수행
    modifiedMessage = message.decode().upper()
    # 8. cleint에게 수정된 메시지 인코딩해 다시 보냄
    serverSocket.sendto(modifiedMessage.encode(), clientAddress)

주요 메서드

소켓 생성: xxSocket = socket(AF_INET, SOCK_DGRAM)

서버 소켓의 바인딩: serverSocket.bind(('', serverPort))

메시지 전송, 주소로: xxSocket.sendto(message, (ip, port))

메시지 받기, 주소와: message, address = xxSocket.recvfrom(2048)

소켓 종료: xxSocket.close()

 

TCP Socket Programming

 

Client side: 서버를 먼저 컨택해야 한다.

- Client 소켓을 만든 직후 client와 server 간에 TCP 연결을 만든다. (서버 ip, port번호 필요)

Server side: 서버는 항상 실행중이며 welcome socket을 열어두고 있다.

- welcome socket에서 클라이언트에게 연락 받은후 그 클라이언트만을 위한 socket을 만든다.

- server가 multiple clients와 대화할 수 있다. client들을 구분하기 위해 source port번호가 사용된다.

 

client-server socket interaction: TCP 

- server가 hostid, port=x에서 돌아가고 있다. 들어올 요청을 위해 웰컴 소켓을 생성해두었다. #  serverSocket

- server는 연결 요청을 기다린다.

- client가 clientSocket을 생성하고 server의 hostid, port로 TCP 연결 setup을 진행한다.

- server에서 클라이언트용 연결소켓을 생성한다. # connectionSocket=serverSocket.accept()

- client에서 clientSocket을 이용해 요청(연락)을 보낸다.

- server는 connectionSocket에서 요청을 읽는다.

- server가 connectionSocket에게 답장한다.

- client가 clientSocket에서 답장을 읽는다.

- client가 clientSocket을 종료한다.

- server가 connectionSocket을 종료한다. (serverSocket은 계속 유효함)

(+TCP 양방향 종료 - client에서 FIN 보내면 server가 FIN 보낸 후 close함. client는 FIN 받고서 close함)

 

예시 앱: TCP client

# 처음은 UDP와 동일
from socket import *
serverName = 'serverName'
serverPort = 12000
# TCP 소켓이므로 SOCK_STREAM
clientSocket = socket(AF_INET, SOCK_STREAM)
# connect(): server (ip, port)로 TCP 연결
cleintSocket.connect((serverName, serverPort))
sentence = raw_input("Input lowercase sentence.")
# send(): server 주소 붙일 필요x. 인코딩된 메시지만 보낸다!
clientSocket.send(sentence.encode())
# recv(): 응답이 문자열이다
modifiedSentence = clientSocket.recv(1024)
print ('From server:', modifiedSentence.decode())
clientSocket.close()

예시 앱: TCP server

from socket import *
serverPort = 12000
# welcoming socket(serverSocket) 생성: TCP 소켓이므로 SOCK_STREAM
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort)) # 튜플임 주의!!
# listen(1): serverSocket이 TCP 요청 받기를 시작한다
serverSocket.listen(1)

while True:
    # server는 accept()에 block되어 요청을 기다린다.
    # 요청이 들어오면 새로 생성된 소켓과 클라이언트 주소 튜플이 리턴된다
    connectionSocket, addr = serverSocket.accept()
    # recv(): connectionSocket에서 바이트를 디코딩후 읽는다 (문자열만)
    sentence = connectionSocket.recv(1024).decode()
    capitalizedSentence = sentence.upper() # 대문자로 변환
    # send(): 문자열을 인코딩해 보낸다 (문자열만)
    capitalizedSocket.send(capitalizedSentence.encode())
# connectionSocket을 종료힌다 (serverSocket은 종료하면 안 됨!!)
connectionSocket.close()

메서드 정리

socket(AF_INET, SOCK_STREAM): clientSocket, serverSocket 생성

serverSocket.bind(('', serverPort))

serverSocket.listen(1) // 요청 받기 시작

clientSocket.connect((serverName, serverPort)): TCP 연결 setup

connectionSocket, addr = serverSocket.accept() // TCP 요청 기다림

xxSocket.send(message): 메시지만을 전송

message = xxSocket.recv(1024): 메시지만을  받음

xxSocket.close(): 종료

 

주의

serverSocket에서 요청을 받겠다는 설정은 1번임!

serverSocket은 절대절대 종료하면 안됨