Coding & Programming/Network(TCP, IP, UDP)

[C언어/C++] Winsock 멀티스레드 1:N 대용량 파일 전송(송수신) 서버/클라이언트 구현하기(TCP/IP Windows socket) #2/3

mainCodes 2021. 4. 27. 10:37

[C언어/C++] Winsock 멀티스레드 1:N 대용량 파일 전송(송/수신) 서버/클라이언트 구현하기(TCP/IP Windows socket) #2/3

 

안녕하세요 JollyTree입니다 (•̀ᴗ•́)و

 

지난번에는 서버, 클라이언트에 대해 구상한 프로그램 개념도, 통신 규약, 서버/클라이언트 통신 절차에 대해 포스팅하였습니다. 이번에는 구상한 내용을 토대로 구현한 클라이언트 프로그램에 대한 설명과 소스코드를 포스팅합니다.

 

클라이언트는 전송하고자 하는 파일의 파일 크기와 데이터를 읽은 뒤 "파일명&파일 데이터" 형태로 패킷 데이터를 우선 구성합니다. 그런 다음 소켓을 초기하고 서버에 연결한 후 패킷 헤더(PACKET_HEADER)를 구성하여 서버에 패킷 헤더 5바이트를 먼저 전송합니다.  

 

typedef struct {
  char type;
  int len;
PACKET_HEADER;
...

len = (long)htonl((long)packet_len);
send_buf[0] = packet_type;
memcpy(send_buf + 1, &len, sizeof(len));
send_len = send(sock, send_buf, 5, 0);


이후 패킷 헤더의 len 크기만큼 패킷 데이터를 1024바이트씩 나누어 전송하며 전송 시 미전송 여부를 체크합니다. 만약 보내고자 했던 크기보다 작게 전송(send) 되었다면 재시도합니다.

클라이언트 전체 소스코드(Client Source Code):
※ 소스코드는 Visual Studio 2019 환경에서 컴파일되었습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
#pragma warning(disable : 4996)
#pragma comment(lib,"ws2_32.lib")
#include <stdio.h>
#include <WinSock2.h>
 
#define        PACKET_SIZE        1024
#define        PACKET_TYPE        0x11
#define        FILE_DELIMITER    '&'
 
typedef struct {
    char type;
    int len;
} PACKET_HEADER;
 
int get_filelen(char* filename)
{
    FILE* fp = NULL;
    int filelen;
 
    if ((fp = fopen(filename, "rb")) == NULL)
    {
        puts("fopen 에러");
        return -1;
    }
    fseek(fp, 0, SEEK_END);
    filelen = ftell(fp);
 
    fclose(fp);
    return filelen;
}
 
int read_file(char* path, char* buf, int len)
{
    FILE* fp = NULL;
    int readlen;
 
    if( (fp = fopen(path, "rb")) == NULL){
        puts("fopen 에러");
        return -1;
    }
    buf[len] = NULL;
    if((readlen = fread(buf, sizeof(char), len, fp)) < len){
        fclose(fp);
        puts("fread 에러");
        return -1;
    }
 
    fclose(fp);
    return 0;
}
 
int send_socket(unsigned int sock, char packet_type, char* packet_buf, int packet_len)
{
    char send_buf[PACKET_SIZE+1];
    int len = 0, send_len = 0;
 
    memset(send_buf, 0sizeof(send_buf));
 
    len = (long)htonl((long)packet_len);
    send_buf[0= packet_type;
    memcpy(send_buf + 1&len, sizeof(len));
    send_len = send(sock, send_buf, 50);
 
    for (;;)
    {
        memset(send_buf, 0sizeof(send_buf));
        if (packet_len <= 0)
            break;
 
        if (packet_len <= PACKET_SIZE) 
        {
            memcpy(send_buf, packet_buf, packet_len);
            if ((send_len = send(sock, send_buf, packet_len, 0)) < 0)
                return -1;
            if (send_len == packet_len)
                break;
 
            packet_buf += send_len;
            packet_len -= send_len;
        }
        else {
            memcpy(send_buf, packet_buf, PACKET_SIZE);
            if ((send_len = send(sock, send_buf, PACKET_SIZE, 0)) < 0)
                return -1;
 
            packet_buf += send_len;
            packet_len -= send_len;
        }
    }
 
    return 0;
}
 
int send_packet(char* server_ip, int server_port, char* packet_buf, int packet_len)
{
    WSADATA wsadata;
    SOCKET sock;
    struct sockaddr_in server_address;
    int len = 0;
 
    if (WSAStartup(MAKEWORD(22), &wsadata) != 0) {
        puts("WSAStartup 에러");
        return -1;
    }
 
    if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
    {
        puts("socket 에러.");
        return -1;
    }
 
    memset((char*)&server_address, 0sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = inet_addr(server_ip);
    server_address.sin_port = htons(server_port);
    if(connect(sock, (struct sockaddr*)&(server_address), sizeof(server_address)) < 0)
    {
        puts("connect 에러.");
        return -1;
    }
 
    puts(" >> 클라이언트 초기화 및 연결 완료!!");
 
    if (send_socket(sock, PACKET_TYPE, packet_buf, packet_len) != 0)
        return -1;
    if (recv(sock, (char*)&len, 40< 0)
        return -1;
 
    closesocket(sock);
    return 0;
}
 
int main(int argc, char* argv[])
{
    char* filename = NULL
    char* server_ip = NULL;
    int server_port = 1234;
    char* filebuf = NULL;
    int filelen = 0;
    char* packet_buf = NULL;
    int packet_len = 0;
 
    if (argc != 4)
    {
        puts("사용법 : fileclient [서버IP][서버포트번호][전송파일이름]\n");
        puts("     ex) fileserver 192.168.1.100 1234 maincodes.jpg\n\n");
        exit(0);
    }
 
    server_ip = argv[1];
    server_port = atoi(argv[2]);
    filename = argv[3];
 
 
    filelen = get_filelen(filename);
    if ((filebuf = (char*)calloc(1, filelen + 1)) == NULL)
        return -1;
    if (read_file(filename, filebuf, filelen) < 0){
        free(filebuf);
        return -1;
    }
    printf(" >> 전송 파일명 : %s 파일크기 : %d바이트\n", filename, filelen);
 
    if ((packet_buf = (char*)calloc(filelen + MAX_PATH, sizeof(char))) == NULL)
    {
        free(filebuf);
        return -1;
    }
    sprintf(packet_buf, "%s%c", filename, FILE_DELIMITER);
    memcpy(packet_buf + strlen(filename) + sizeof(FILE_DELIMITER), filebuf, filelen);
    packet_len = strlen(filename) + sizeof(FILE_DELIMITER) + filelen;
 
    printf(" >> 전송 패킷 길이 : %d바이트\n", packet_len);
    if (send_packet(server_ip, server_port, packet_buf, packet_len) != 0){
        puts("send_packet 에러");
        free(filebuf); free(packet_buf);
   }else
    puts(" >> 파일 전송 완료!!");
 
    free(filebuf); free(packet_buf);
    return 0;
}
 
cs


파일 읽기, 파일 길이 구하기, 소켓 초기화 등의 클라이언트 소스코드에 있는 내용과 추가 예제들은 여기 블로그에 기록되어 있으니 참고하세요. 이상 JollyTree였습니다. (•̀ᴗ•́)و