Computer Science/컴퓨터네트워크(ComNet)

[컴네/CN] Socket Programming

gxxgsta 2023. 12. 5. 02:31
반응형
SMALL

네트워크 프로그래밍

운영체제의 프로세스 간 통신을 위한 방식으로 IPC(inter-process communication) 방식을 사용하여 여러 개의 프로세스간 통신이 가능하다. (통신은 프로세스임)

 

분산으로 각 프로세스가 나뉘어져 있는 경우 별도의 운영체제에서 소켓에 대한 api를 제공한다. 이때, 네트워크에 존재하는 클라이언트와 서버가 통신하기 위한 도구가 소켓이다. 이러한 소켓은 C, C++에서 구조체를 통해 제공하며, bind(), listen(), accept() 함수 등을 이용하여 소켓 통신하는 응용 프로그램을 제작할 수 있다.

 

운영체제는 C나 C++로 제작되어 있기 때문에 전통적으로는 C 계열의 언어를 이용하여 소켓 프로그래밍을 진행하였다.

하지만, 최근에는 응용 개발 시 순수 소켓을 사용하지 않는다.

왜냐하면 최근에 관련 라이브러리가 많이 생겼고, 다양한 framework들을 가져다 쓸 수 있기 때문이다.

 

이러한 framework에는

Java Spring, Netty, Python Django, FastAPI, JS Nodejs, Express가 있다.

 

Socket Programming 발전 트렌드

소켓 프로그래밍은 아주 기초적인 함수 이용방법 말고도 직군 별로 framework이 많아 응용하며 다양한 프로그램을 만들 수 있다.

 

- 백엔드/서버

C나 C++을 사용하던 Unix에서 Spring(JS, Go) 등을 사용할 수 있다.

 

- 프론트엔드/클라이언트

윈도우 mfc C++, C#를 사용했다.

최근에는 웹을 기반한 Java나 JS를 주로 사용한다.

 

- Web framework: 백엔드, 풀스택

Spring가 디폴트로 Python 계열의 Flask, Django, FastAPI 등을 사용한다.

Node.js는 서버와 클라이언트에서 JS를 공통으로 이용할 수 있으므로 풀스텍으로 최근에 주목받고 있다. 특히 비동기 + 싱글 스레드로 구현되어 성능이 좋다는 장점도 있다.

 

- 프론트엔드/앱

안드로이드나 Swift가 주로 사용되며 모두 소켓 기반 클래스들이 확장되었다.

 

socket programming 학습 이유

framework가 존재함에도 불구하고 우리가 소켓 프로그래밍을 공부해야 하는 이유가 무엇일까?

 

1. 네트워크 응용 소프트웨어를 직접 개발

가장 로우한 소켓api를 호출하여 개발해도 소켓에 대한 개념이 있어야 쉽게 개발 가능

 

2. 분산, 운체, 네트워크에서 기본적인 작동 방식 이해하고 학습해야 한다.

 

3. 대규모, 고성능 서버 개발

대규모, 고성능 서버 개발 위해 소켓 프로그래밍을 직접 이용해서 개발

이러한 서버는 게임서버를 위해 주로 개발되며 C, C++로 개발함

 

4. AI, 클라우드 서비스도 서버-클라이언트 구조의 서버가 분산 시스템화 되어 있는 것이므로 소켓 프로그래밍에 대해 학습하는 것이 좋다.

 

현재 소켓 뿐만 아니라 rpc 등은 직접 구현하고 있다.

 

소켓 프로그래밍 개요 (1)

 

위 그림은 소켓 프로그래밍의 가장 기본적인 구조이다.

소켓은 응용 계층과 전송 계층 사이의 api를 지칭한다.

 

전송(transport) 계층 아래는 운영체제 단계로 커널에서 기능을 제공한다.

커널은 네트워크 사이의 클라이언트 - 서버 기능을 제공해주고, 이러한 기능을 이용하기 위한 api가 소켓이다.

 

이러한 소켓을 이해하기 위해서는 소켓을 파일이라고 생각하는 것이 좋다.

소켓 또한 파일처럼 생성, 삭제와 데이터를 읽고 쓰는 작업을 하기 때문이다.

소켓은 네트워크를 연결하는 파일이라고 생각하면 된다.

 

Linux manual page

리눅스의 메뉴얼 페이지에서 소켓을 소개한다.

이때, 소켓은 endpoint를 만드는데, 이는 통신을 위함이다.

 

소켓은 endpoint를 참조하는 file discriptor를 리턴한다.

즉, 파일에 대한 자료구조를 동일하게 이용하는데, 이는 통신의 끝단인 포트번호를 이용하여 사용한다.

포트번호를 이용하여 데이터를 읽고 쓰는데, 이러한 데이터 r/w가 파일과 동일하다.

 

file discriptor로 데이터를 읽고 쓰는 것은 네트워크를 통해 데이터의 통신이 가능한 자료 구조임을 의미하는데, 이 소켓을 커널에서 작동시키고 있기 때문에 소켓함수를 이용하면 쉽게 통신을 할 수 있다.

 

리눅스 커널 소스코드는 공개되어 있다. 해당 파일에 대한 디스크립터로 읽고 쓰기가 가능하다.

 

소켓 프로그래밍 개요 (2)

소켓은 전송계층의 TCP 또는 UDP와 1:1로 매핑된다.

TCP나 UDP가 제공하는 기능을 각각 만족하는 소켓이 존재한다.

 

- UDP

비신뢰성: 패킷 손실, 순서 변경 가능

연결 없음

ip 주소와 포트번호로 송수신

 

- TCP

신뢰성: 패킷 손실 복구, 순서대로 패킷 제공

연결 설정 필요

바이트 단위의 스트림 전송

 

UDP 통신

 

소켓 생성 시 파라미터의 인자를 살펴보자.

AF_INET은 인터넷을 의미하고, SOCK_DGRAM은 UDP 통신임을 알려준다.

 

소켓 만들면 UDP는 연결을 생성하지 않기 때문에 서버 소켓에서 패킷을 기다린다.

클라이언트로부터 패킷을 받으면 다시 통신이 가능하다.

 

클라이언트도 동일한 소켓을 만들도록 ip 주소와 포트번호를 명시한다.

이후 전송에 대한 적절한 처리 후 응답한다.

 

Python UDP Server

 

파이썬으로 UDP 통신 시 서버의 코드는 위와 같다.

 

포트번호는 멀티플렉싱을 하기 위해 필수인 부분이다.

소켓을 만들고 소켓이라는 자료구조가 몇 번 포트와 바인드, 결합 되는지 bind()를 이용하여 해당 프로세스가 12000포트를 통해 패킷을 받을 수 잇다

recvfrom()함수를 통해 데이터를 수신할 수 있다.

 

Python UDP Client

클라이언트는 서버의 ip주소와, 호스트 이름을 사용한다.

서버의 포트 번호를 명시하고 데이터를 전송하는데, 소켓 만들때 데이터 그램이 언급된다면 해당 연결은 UDP 연결이다.

sendto()로 메세지를 전송하는데, 호스트네임와 포트번호 명시해야 한다. 

 

UDP는 간단하게 소켓을 만들고, ip주소와 포트 번호만 쓰고 데이터를 전송한다.

 

UDP Example

위 코드는 실제로 사용되는 코드로 Python3으로 구현되어 있다.

(앞의 코드는 Python2로 구현됨)

 

위 예제는 sender의 데이터 전송을 대기하다가 데이터를 받으면 다시 에코해준다.

sender는 서버의 ip주소와 포트번호를 사용한다.

 

TCP 연결

tcp 이용한 소켓 프로그래밍은 연결을 만든다.

제공되는 api에서 TCP 연결을 만들기 위한 함수들이 존재한다.

 

소켓 생성은 동일하지만, accept()와 listen()이 있다.

연결 만들고 나면 데이터를 전송할 수 있다.

 

Python TCP Client

 

클라이언트는 서버의 이름이나 ip주소와 포트번호를 쓴다.

소켓 생성 시 인자로 SOCKET_STREAM이 들어가는데, 이는 TCP가 스트림 단위로 전송되므로 TCP 연결을 의미한다.

 

connect()함수를 통해 서버와 실제 소켓을 연결하고 난 뒤 데이터를 전송한다.

 

Python TCP Server

 

마찬가지로 소켓 생성 시 인자로 SOCKET_STREAM이 들어가는데, 이는 TCP가 스트림 단위로 전송되므로 TCP 연결을 의미한다.

 

이후 소켓을 bind 한 후 listen 상태로 들어가게 된다.

listen 상태는 연결을 만들고 연결이 accept() (= 완료)되는 상태를 기다린다.

연결이 만들어지면 recv()함수를 통해 데이터의 수신을 기다린다.

 

TCP Example

 

위 코드에서 좌측은 서버의 코드이고, 우측은 클라이언트의 코드이다.

 

- 서버

소켓 생성 -> bind() , listen() -> 대기

accept() -> 연결 완료 -> recv()로  수신 대기

 

- 클라이언트

소켓 생성 -> 커넥트로 연결 생성 -> 연결 생성 시 바로 데이터 전송

 

TCP Server

- 소켓 만들기
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 

- IP주소와 포트번호로 서버 소켓 묶기: 바인드
s.bind((IP_ADDR, TCP_PORT))

 

- 대기하기(인자: 연결요청 넣을 큐의 크기)
s.listen(1)

 

- 연결 수락하기
conn, addr = s.accept()

(연결이 수락되면 s.accept()로 리턴된다.)

 

- 데이터 수신
data = conn.recv(BUFFER_SIZE)

 

소켓 생성 -> bind -> listen -> accept -> recv

 

TCP Client

- 소켓 만들기
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

 

- 연결
s.connect((IP, TCP_PORT))

 

- 데이터전송
s.send(MESSAGE.encode('utf-8'))

 

소켓 생성 -> connect -> send

 

서버 I/O 구조

여러 개의 클라이언트 요청을 동시에 처리하기 위해 가능한 여러 가지의 작동 방식이 존재한다.

이러한 작동 방식은 운영체제의 i/o 처리와 동일하다.

 

Blocking

프로세스가 시스템을 호출 하고나서 결 과가 반환되기까지 다음 처리로 넘어가지 않음


Non-blocking

시스템을 호출한 직후에 프로그램 으로 제어가 다시 돌아와서 시스템 호출의 종료를 기다리지 않고 다음 처리로 넘어갈 수 있음

서버가 DB 처리, 파일 rw 등 io시간이 길면 논블로킹 방법이 효율적이다.

 

Synchronous

작업을 요청한 후 작업의 결과가 나올 때까지 기다린 후 처리


Asynchronous

직전 시스템 호출의 종료가 발생하 면 그에 따른 처리를 진행 (Android App)

 

위의 작동 방식을 혼합하여 사용이 가능하다.

 

위와 같이 여러 작동 방식을 혼합하여 사용하는 이유는 여러 클라이언트의 요청을 동시에 처리하여 성능을 높이기 위함이다.

 

multiprocessing

프로세스를 여러 개 사용하는 방식으로 fork(), 멀티 프로세스 관리, IPC 통신 방법 등을 사용한다.

파이썬으로 멀티 프로세스의 별도의 라이브러리 호출로 사용하고 있다.

 

multithread

멀티 프로세싱 방법은 운영체제의 자원을 많이 사용한다. 따라서 더 작은 단위인 쓰레드를 이용하여 멀티 스레딩을 진행할 수 있다. 멀티 프로세싱에 비해 자원을 덜 사용한다는 장점이 있다.

 

멀티 스레드는 언어 별로 지원하며, io 연산이 존재할 때 효과적이다.

Python에서는 스레드를 별도의 묘듈로 이용이 가능하다.

 

start()와 join() 함수가 존재하는데, 이때 join() 함수는 새로 생성한 스레드에 대해 종료까지 기다리는 역할을 한다.

 

Select

소켓을 여러 개 생성하여 모든 소켓에 대해 송수신 이벤트가 발생했는지 계속해서 모니터링하는 방법이다.

싱글 스레드로 대규모의 여러 클라이언트를 처리할 수 있다.

하지만 계속해서 모니터링 해야 하므로 cpu의 자원을 계속 사용하여 일부 task가 대기해야 한다.

blocking+async인 방법이다.

 

Async

최근에 자주 사용되는 방법으로 많은 클라이언트 요청을 처리할 수 있다.

자원의 효율성이 좋다는 장점이 있지만,

코드가 복잡하여 프로그래밍이 어렵고, 디버깅과 테스트가 어렵다는 단점이 있다.

 

WebSocket, Socket.io

소켓 프로그래밍은 소켓을 이용한 직접 프로그래밍 방식보다 framework단에서 많이 사용한다.

그 중에서 소켓 사용 시 웹 소켓 이용한 프로그래밍 하는 경우가 간혹 존재한다.

하지만 웹 소켓은 지금까지 우리가 학습한 소켓 프로그래밍과 차이가 있다.

 

웹에서의 소켓은 구현된 예제가 socket.io이다.

우리가 공부한 소켓 프로그래밍은 L4로 TCP/UDP단에서 사용 가능한 소켓이다.

하지만 웹 소켓으로 프로그램 짤 때 응용 계층(http 계층)에서 짜야 한다. 

 

실시간 통신 시 웹에서 보통 처리를 하는데, 이러한 웹소켓은 RFC6455 표준으로 등록되어 있다.

 

http에서는 실시간 양방향 통신이 어렵다.

왜냐하면, http는 상태가 없으므로 서버와 클라이언트 사이의 연결 자체가 존재할 수 없기 때문이다.

따라서, 연결을 만들어 지속적으로 데이터를 주고 받기 위해서는 라이브러리가 필요하다.

 

이러한 기능은 웹 기반에서 많이 필요하므로 http 위에서 이러한 소켓 구조를 만들어서 활용하게 한다.

이전까지의 polling이나 stream이 웹 소켓으로 발전한 것이다.

 

가장 대표적으로 socket.io가 존재한다.

WebSocket, FlashSocket, AJAX Long Polling, Ajax Multi Part Streaming, Iframe, JSONP Polling 등에서 주로 사용되며, 특히 Node.js에서 가장 많이 사용된다.

Node.js에서 socket.io

많은 사례들에서 socket.io로 실시간 응용을 만든다.

Node.js에서는 웹 서버에서 굉장히 많이 사용되고 최근에 주목 받고 있는 framework다.

왜냐하면 Node.js는 js로 짜여지기 때문데 브라우저와 서버가 동일한 언어로 구현할 수 있기 때문이다.

 

Node.js는 비동기 방식에 싱글 스레드를 이용하여 굉장히 빠른 성능을 가지고 있는데

네이버에서는 초당 5천건을 24코어의 서버 4대를 사용하고 있다.

 

https://poiemaweb.com/nodejs-socketio

위 사이트를 통해 웹 브라우저에서 채팅 프로그램을 만들 수 있다.

 

반응형
LIST

'Computer Science > 컴퓨터네트워크(ComNet)' 카테고리의 다른 글

[컴네/CN] Network계층의 IP  (1) 2023.12.06
[컴네/CN] 혼잡제어  (1) 2023.12.05
[컴네/CN] TCP  (0) 2023.12.04
[컴네/CN] 비동기 프로그래밍  (1) 2023.12.04
[컴네/CN] 신뢰성 있는 프로토콜  (2) 2023.12.03