Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- dictionary
- Kotlin
- 파일입출력
- 진수변환기
- UI
- 콘솔 키보드 이벤트
- 소켓프로그래밍
- foreground service
- InfluxDB
- dart
- 코틀린
- 자료구조
- 문자열다루기
- 문자열 다루기
- background service
- socket
- FTP
- 딕셔너리
- 연결리스트
- vs code
- Mat 변수
- ws2_32.lib
- InfluxDBClient
- Android
- Linked List
- 메모리반환
- 문자열파싱
- C
- flutter
- vscode
Archives
- Today
- Total
무슨 일로 C 하셨습니까?
[C] 소켓 프로그래밍::FTP - 파일 전송 본문
FTP(File Transfer Protocol) 파일 전송 프로토콜에 맞게
우선은 파일 전송을 기능을 구현해보겠다.
네트워크를 통한 전송에 앞서
파일을 읽고 읽은 파일을 다시 기록하는 것 부터 시작해야겠다.
void f() {
FILE* from;
FILE* to;
char path[] = "C:\\Users\\OJJJ\\Desktop\\";
char filename[] = "lena.JPG";
char* target = StringAdd1(path, filename);
char* target2 = StringAdd1(path, "copy.JPG");
fopen_s(&from, target, "rb");
fopen_s(&to, target2, "wb");
free(traget);
free(target2);
fclose(from);
fclose(to);
}
from으로 부터 읽어서 to에 기록하도록 하겠다.
int bufsz = 1000;
char* buf = (char*)malloc(sizeof(char) * bufsz);
while (!feof(from)) {
fread(buf, 1, bufsz, from);
fwrite(buf, 1, bufsz, to);
}
free(buf);
단순하게 파일을 읽을 버퍼의 크기를 정한 후
그 크기만큼 계속 읽어서 복사하면 간단하게 만들 수 있다.
그러나 위와 같이 작성한다면 파일의 끝에 마주쳐도 bufsz 만큼 더 읽기 때문에 쓰레기 값이 추가로 더 읽힐 수 있다.
이를 처리하기 위해 다음과 같은 함수들을 사용한다.
fseek(from, 0, SEEK_END);
int filesz = ftell(from);
printf("file size is : %d\n", filesz);
fseek(from, 0, SEEK_SET);
fseek() 함수를 통해 파일의 포인터를 옮길 수 있고 ftell() 함수를 통해 해당 위치를 가져올 수도 있다.
이를 활용하여
int bufsz = 30;
char* buf = (char*)malloc(sizeof(char) * bufsz);
fseek(from, 0, SEEK_END);
int filesz = ftell(from);
printf("file size is : %d\n", filesz);
printf("buf size is : %d\n", bufsz);
fseek(from, 0, SEEK_SET);
while (!feof(from)) {
int cur = ftell(from);
if (cur == file_sz) break;
else if (cur + bufsz > filesz) {
int readsz = filesz - cur;
fread(buf, 1, readsz, from);
fwrite(buf, 1, readsz, to);
// buf[readsz] = 0;
}
else {
fread(buf, 1, bufsz, from);
fwrite(buf, 1, bufsz, to);
// buf[bufsz] = 0;
}
}
free(buf);
단순하게 파일을 읽어들이는 것이 아닌
파일의 남은 크기와 지금까지 읽은, 그리고 앞으로 읽어야할 크기를 적절히 비교해서 읽어주면 되겠다.
void f() {
FILE* from;
FILE* to;
char path[] = "C:\\Users\\OJJJ\\Desktop\\";
char filename[] = "lena.jpg";
char* target = StringAdd1(path, filename);
char* target2 = StringAdd1(path, "copy.jpg");
int err;
err = fopen_s(&from, target, "rb");
err = fopen_s(&to, target2, "wb");
int bufsz = 1000;
char* buf = (char*)malloc(sizeof(char) * bufsz);
fseek(from, 0, SEEK_END);
int filesz = ftell(from);
printf("file size is : %d\n", filesz);
printf("buf size is : %d\n", bufsz);
fseek(from, 0, SEEK_SET);
int writebuf = 0;
while (!feof(from)) {
int cur = ftell(from);
printf("copying .... [%d/%d]\n", cur, filesz);
if (cur == filesz) break;
else if (cur + bufsz > filesz) {
int readsz = filesz - cur;
fread(buf, 1, readsz, from);
fwrite(buf, 1, readsz, to);
writebuf += readsz;
// buf[readsz] = 0;
}
else {
fread(buf, 1, bufsz, from);
fwrite(buf, 1, bufsz, to);
writebuf += bufsz;
// buf[bufsz] = 0;
}
}
printf("write file size is : %d\n", writebuf);
free(buf);
free(target);
free(target2);
fclose(from);
fclose(to);
}
최종 코드는 다음과 같으며 동작 시켜 보면
아주 잘 복사된 것을 볼 수 있다.
이 코드를 기반으로 네트워크 상에서 해당 작업이 이루어질 수 있도록 Sender와 Reciver를 만들면 되겠다.
// 실제 데이터 전송 하는 함수
void Sender_Data(SOCKET _sock, char* _path, char* _filename) {
FILE* stream;
char* target_ = StringAdd1(_path, _filename);
fopen_s(&stream, target_, "rb");
int bufsz = PACKSZ;
char* buf_ = (char*)malloc(sizeof(char) * bufsz);
fseek(stream, 0, SEEK_END);
int filesz = ftell(stream);
printf("file size is : %d\n", filesz);
sprintf(buf_, "%d\0", filesz);
send(_sock, buf_, StringLength(buf_), 0);
fseek(stream, 0, SEEK_SET);
int writebuf = 0;
int readsz, msglen;
char flag[100];
while (!feof(stream)) {
int cur = ftell(stream);
if (cur == filesz) break;
else if (cur + bufsz > filesz) readsz = filesz - cur;
else readsz = bufsz;
fread(buf_, 1, readsz, stream);
//printf("sending .... %d[%d/%d]", readsz, cur, filesz);
while (1) {
if ((msglen = send(_sock, buf_, readsz, 0)) == -1) {
//puts("disconnected!! fin");
break;
}
if ((msglen = recv(_sock, flag, 100, 0)) == -1) {
//puts("disconnected!! fin");
break;
}
flag[msglen] = 0;
if (StringCompare(flag, "go") == COMPARE_SAME) {
//puts("send success");
break;
}
else if (StringCompare(flag, "no") == COMPARE_SAME) {
//puts("send fail... retry");
continue;
}
else {
//puts("packet err - resending");
}
}
if (msglen == -1) {
break;
}
writebuf += readsz;
}
printf("send complete .... %s%s", _path, _filename);
free(buf_);
free(target_);
fclose(stream);
}
// 실제 데이터를 받는 함수
void Receiver_Data(SOCKET sock) {
FILE* stream = NULL;
char path[] = "C:\\Users\\OJJJ\\Desktop\\";
char filename[] = "copy.jpg";
char* target_ = StringAdd1(path, filename);
fopen_s(&stream, target_, "wb");
int bufsz = PACKSZ;
char* buf_ = (char*)malloc(sizeof(char) * bufsz);
int sz, filesz, cur;
recv(sock, buf_, PACKSZ, 0);
sscanf(buf_, "%d", &filesz);
//printf("file size is : %d\n", filesz);
cur = 0;
while (1) {
sz = recv(sock, buf_, PACKSZ, 0);
//printf("receive data : %d ..... ", sz);
if (sz == -1) {
//printf("err, fin.\n");
break;
}
else if (sz != bufsz) {
if (sz + cur < filesz) {
send(sock, "no", StringLength("no"), 0);
//printf("err retry (%d)\n", sz);
continue;
}
}
send(sock, "go", StringLength("go"), 0);
fwrite(buf_, 1, sz, stream);
cur += sz;
//printf("[%d/%d]\n", cur, filesz);
if (cur > filesz) {
//printf("▒▒▒▒data over▒▒▒▒\n");
break;
}
else if (filesz == cur) {
printf("recv complete ------> ");
printf("%s%s\n", path, filename);
break;
}
}
free(buf_);
free(target_);
fclose(stream);
}
각각 Sender와 Receiver 이다.
파라미터로 받는 Socket은 이전에 연결한 소켓을 넣어주면 되겠다.
아직 파일 이름이나 패킷 크기 등은 고정적인 값을 사용했기 때문에
앞으로 이것을 동적으로 설정할 수 있도록 만들어야겠다.
'C - 이걸 굳이? > FTP' 카테고리의 다른 글
[C] 소켓 프로그래밍::FTP - UI(2) (0) | 2021.04.25 |
---|---|
[C] 소켓 프로그래밍::FTP - UI(1) (0) | 2021.03.31 |
[C] 소켓 프로그래밍::FTP - 디렉토리 (0) | 2021.01.11 |
[C] 소켓 프로그래밍::FTP - 연결 (0) | 2020.12.04 |
[C] 소켓 프로그래밍::FTP (0) | 2020.12.03 |
Comments