본문 바로가기
C&C++/소켓 프로그래밍

[C/C++] TCP/IP 대용량 파일 전송 프로그램 (서버, 클라이언트)

by DeveloperJW 2022. 8. 22.

2022.07.22 - [C&C++/소켓 프로그래밍] - [C/C++] VSCode 윈도우 기반 서버, 클라이언트 예제

클라이언트 -> 서버로 파일을 전송하는 프로그램입니다.

 

파일 처리 방법

파일은 결국 바이너리 파일이라 읽고 쓰기 위해서는 인코딩 과정이 필요하다

그래서 파일을 주고 받을 때도 인코딩을 해서 파일에 쓰고 다시 디코딩 해서 읽어야 한다

 

파일 전송 과정-서버 측

1. 클라이언트가 파일을 요청한다. 이 때 파일 이름은 이진 바이트 스트림 데이터 형태로 온다

2. 파일 이름을 일반 문자열로 변환한다

3. open으로 해당 파일을 연다

4. read로 파일을 1024바이트 읽는다

5. 파일이 빈 문자열일 때까지 계속 1024바이트씩 읽고 전송한다(각 라인을 읽어서 리턴해야 하기 때문에 끊어서)

 

파일 수신 과정-클라이언트 측

1. 서버에 파일을 요청한다

2. 서버가 파일을 1024바이트씩 보내기 시작한다

3. 데이터가 없을 때까지 write로 1024바이트씩 써서 원하는 폴더+파일 이름으로 파일을 만든다

 

소스코드를 보겠습니다.

 

- 서버

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <windows.h>
#include <process.h>
#include <iostream>

#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN

#pragma comment(lib, "Ws2_32.lib")
#define BUF_SIZE 1024

using namespace std;

SOCKET s_listen, s_accept;

int main(int argc, char **argv)
{
    WSADATA wsaData;
    struct sockaddr_in local_addr;

    if (argc != 3)
    {
        printf("Command parameter does not right.\n");
        exit(1);
    }

    WSAStartup(MAKEWORD(2, 2), &wsaData);

    if ((s_listen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Socket Creat Error.\n");
        exit(1);
    }

    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    local_addr.sin_port = htons(atoi(argv[1]));

    if (bind(s_listen, (SOCKADDR *)&local_addr, sizeof(local_addr)) == SOCKET_ERROR)
    {
        printf("Socket Bind Error.\n");
        exit(1);
    }

    if (listen(s_listen, 5) == SOCKET_ERROR)
    {
        printf("Socket Listen Error.\n");
        exit(1);
    }

    printf("This server is listening... \n");

    struct sockaddr_in client_addr;
    int len_addr = sizeof(client_addr);
    int readBytes;
    long file_size;
    long totalReadBytes;

    char buf[BUF_SIZE];

    FILE *fp;
    fp = fopen(argv[2], "wb");

    s_accept = accept(s_listen, (SOCKADDR *)&client_addr, &len_addr);

    if (s_accept)
    {
        printf("Connection Request from Client [IP:%s, Port:%d] has been Accepted\n",
               inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));

        readBytes = recv(s_accept, buf, BUF_SIZE, 0); // 파일 크기를 받아옴
        file_size = atol(buf);                        // 파일 크기 형변환 char -> long

        totalReadBytes = 0;
        printf("In progress: %d/%dByte(s) [%d%%]\n", totalReadBytes, file_size, (totalReadBytes * 100 / file_size));

        while (totalReadBytes != file_size)
        {
            readBytes = recv(s_accept, buf, BUF_SIZE, 0); // 버퍼 사이즈만큼 클라이언트에서 데이터 받기
            totalReadBytes += readBytes;                  // 받은 데이터 누적
            printf("In progress: %d/%dByte(s) [%d%%]\n", totalReadBytes, file_size, (totalReadBytes * 100 / file_size));
            fwrite(buf, sizeof(char), readBytes, fp); // 파일에 받은 데이터 쓰기

            if (readBytes == SOCKET_ERROR)
            {
                printf("File Receive Error");
                exit(1);
            }
        }

        closesocket(s_accept);
        printf("File recieve successed");
    }
    else
    {
        printf("File Accept Error");
    }

    closesocket(s_listen);
    WSACleanup();

    return 0;
}

 

 

- 클라이언트

#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN

#pragma comment(lib, "Ws2_32.lib")
#define BUF_SIZE 1024

int main(int argc, char **argv)
{
    int num;
    WSADATA wsaData;
    struct sockaddr_in server_addr;
    SOCKET s;

    if (argc != 4)
    {
        printf("Command parameter does not right.\n");
        exit(1);
    }

    WSAStartup(MAKEWORD(2, 2), &wsaData);

    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Socket Creat Error.\n");
        exit(1);
    }

    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    server_addr.sin_port = htons(atoi(argv[2]));

    if (connect(s, (SOCKADDR *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR)
    {
        printf("Socket Connection Error.\n");
        exit(1);
    }

    printf("File Send Start\n");

    int sendBytes;
    long totalSendBytes;
    long file_size;
    char buf[BUF_SIZE];
    char SendFilesize;

    FILE *fp;
    fp = fopen(argv[3], "rb"); // 파일 읽기 모드
    if (fp == NULL)
    {
        printf("File not Exist");
        exit(1);
    }

    fseek(fp, 0, SEEK_END); // 파일 포인터의 마지막 위치를 기준으로 이동
    file_size = ftell(fp);  // 파일 포인터의 위치로 파일의 크기 구하기
    fseek(fp, 0, SEEK_SET); // 파일의 처음 위치를 기준으로 이동
    totalSendBytes = 0;

    printf("In progress: %d/%dByte(s) [%d%%]\n", totalSendBytes, file_size, (totalSendBytes * 100 / file_size));

    SendFilesize = _snprintf(buf, sizeof(buf), "%d", file_size); // snprintf()는 사이즈를 체크할 수 있기 때문에
                                                                 // 사이즈 커져 생길 수 있는 오류를 미리 예방할 수 있다.
    send(s, buf, SendFilesize, 0);                               // 파일 크기 전송

    while ((sendBytes = fread(buf, sizeof(char), sizeof(buf), fp)) > 0) // 남은 데이터가 없을 때 까지 반복
    {
        send(s, buf, sendBytes, 0);  // 버퍼사이즈 만큼 서버에 데이터 보냄
        totalSendBytes += sendBytes; // 보낸 데이터 누적
        printf("In progress: %d/%dByte(s) [%d%%]\n", totalSendBytes, file_size, (totalSendBytes * 100 / file_size));
    }
    printf("File send successed");

    closesocket(s);
    WSACleanup();

    return 0;
}

 

 

댓글