풀스택 서비스 네트워크(4), Socket

2022. 10. 11. 15:28강의 내용 정리/풀스택서비스네트워킹

728x90
반응형

Socket


1. Socket & Socket API의 이해

1) Network Socket이란?

내부의 엔드포인트를 통해 데이터를 송수신하는 단위

 

  • Socket Programming은 TCP/IP Networks에서 커뮤니케이션하는 기반이 되는 기술을 의미한다.
  • 소켓 연결은 두 개의 다른 컴퓨터들 간에 이뤄질 수 있다.

 

2) Client and Server Architecture

(1) Socket Server

소켓을 전송받는 서버

 

(2) Client

소켓을 전송하는 클라이언트

 

 

3) Berkeley Socket API

Socket API는 다른 컴퓨터와의 메세지를 주고받기위해 사용하는 API이다. 

cf) API는 요구 하는 것에 대해 응답하는 것을 개념이다. 

 

(1) Berkeley Socket API Functions

  • socket(): 소켓을 열 때 사용한다. 따라서 버퍼를 만들기도 한다.
  • bind(): 통신하기 위해 필요한 정보인 IP address와 port number를 os에게 전달해서 바인드하는 행위를 한다. 일반적으로 서버에서 사용한다.
  • listen(): 클라이언트에게로부터 정보를 받기 위해 기다린다.
  • connect(): 클라이언트가 서버에게 연결 요청을 하는 함수이다. 클라이언트의 포트넘버는 직접 넘기지 않고, os가 알아서 넘긴다. 
  • accept(): 서버에서 연결 요청을 받아들인다.
  • send(), recv(), sendto(), recfrom(): 메세지를 주고 받는다.
  • close(): 소켓을 닫는다. 일반적으로 클라이언트가 요청한다.
  • gethostbyname(), gethostbyaddr(): 이름으로 호스트를 알아내거나, 어드레스로 호스트를 알아내는 함수이다.
  • select(): poll을 통해 가져온 것을 실행한다.
  • poll(): 소켓의 상태를 보다가 상태가 괜찮으면 소켓을 가져온다,

2. 1:1 TCP Socket 프로그래밍

1) socket function

유닉스에서는 파일이나 네트워크를 숫자로 지정하는데 이를 file descriptor라고 한다.

  • -1은 에러를 의미한다. 

(1) Three arguments

 

 

socket function은 C를 기반으로 하기에 객체지향 정보가 없다. 따라서 이와 관련된 정보인 IPPROTO_TCP 등등을 넘긴다. 하지만 파이썬의 경우에는 객체지향언어로 import해서 객체를 부르기 때문에 이러한 정보를 전달할 필요가 없다. 또한 C는 각각의 변수를 따로따로 저장해 어드레스와 랭스를 멤버함수에 넣어 변수를 불러온다.

 

 

2) bind()

 연결 설정을 하기위해 사전에 IP address와 Port 번호를 연결하는 역할을 한다.

  • 0을 리턴하면 성공, -1을 리턴하면 실패로 간주한다. 
  • sockfd, my_addr, addrlen을 파라미터로 전달받는다. 이는 각각 소켓 descriptor, address, 소켓 address의 사이즈를 의미한다.

 

3) listen()

클라이언트가 서버로 연결하는 것을 기다린다. 이는 tcp에만 있다. 백로그는 큐에 자료를 저장한다. 동기식 방식에서 의미가 있다.

 

4) Accept()

Accept는 커넥션 연결을 수락하고 그에 관한 정보를 리턴한다.

  • 객체지향 언어로 되어있는 경우에는 대부분 드러나있지 않다.
  • sockfd는 소켓을 연결하는 디스크립터이다.
  • cliaddr은 클라이언트의 주소 정보를 전달받는 포인터이다.
  • addrlen은 클라이언트 주소 정보의 실이를 의미한다. 
  • accept 함수는 새로운 소켓 디스크립터를 리턴한다. 만약 -1이면 connection 에러가 발생된 것이다.
  • udp(datagram sockets)에서 connect를 호출하는 경우는 거의 없다.
  •  

 5) Connect()

Connect는 외부 호스트와 직접적인 커뮤니케이션 링크를 설립하는 것을 의미한다.

  • udp과 같이 커넥션 리스 프로토콜의 경우 외부 호스트와 메세지를 보내고, 받는 것이 연결임을 시사하기에 이러한 작업이 필요 없다.
  • connect 함수는 연결이 성공되면 0, 연결이 실패하면 -1을 리턴한다.

 

 

6) 파이썬 코드

(1) Server

통신에서는 예외처리를 해야한다.

 (2) Client

client에서는 input을 보내고 응답을 기다리는 동기식으로 처리가 되고 있다.

 

 


3. 1:N 동기식 & 비동기식 TCP Socket 서버 프로그래밍

1) 동기식 1:N 서버 프로그래밍

 

  • 접근 1은 Socket API와 운영체제가 지원하는 순수한 비동기 함수나 멀티쓰레드 함수를 엮어서 사용할 수 있다.
  • 접근 2는 최근에 나온 언어들에서 제공하는 라이브러리를 사용해서 구현

 

(1) socketserver 모듈

  • RequestHandler는 요청을 다루는 함수이다. Bind_and_active는 연결되면 살아나서 사용할 수 있도록 한다.

 

(2) TCP 서버를 만드는 절차

1. request handler 클래스인 BaseRequestHandler 클래스를 오버라이딩해서 handle 메서드를 오버라이딩한다.

2. TCP 서버 클래스를 구현하기 위해 IP 주소와 포트 번호와 함께 상속받은 request handler class를 생성자에 전달해 이를 인스턴스를 생성한다.

3. handle_request() 메서드나 serve_forever() 메서드를 호출한다.

4. 이후 서버의 모든 작업이 끝나면 server_close() 함수를 호출해 socket을 닫아준다.

 

  • BaseServer를 통해 이를 tcp, udp 등등으로 상속받아서 사용할 수 있다.

 

 

2) 동기식 1:N 파이썬 코드

(1) Server

클라이언트의 요청을 받아서 처리할 request handler class를 오버라이드하고 tcp server를 사용한다.

 

(2) 문제점

초기 파이썬 파일의 경우에는 동기식 처리가 되기 때문에 클라이언트가 n이 된다면 이를 제대로 통신할 수 없다.

 

따라서 이를 처리하기 위해 Thread를 활용하여 각 클라이언트마다 Thread를 할당해준다. 이를 위해 믹스인 클래스를 사용한다.

 

3) 비동기식 1:N 서버 프로그래밍

(1) 동기식 동작과 비동기식 동작

 

(2) Mixin class

믹스인 클래스는 다른 클래스와 붙였을 때 유용할 것 같은 클래스를 의미한다. 해당 클래스는 인스턴스로 만들지는 않지만 조합을 했을 때는 유용하게 된다. Such as flag 클래스, 우리가 만들 mixin 클래스는 ForkingMixIn, ThreadingMixIn이다.

 

(3) Thread

자원을 공유하며 계산을 하거나 동일한 정보에 대해 응답을 하는 경우에는 멀티 쓰레드, 자원을 분리해서 각각의 프로그램을 돌릴 경우에는 멀티 프로세스를 사용한다. 대부분의 운영체제 프로그래밍에서 프로세스를 사용할 때는 fork, 쓰레드를 사용할 때는 Thread를 사용한다.

 

(4)  multi thread 버전 생성 과정

1. ThreadlinMixIn class와  TCPServer class를 상속받아 클래스를 오버라이드 한다.

이때 TCPServer 클래스를 ThreadingMixIn 클래스로 오버라이드 해야하기에 순서를 맞춰야한다.

Mixin 할 때는 base가 뒤에 쓰이고, mixin 클래스를 앞에 써서 동일한 메서드를 덮는다.

 

2.  sockeetserver의 Request Handler를 수정한다. 

이때 Request Handler class의 handle 메서드를 오버라이드 해서 덮어 쓴다. 

cf) request handler 클래스는 datagram이나 stream services와는 차이가 있기에 이 때에는 각각 DatagramRequestHandler나 StreamRequestHandler를 사용해야한다.

 

3. Main Thread를 설정하고 실행한다.

 

Main이 죽는 경우에 다른 쓰레드를 모두 죽이거나, 다른 쓰레드가 돌아가다가 스스로 죽이는 것을 설정할 수 있다. 이는 daemon threads이다. 쓰레드가 여러 개일 때 이를 처리할 수 있다. Daemon을 true로 설정하면 while이 끝나면 모든 쓰레드를 죽이는 것을 의미한다.

While 문 내부에서는 서버 운영자가 quit을 입력하면 서버가 죽도록 만들었다. 따라서 에코 프로그램을 만드는 와중에서도 서버를 종료시키는 것을 만들 수 있다.

 

  • threading.Thread(target = server.serve_forever): target에 주어진 키워드에 따라 쓰레드를 생성한다.
  • server_thread.start():  쓰레드의 동작을 수행한다.
  • threading.active_count(): 살아있는 쓰레드의 개수를 리턴한다.
  • server_thread.run(): 쓰레드의 동작을 표현한다. 이는 서브 클래스에서 오버라이드해서 사용할 수 있다.

서버를 실행시킬 때 추가적으로 어떤 작업을 하게 시킬 때에는 server의 run()을 오버라이드할 수 있다.

 

 

4) 1:N 비동기식 TCP 파이썬 코드

(1) Server

threading.Thread는 지속적으로 listen을 하고 클라이언트와 연결이 된다면 쓰레드를 하나 할당한 뒤 오버라이드한 handle 부분이 실행되는 것을 볼 수 있다.

 

(2) Client

2) 의 (2)와 동일하다.

 

(3) 실행 화면

 

5) 그 외의 비동기식 Socket 라이브러리 지원

asyncio

selectors


4. N:M TCP 기반 비동기식 채팅 프로그램 개발

서버의 경우에는 1:N에서 비동기식으로 개발을 했기 때문에 클라이언트 정보를 모두 담는다는 것을 제외하곤  크게 변경된 부분은 없다.  하지만 클라이언트부분에서도 보내고 받는 것을 비동기식으로 처리해야하기에 이를 처리한다.

 

서버에서는 연결이 되어있는 모든 소켓(클라이언트)에게 메시지를 모두 보내는 역할을 한다.

 

1) 변경사항

(1) Server 변경 내용

- 모든 client의 socket 연결 정보를 저장해둔다. (등록, 삭제 과정을 거친다.)

- 특정 client가 전송한 메세지를 모든 client들에게 전달한다.

 

(2) Client 변경 내용

- Thread를 사용하여 전송과 수신을 비동기적으로 분리한다.

- 수신 Thread를 통해 서버로부터의 전송에 항상 준비를 한다.

 

2) 파이썬 코드

(1) Server

group_queue라는 이름의 리스트로 연결된 클라이언트를 관리한다.

 

(2) Client

client에서 수신/송신을 비동기적으로 하기 위한 쓰레드를 만들어서 관리하고 있다.

clientThread는 수신을 위한 쓰레드이다. 

 

(3) 동작 화면


5. 1:N UDP Socket 서버 프로그래밍

UDP 서버는 connetion-less 방식으로 connect 절차가 없다.

연결 설정 및 해제 과정이 없어진다.

 

2) 파이썬 코드

(1) Server

socketserver.UDPServer를 열어서 사용한다. 이때 소켓을 전달한 클라이언트에게 전달받은 메세지를 그대로 보내는 것을 확인할 수 있다. (16줄)

self.request는 TCP와는 다르게 data와 client socket이 들어가있다. 연결과정이 없기에 단순히 데이터를 전달하는 식으로 동작한다.

cf) TCP에서의 self.request는 클라이언트의 연결 정보를 가지고 있다. 따라서 request[0]과 동일하다. TCP는 self.request.recv(1024) 함수를 호출해 메세지를 전달받는다.

 

(2) Client

TCP에서는 client에서 connect 연결 요청을 보냈다. 하지만 UDP에서는 그러한 과정이 사라졌다.

 

 

 

 

 

 

6. N:M UDP 기반 채팅 서비스 개발

UDP는 연결 설정 및 해제 과정이 없기에 클라이언트가 채팅에 참여하고 싶으면 #REG를 입력해서 연결설정을 하는 것처럼 행동한다. #DEREG면 채팅방을 나가는 것처럼 구현한다. 

 

또한 서버는 멀티쓰레드를 할 필요가 없어진다. 각 쓰레드별로 클라이언트와 커넥션을 하기위해 멀티쓰레드를 사용했지만 연결 설정 과정이 필요 없기에 이젠 멀티 쓰레드가 필요가 없어진다.

 

cf) TCP는 클라이언트와의 통신을 위해 계속 클라이언트와의 연결설정 및 해제를 해야했다. 이에 따라 각 클라이언트별로 쓰레드를 할당하여 관리했는데, UDP는 connectionless이기에 해당 과정이 빠져있다.

 

2) 파이썬 코드

(1) Server

handler의 handle 메서드에서의 조건문이 달라진 것을 확인할 수 있다.  이는 클라이언트를 등록하기 위한 과정이다.

main에서 서버는 단순히 실행 중이고, ctrl + c를 눌렀을 때 종료되도록 만들었다.

 

(2) Client

위의 코드와 동일하지만 등록을 하기 위해 먼저 #REG 코드를 보내고, 등록 해제를 하기 위해 #DREG 코드를 보내는 식의 과정이 필요하다.

 

추가적인 고려사항

프로그램을 끄거나 컴퓨터를 껐을 때 채팅방을 나가는 기능 등등

728x90
반응형