C++ Socket 프로로그래밍 [POSIX]

💡 Quotation

네트워크 소켓(network socket)은 컴퓨터 네트워크를 경유하는 프로세스 간 통신의 종착점이다. 오늘날 컴퓨터 간 통신의 대부분은 인터넷 프로토콜을 기반으로 하고 있으므로, 대부분의 네트워크 소켓은 인터넷 소켓이다. 네트워크 통신을 위한 프로그램들은 소켓을 생성하고, 이 소켓을 통해서 서로 데이터를 교환한다. 소켓은 RFC 147에 기술사항이 정의되어 있다. wikipedia

소켓은 운영체제에 종속되어 있어 플랫폼마다 사용하는 방법이 아주 조금 다릅니다. 이 글에서는 유닉스 또는 리눅스에 사용되는 POSIX1) Socket 을 간단하게 소개하고 프로그래밍 방법을 소개합니다.

POSIX Socket 🌏

BSD Socket 또는 Burkeley Socket 이라고 불리는 이 소켓의 가장 큰 특징은 파일처럼 핸들링한다는 점입니다! C 언어에서 파일을 열고 쓰기 위해 FILE 구조체를 open() 함수로 연 후 fputs() 와 같은 함수에 FILE 구조체를 인자로 전달하여 파일을 썼습니다.

마찬가지로 상대방 호스트의 특정 포트에 소켓을 열어 recv() 또는 send() 함수를 사용하여 파일을 읽고 쓰면서 통신을 합니다.

💡 Tips

파일과 관련된 함수를 사용해도 작동합니다!

이제 소켓으로 연결하는 과정을 살펴보도록 하겠습니다.

Procedure

Server 📡

서버는 클라이언트의 접속을 기다렸다가 연결 신호가 오면 연결을 해야 합니다. 따라서 기본적인 소켓 서버의 설정과 백그라운드에서 대기하며 클라이언트의 연결 수락과 데이터 수신을 할 수 있는 스레드 기술이 필요합니다!

IndexFunctionDescription
0socket()주소체계, 종류, 프로토콜(TCP, UDP 등)을 설정하고 소켓을 반환합니다.
1bind()소켓에 주소를 설정(바인딩)합니다.
2listen()서버를 열어 클라이언트의 연결을 기다립니다.
3accept()클라이언트의 연결을 받아들이고 클라이언트와 통신할 소켓을 반환합니다.
4recv() / send()반환된 클라이언트 소켓을 이용하여 읽고/쓰면서 통신을 합니다.
5shutdown()소켓의 연결을 종료합니다. 이때 한 방향만 종료할 수도 있습니다.
6close()생성된 소켓의 File descriptor1)를 닫습니다.

Client 🔌

클라이언트는 서버에 비해 간단합니다. 서버처럼 연결된 클라이언트들의 소켓을 관리해줄 필요도 없으며, 클라이언트 본인이 생성한 소켓으로만 통신을 하니 구현도 간단합니다.

IndexFunctionDescription
0socket()주소체계, 종류, 프로토콜(TCP, UDP 등)을 설정하고 소켓을 반환합니다.
1connect()설정된 주소로 서버에 접속합니다.
2recv() / send()반환된 클라이언트 소켓을 이용하여 읽고/쓰면서 통신을 합니다.
3shutdown()소켓의 연결을 종료합니다. 이때 한 방향만 종료할 수도 있습니다.
4close()생성된 소켓의 File descriptor 를 닫습니다.

Design 🛠️

  1. Blocking I/O 처리
    • accept() 함수와 recv() 함수는 클라이언트의 연결 또는 수신된 데이터가 있을 때까지 함수가 종료되지 않는 Blocking function입니다. 따라서 메인 스레드에서 위와 같은 함수를 사용한다면 프로그램 전체가 멈출테니 스레드를 생성하여 처리해야 합니다.
  2. Callback 처리
    • 메인 스레드에서 소켓을 통해 들어온 데이터를 수시로 확인하면서 처리한다면busy wating 쓸데없는 자원 낭비와 좋은 성능을 얻지 못합니다. 따라서 메인 스레드로부터 데이터를 수신하면 실행할 함수를 미리 받아 처리합니다.
  3. 확실한 자원 해제
    • 소켓뿐만 아니라 C/C++ 프로그래밍에 있어 자원의 해제는 엄청 중요합니다. close() 함수를 통하여 반드시 File descriptor 를 해제해야 합니다.
    • ⚠️ Warning

      Non-Blocking I/O를 구현하기 위해 select()함수를 사용할텐데, 이 때 File descriptor 가 1000개가 넘어가면 비정상적인 작동을 일으킵니다!

  4. 가변 길이 전송
    • 소켓을 이용하여 데이터를 전송할 때 데이터의 크기는 정해져 있지 않으며 어떤 형태던지 전송할 수 있어야 합니다.
    • 💡 Tips

      가변 길이 구조체를 사용하면 훨씬 쉽게 구현할 수 있습니다!

      자세한 내용은 이 곳을 참조해주세요. 😎

Implement ⌨️

구현 코드는 Github 에 있으며 샘플은 main.cpp를 참고하시기 바랍니다.

각주

  1. POSIX(포직스, /ˈpɒzɪks/)는 이식 가능 운영 체제 인터페이스(移植可能運營體制 interface, portable operating system interface)의 약자로, 서로 다른 UNIX OS의 공통 API를 정리하여 이식성이 높은 유닉스 응용 프로그램을 개발하기 위한 목적으로 IEEE가 책정한 애플리케이션 인터페이스 규격이다. wikipedia
  2. 컴퓨터 프로그래밍 분야에서 파일 서술자(file descriptor) 또는 파일 기술자는 특정한 파일에 접근하기 위한 추상적인 키이다. 이 용어는 일반적으로 POSIX 운영 체제에 쓰인다. wikipedia