본문 바로가기
운영체제&네트워크/Network

소켓 프로그래밍 기본: 데이터 송수신과 동시성 처리

by GangDev 2024. 4. 25.

 

프로토콜 스택에 메시지 송신을 의뢰하는 과정은 소켓을 통해 데이터를 송수신하는 단계를 포함한다. 이 과정은 다음과 같은 단계로 구성된다:

1. **소켓 연결 후 파이프 생성**: 소켓이 상대측과 연결되면, 파이프가 생성되어 애플리케이션이 소켓을 직접 다룰 수 있게 된다. 이 때, 소켓을 통해 프로토콜 스택에 일을 의뢰하기 위해 Socket 라이브러리를 사용한다.

2. **메시지 송신**: 송신 데이터를 서버에게 송신하기 위해, `write`라는 프로그램 부품을 사용한다. 이 때, 소켓에는 연결된 상대가 기록되어 있으므로, 디스크립터로 소켓을 지정하면 연결된 상대가 판명되어 그 곳을 향해 데이터를 송신한다.

3. **메시지 수신**: 응답 메시지를 수신하기 위해, Socket의 `read` 프로그램 부품을 통해 프로토콜 스택에 수신 동작을 의뢰한다. 이 때, 응답 메시지를 저장하기 위한 메모리 영역인 수신 버퍼를 지정해야 한다. `read`는 응답 메시지를 받아서 이 수신 버퍼에 저장한다.

4. **연결 끊기**: 데이터 송수신을 마친 후에는, 소켓 연결을 끊기 위해 Socket 라이브러리의 `close` 프로그램 부품을 호출한다. 이 때, 애플리케이션의 종류에 따라 서버 측에서 먼저 `close`를 실행하는 경우도 있고, 클라이언트에서 먼저 `close`를 실행하는 경우도 있다. 만약 서버 측에서 먼저 `close`를 호출했다면, 연결을 끊었다는 것이 클라이언트 측에 전달되어 클라이언트의 소켓도 연결을 끊는다.

이 과정을 통해 클라이언트는 웹 서버로 접속하고, 프로토콜 스택이 클라이언트 측의 소켓을 식별하여 적절한 처리를 하고, 응답 메시지를 반송하는 과정을 거친다. 이러한 과정은 네트워크 통신에서 중요한 역할을 하며, 애플리케이션이 데이터를 송수신할 수 있게 해준다.

 


소켓은 네트워크를 통해 두 개의 노드(컴퓨터 또는 인터넷 연결 장치) 간의 통신을 설정하는 방법이다. 각 노드는 소켓을 사용하여 서로에게 연결을 설정하고, 이 연결을 통해 데이터를 주고받는다. 소켓은 노드에 연결되어 있으며, 두 노드 간의 연결을 구현하기 위해 필요한 신호를 소켓을 통해 보내고 받는다.

소켓 프로그래밍은 서버 노드와 클라이언트 노드 간의 연결을 설정하여 인터넷을 통해 데이터를 공유하는 데 사용된다. 서버 노드는 연결 신호를 기다리고 서버와 클라이언트 노드 간의 연결을 설정하며, 클라이언트는 서버에 연결 요청을 보낸다. 클라이언트와 서버 노드 간의 연결을 설정하기 위해 다양한 모드와 옵션이 있다. 포트와 주소에 바인딩된 소켓은 클라이언트와 서버 노드 간에 데이터를 보내고 받는 데 사용된다. 데이터를 소켓을 통해 보내고 받기 위해 `write()`와 `read()` 함수가 사용된다.

소켓 프로그래밍의 구현은 C 프로그래밍 언어를 사용하여 이루어진다. 클라이언트 측에서 소켓은 `socket()` 함수를 사용하여 생성되고, `bind()` 함수를 사용하여 포트와 주소에 바인딩된다. 클라이언트는 `connect()` 함수를 사용하여 서버 노드에 연결 요청을 보낼 수 있다. 서버와 클라이언트 노드는 `connect()` 함수를 사용하여 연결되며, 메시지는 `write()` 함수를 사용하여 클라이언트에게 보내고, `read()` 함수를 사용하여 메시지를 받을 수 있다.

소켓 프로그래밍은 네트워크 프로그래밍의 기본적인 부분으로, 두 개의 노드가 특정 IP의 특정 포트에서 서로를 듣고 연결을 형성하는 방식으로 작동한다. 서버는 리스너 소켓을 형성하고, 클라이언트는 서버에 도달하여 연결을 형성한다.

소켓은 프로그래머 또는 API에 의해 정의된 데이터 구조이며, 네트워크 카드의 하드웨어 장치가 아니다. 소켓은 데이터를 전달하는 데 사용되며, 이 데이터는 텍스트 기반 또는 이진 형식일 수 있다. 소켓 기반 네트워크 프로그래밍의 대안은 없으며, 모든 네트워크 애플리케이션은 어떤 형태의 소켓을 사용한다.


소켓과 함께 사용되는 일반적인 프로토콜은 다음과 같다:

1. **TCP (Transmission Control Protocol)**: TCP는 연결 지향형 프로토콜로, 데이터를 안정적으로 전송하기 위해 사용된다. 데이터를 패킷으로 나누고, 각 패킷에 순서 번호를 부여하여 수신측에서 순서대로 재조립한다. 또한, 패킷 손실을 감지하고 재전송을 요청할 수 있다. TCP는 웹 브라우저와 서버 간의 HTTP 통신에 주로 사용된다.

2. **UDP (User Datagram Protocol)**: UDP는 비연결형 프로토콜로, 데이터를 패킷으로 나누고 수신측에 전송하지만, 패킷의 순서나 손실을 보장하지 않는다. 이는 실시간 스트리밍이나 게임과 같은 애플리케이션에서 사용되는데, 빠른 응답 시간이 중요한 경우에 적합하다.

3. **SSL/TLS (Secure Sockets Layer/Transport Layer Security)**: SSL과 TLS는 네트워크 통신을 암호화하여 데이터의 보안을 유지하는 프로토콜이다. 이들은 주로 웹 브라우저와 서버 간의 HTTPS 통신에 사용되며, 데이터 전송 과정에서 데이터의 무결성과 기밀성을 보장한다.

4. **FTP (File Transfer Protocol)**: FTP는 파일 전송을 위한 프로토콜로, 클라이언트와 서버 간에 파일을 안전하게 전송하기 위해 사용된다. FTP는 두 개의 연결을 사용하여 명령과 데이터를 전송한다. 명령 연결은 클라이언트와 서버 간의 제어 정보를 전송하고, 데이터 연결은 실제 파일 데이터를 전송한다.

5. **SMTP (Simple Mail Transfer Protocol)**: SMTP는 이메일 메시지를 전송하기 위한 프로토콜로, 메일 서버 간에 메일을 라우팅하고 전송한다. SMTP는 메일 서버가 메일 메시지를 수신하고, 다른 메일 서버로 전송하는 데 사용된다.

6. **DNS (Domain Name System)**: DNS는 도메인 이름을 IP 주소로 변환하는 프로토콜로, 사용자가 웹 사이트를 방문할 때 도메인 이름을 입력하면 DNS 서버가 해당 도메인의 IP 주소를 반환한다. 이를 통해 웹 브라우저는 웹 서버에 연결할 수 있다.

이러한 프로토콜은 소켓을 통해 네트워크 통신을 구현하는 데 사용되며, 각각의 특성과 요구 사항에 따라 적절한 프로토콜을 선택하여 사용합니다.


소켓과 TCP 또는 UDP 연결은 네트워크 통신에서 중요한 개념으로, 각각의 차이점을 이해하는 것이 중요하다.

- **소켓**은 네트워크 통신을 위한 추상적인 개념으로, 두 개의 노드 간에 데이터를 주고받는 데 사용되는 인터페이스이다. 소켓은 프로토콜 스택의 일부로, 데이터를 전송하고 수신하는 데 사용되는 프로그래밍 인터페이스를 제공한다. 소켓은 프로토콜(예: TCP, UDP)에 독립적이며, 프로토콜의 구현 세부사항을 추상화한다. 즉, 소켓은 프로토콜의 종류에 상관없이 데이터를 전송하고 수신하는 방법을 제공한다.

- **TCP (Transmission Control Protocol)**는 연결 지향형 프로토콜로, 데이터를 안정적으로 전송하기 위해 사용된다. TCP는 데이터를 패킷으로 나누고, 각 패킷에 순서 번호를 부여하여 수신측에서 순서대로 재조립한다. 또한, 패킷 손실을 감지하고 재전송을 요청할 수 있다. TCP는 웹 브라우저와 서버 간의 HTTP 통신에 주로 사용된다.

- **UDP (User Datagram Protocol)**는 비연결형 프로토콜로, 데이터를 패킷으로 나누고 수신측에 전송하지만, 패킷의 순서나 손실을 보장하지 않는다. 이는 실시간 스트리밍이나 게임과 같은 애플리케이션에서 사용되는데, 빠른 응답 시간이 중요한 경우에 적합하다.

소켓과 TCP 또는 UDP 연결의 주요 차이점은 프로토콜의 연결 지향성과 신뢰성에 있다. TCP는 연결 지향형 프로토콜로, 데이터 전송 전에 연결을 설정하고, 데이터 전송 후에 연결을 종료한다. 반면, UDP는 비연결형 프로토콜로, 연결 설정 없이 데이터를 전송한다. 이로 인해 TCP는 데이터 전송의 신뢰성을 보장하지만, UDP는 데이터 전송의 신뢰성을 보장하지 않는다.

따라서, 소켓은 프로토콜의 구현 세부사항을 추상화하는 인터페이스이며, TCP 또는 UDP 연결은 소켓을 통해 데이터를 전송하고 수신하는 데 사용되는 프로토콜의 종류이다. 각 프로토콜은 데이터 전송의 신뢰성과 연결 지향성에 따라 다르게 동작하며, 애플리케이션의 요구 사항에 따라 적절한 프로토콜을 선택하여 사용한다.


소켓을 사용하여 동시에 데이터를 보내고 받는 것은 가능하다. 이는 멀티스레딩(multithreading) 또는 비동기 I/O(asynchronous I/O)를 통해 구현될 수 있다. 멀티스레딩은 여러 스레드를 사용하여 소켓에서 데이터를 동시에 보내고 받는 것을 가능하게 한다. 각 스레드는 별도의 작업을 수행할 수 있으며, 한 스레드는 데이터를 보내고(send), 다른 스레드는 데이터를 받는(receive) 작업을 병렬로 처리할 수 있다.

비동기 I/O는 소켓에서 데이터를 보내고 받는 작업을 비동기적으로 처리할 수 있게 해준다. 이는 데이터를 보내거나 받는 작업이 완료될 때까지 기다리지 않고, 다른 작업을 계속 수행할 수 있게 해준다. 비동기 I/O를 사용하면, 소켓에서 데이터를 보내고 받는 작업이 동시에 이루어질 수 있다.

멀티스레딩과 비동기 I/O 모두 소켓을 사용하여 동시에 데이터를 보내고 받는 것을 가능하게 하지만, 각각의 방식은 애플리케이션의 요구 사항과 환경에 따라 선택된다. 멀티스레딩은 여러 스레드를 사용하여 동시에 작업을 처리하는 데 적합하며, 비동기 I/O는 데이터를 보내고 받는 작업이 완료될 때까지 기다리지 않고 다른 작업을 계속 수행할 수 있게 해주는 데 적합하다.

따라서, 소켓을 사용하여 동시에 데이터를 보내고 받는 것은 가능하며, 이는 멀티스레딩 또는 비동기 I/O를 통해 구현될 수 있다. 이러한 방식은 애플리케이션의 성능을 향상시키고, 데이터 전송의 효율성을 높일 수 있다.


SSL/TLS는 통신 중 데이터의 무결성과 기밀성을 보장하기 위해 다음과 같은 방법을 사용한다:

1. **기밀성**: SSL/TLS는 대칭 키 암호화를 사용하여 데이터의 기밀성을 보장한다. 데이터를 송신하는 측(클라이언트/서버)은 데이터를 암호화하고, 수신하는 측(서버/클라이언트)은 암호화된 데이터를 복호화하여 사용한다. 이 과정에서 암호화 키는 송신자와 수신자 사이에만 공유되므로, 도청자는 실제 데이터를 읽을 수 없다.

2. **인증**: SSL/TLS는 인증서 검증 과정을 통해 서버(및 선택적으로 클라이언트) 인증을 제공한다. 클라이언트는 서버의 인증서를 검증하여 올바른 서버와 통신하고 있는지 확인한다. 이 과정은 클라이언트가 올바른 서버와 통신하고 있음을 보장하며, 클라이언트 인증이 활성화된 경우 서버도 올바른 클라이언트와 통신하고 있음을 보장한다.

3. **메시지 무결성**: SSL/TLS는 메시지 인증 코드(MAC)를 사용하여 메시지 무결성을 보장한다. 데이터 교환 중 메시지의 손실, 의도적인 수정, 또는 변조를 감지하고, 이를 통해 메시지 무결성과 인증성을 지원한다. 메시지가 전송될 때 각 메시지에 MAC 또는 해시 기반 메시지 인증 코드(HMAC)가 첨부되어, 수신 측이 데이터가 전송 중에 변경되지 않았음을 확인할 수 있다.

이러한 방법을 통해 SSL/TLS는 통신 중 데이터의 무결성과 기밀성을 보장한다. 데이터가 암호화되어 있으므로 도청자는 실제 데이터를 읽을 수 없으며, 인증서 검증을 통해 통신 상대가 누구인지 확인할 수 있다. 또한, MAC를 사용하여 데이터가 전송 중에 변경되지 않았음을 확인할 수 있다.


멀티스레딩과 비동기 I/O 사이의 성능 비교는 애플리케이션의 특성과 사용 환경에 따라 다르다. 두 방식 모두 데이터를 보내고 받는 작업을 효율적으로 처리하는 데 사용될 수 있지만, 각각의 방식은 다른 장단점을 가지고 있다.

- **멀티스레딩**: 멀티스레딩은 여러 스레드를 사용하여 동시에 작업을 처리하는 방식이다. 이 방식은 네트워크 바운드 애플리케이션에서 특히 유용할 수 있다. 스레드 간의 컨텍스트 전환 비용이 있지만, 물리적 프로세서의 수에 따라 최적의 스레드 수를 찾을 수 있다. 멀티스레딩은 데이터를 보내고 받는 작업을 병렬로 처리할 수 있어 성능을 향상시킬 수 있다.

- **비동기 I/O**: 비동기 I/O는 단일 스레드 내에서 동시에 여러 작업을 처리할 수 있게 해주는 방식이다. 이 방식은 I/O 대기 시간 동안 다른 작업을 수행할 수 있어 시스템 자원을 더 효율적으로 사용할 수 있다. 멀티스레딩에 비해 오버헤드가 낮고, 스레드 컨텍스트 전환 비용이 없다. 또한, 비동기 I/O는 더 간단한 동시성 모델을 제공하며, GIL(Global Interpreter Lock)과 같은 멀티스레딩의 복잡한 문제를 피할 수 있다.

성능 비교에 따르면, 멀티스레딩은 실행 시간이 비동기 I/O보다 높지만, 네트워크 바운드 애플리케이션에서는 여전히 유용할 수 있다. 반면, 비동기 I/O는 실행 시간이 더 낮고, 더 간단한 동시성 모델을 제공하며, 멀티스레딩의 복잡한 문제를 피할 수 있다.

따라서, 애플리케이션의 특성과 요구 사항에 따라 멀티스레딩과 비동기 I/O 중 하나를 선택하는 것이 중요하다. 멀티스레딩은 병렬 처리가 필요한 경우에 적합하며, 비동기 I/O는 I/O 바운드 애플리케이션에서 더 효율적인 성능을 제공할 수 있다.


멀티스레딩과 비동기 I/O를 사용하는 것에는 여러 가지 잠재적인 문제점과 단점이 있다:

- **멀티스레딩**:
 - **복잡성**: 멀티스레딩은 여러 스레드를 관리하고 동기화해야 하므로, 코드가 복잡해질 수 있다. 이는 디버깅을 어렵게 만들 수 있다.
 - **컨텍스트 스위칭 비용**: 스레드 간의 컨텍스트 스위칭은 성능에 영향을 줄 수 있다. 특히, 단일 코어 시스템에서는 컨텍스트 스위칭 비용이 더 크게 될 수 있다.
 - **메모리 사용량**: 각 스레드는 자체의 스택을 가지고 있으므로, 메모리 사용량이 증가할 수 있다. 이는 특히 많은 수의 스레드를 사용하는 경우에 문제가 될 수 있다.

- **비동기 I/O**:
 - **복잡성**: 비동기 I/O는 콜백 기반의 코드를 작성해야 하므로, 코드의 흐름을 추적하기 어렵게 만들 수 있다. 이는 디버깅을 더 어렵게 만들 수 있다.
 - **성능**: 일부 경우에서는 비동기 I/O가 멀티스레딩보다 더 낮은 성능을 보일 수 있다. 예를 들어, 단일 스레드 모델에서는 멀티스레딩이 더 빠른 성능을 보일 수 있다. 이는 특히 I/O 바운드 작업이나 공유 자원에 대한 동기화가 필요한 경우에 해당한다.
 - **운영 체제 의존성**: 비동기 I/O의 구현은 운영 체제에 따라 다르게 동작할 수 있다. 일부 운영 체제는 여러 개의 비동기 I/O 요청을 큐에 넣는 것을 지원하지 않거나, 예상치 못한 방식으로 패킷을 처리할 수 있다.

따라서, 멀티스레딩과 비동기 I/O를 사용할 때는 이러한 잠재적인 문제점과 단점을 고려해야 한다. 애플리케이션의 요구 사항, 성능 요구 사항, 그리고 개발 환경에 따라 적절한 방식을 선택하는 것이 중요하다.