무슨 일로 C 하셨습니까?

[C] 소켓 프로그래밍::FTP - 파일 전송 본문

C - 이걸 굳이?/FTP

[C] 소켓 프로그래밍::FTP - 파일 전송

OJJJ 2020. 12. 10. 20:41

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);
}

최종 코드는 다음과 같으며 동작 시켜 보면

 

아주 잘 복사된 것을 볼 수 있다.

 

이 코드를 기반으로 네트워크 상에서 해당 작업이 이루어질 수 있도록 SenderReciver를 만들면 되겠다.

 


// 실제 데이터 전송 하는 함수
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);
}

각각 SenderReceiver 이다.

파라미터로 받는 Socket은 이전에 연결한 소켓을 넣어주면 되겠다.

 

아직 파일 이름이나 패킷 크기 등은 고정적인 값을 사용했기 때문에

 

앞으로 이것을 동적으로 설정할 수 있도록 만들어야겠다.

Comments