파일 송/수신 서비스에서 중요한 핵심은 프로토콜 설계와 데이터 처리 방식입니다. 특히, TCP의 특성상 데이터를 스트림 형태로 송신하고, 수신 측에서는 데이터를 끊어서 처리해야 합니다. 이 글에서는 효율적인 파일 송/수신 서비스 설계를 위한 구조와 프로토콜 설계를 정리합니다.
1. 기본 개념: send와 recv의 불일치
1.1 send와 recv의 동작 차이
- send:
- 송신 측에서 데이터를 한 번에 보낼 수 있음.
- 하지만 실제 네트워크에서는 이 데이터가 여러 개의 세그먼트로 분리되어 전달.
- recv:
- 수신 측에서 데이터를 조립하여 애플리케이션에 제공.
- 데이터를 조립하지 못한 경우 여러 번 recv 호출이 필요.
1.2 스트림 데이터 처리
- TCP는 데이터를 스트림으로 전달.
- 송신 측:
- 데이터를 순차적으로 전송하며, 스트림의 경계를 신경 쓰지 않음.
- 수신 측:
- 데이터를 헤더와 페이로드(Payload)로 나눠서 해석.
- recv를 여러 번 호출해 데이터를 조립해야 함.
2. 프로토콜 설계: 헤더와 페이로드
2.1 헤더와 페이로드 구조
- 데이터를 송신할 때는 헤더(Header)와 페이로드(Payload)로 나눔.
- 헤더(Header):
- 메타데이터(파일 이름, 크기 등)를 포함.
- 수신 측이 데이터를 해석하기 위한 기본 정보.
- 페이로드(Payload):
- 실제 전송하려는 데이터(파일 내용 등).
2.2 헤더 설계
(1) 기본 헤더
- 프로토콜의 기본 구조를 정의:
- 코드 값(Code): 확장 헤더와 데이터를 구분하기 위한 식별자.
- 데이터 크기(Size): 페이로드의 크기.
typedef struct {
uint16_t code; // 메시지 코드
uint32_t size; // 데이터 크기
} BasicHeader;
(2) 확장 헤더
- 기본 헤더의 정보를 기반으로 확장 정보를 포함:
- 파일 이름, 파일 크기 등.
- 예시:
typedef struct {
char filename[256]; // 파일 이름
uint32_t filesize; // 파일 크기
} FileHeader;
3. 송신 프로세스 설계
3.1 송신 흐름
- 헤더 생성:
- 송신할 데이터를 설명하는 기본 및 확장 헤더 생성.
- 헤더 송신:
- 기본 헤더와 확장 헤더를 송신.
- 페이로드 송신:
- 데이터를 64KB와 같은 고정 크기로 분할하여 송신.
3.2 송신 예제
// 1. 기본 헤더 전송
BasicHeader header = { CODE_FILE_TRANSFER, filesize };
send(sock, (char*)&header, sizeof(header), 0);
// 2. 확장 헤더 전송
FileHeader fileHeader = { "example.txt", filesize };
send(sock, (char*)&fileHeader, sizeof(fileHeader), 0);
// 3. 파일 데이터 송신
while ((bytesRead = fread(buffer, 1, BUFF_SIZE, file)) > 0) {
send(sock, buffer, bytesRead, 0);
}
4. 수신 프로세스 설계
4.1 수신 흐름
- 헤더 수신:
- 기본 헤더를 수신하여 확장 헤더와 데이터를 구분.
- 확장 헤더 수신:
- 확장 정보를 수신하고, 파일을 생성하거나 저장 공간 확보.
- 데이터 수신:
- 페이로드를 읽어 파일에 저장.
// 1. 기본 헤더 수신
BasicHeader header;
recv(sock, (char*)&header, sizeof(header), 0);
// 2. 확장 헤더 수신
FileHeader fileHeader;
recv(sock, (char*)&fileHeader, sizeof(fileHeader), 0);
// 3. 파일 데이터 수신
FILE* file = fopen(fileHeader.filename, "wb");
while ((bytesRead = recv(sock, buffer, BUFF_SIZE, 0)) > 0) {
fwrite(buffer, 1, bytesRead, file);
}
fclose(file);
5. 효율적인 프로토콜 설계 고려사항
5.1 데이터 단위 설계
- 헤더-페이로드 방식:
- 데이터를 단위별로 정의해 수신 측에서 쉽게 해석 가능.
- 코드 값 활용:
- 코드 값으로 데이터 유형을 구분:
- 예: CODE_FILE_TRANSFER, CODE_HEARTBEAT.
- 코드 값으로 데이터 유형을 구분:
5.2 성능 최적화
- 버퍼 크기 설정:
- 송수신 버퍼 크기를 적절히 설정(예: 64KB).
- 너무 작은 크기는 성능 저하, 너무 큰 크기는 메모리 낭비.
- 송신과 수신의 동기화:
- 수신 속도가 송신 속도를 따라잡을 수 있도록 설계.
- 네트워크 장애 처리:
- 타임아웃 및 재전송 메커니즘 구현.
5.3 데이터 무결성 검증
- 해시 값 사용:
- MD5, SHA256 등의 해시 값을 전송해 데이터 무결성 확인.
- ACK 기반 확인:
- 수신 측에서 송신 측으로 **확인 응답(ACK)**을 보내 데이터 손실 확인.
'프로그래밍 > 소켓 프로그래밍 입문' 카테고리의 다른 글
프로토콜이 적용된 파일 송신 서버 구조 (0) | 2025.01.21 |
---|---|
응용 프로그램 프로토콜 디자인: 헤더와 확장 헤더 설계해보기 (0) | 2025.01.21 |
TCP 연결의 본질과 연결 상태 확인 (0) | 2025.01.20 |
대표적인 TCP 장애 유형 (0) | 2025.01.20 |
Win32 API 기반의 파일 송/수신: TransmitFile을 중심으로 해보자! (0) | 2025.01.20 |