무슨 일로 C 하셨습니까?

[C] 소켓 프로그래밍::FTP - 디렉토리 본문

C - 이걸 굳이?/FTP

[C] 소켓 프로그래밍::FTP - 디렉토리

OJJJ 2021. 1. 11. 00:22

카카오톡으로 사진을 넘겨주듯 보내는 쪽에서 선택해서 파일을 넘겨주면 좋겠지만

 

받는 쪽에서 원하는 파일을 선택하게 만들고싶다.

 

그것도 한정된 몇개의 파일이 상대방 컴퓨터 내의 모든 파일을 대상으로

 

그것을 가능하도록 만들어보겠다.


#include<stdio.h>
#include<io.h>

void f() {
    struct _finddata_t target;
    intptr_t handle;
    int result;

    handle = _findfirst("*.*", &target);

    if (handle == -1) return;

    while (1)
    {
        printf("%s[%d]\n", target.name,target.size);
        result = _findnext(handle, &target);
        
        if (result == -1) break;
    }

    _findclose(handle);
    return;
}

 

여러 오픈 소스들을 토대로 만든 코드로 

 

실행하면 다음과 같이 현재 위치 상 폴더 디렉토리를 볼 수 있다.

(기본 경로는 프로젝트 내 실행파일(.exe)이 있는 폴더이다.)

 

파일 이름 뿐만 아니라 확장자, 파일 크기 까지 알 수 있고 크기로 마루어 보아 해당 파일이 폴더인지 파일인지 구분할 수 있을 것으로 보인다.

 

이렇게 알아낸 목록을 상대방에게 보여주면 원하는 파일을 선택할 수 있을 것이다.


먼저 읽어온 디렉토리에 대한 정보를 담을 수 있는 자료형을 정의하자

typedef struct _filedata {
	int idx;
	char* name;
	int size;
	struct _filedata* link;
}filedata;

추후에 어떻게 추가될지는 모르겠지만 일단은 파일의 순번, 이름, 크기를 담도록 했다.

 

 

filedata* GetDirectory(char* _path) {
	struct _finddata_t target;
	intptr_t handle;

	char* file_ = StringAdd1(_path, "*.*");
	handle = _findfirst(file_, &target);

	if (handle == -1)
	{
		printf("empty\n");
		return;
	}

	int idx = 0;
	filedata* hptr = NULL;
	filedata* node, * ptr;
	
	while (1)
	{
		node = (filedata*)malloc(sizeof(filedata));
		node->idx = idx++;
		node->size = target.size;
		node->name = StringAdd1(target.name, "");
		node->link = NULL;

		if (hptr == NULL) { 
			hptr = node;
			ptr = node;
		}
		else {
			ptr->link = node;
			ptr = node;
		}

		if (_findnext(handle, &target) == -1) break;
	}

	_findclose(handle);
	free(file_);
	return hptr;
}

읽어온 파일을 자료형에 맞게 저장하는 함수이다.

( 각각  _자료형 : 매개변수, 자료형_ : 동적할당을 한 변수로 메모리 반환을 해야하는 변수 를 의미 )

 

데이터의 index는 그저 컴퓨터가 파일을 읽은 순서대로 부여한다.

 

void Sender_Directory(SOCKET _sock, filedata* _files){
	filedata* ptr = _files;
	char msg[100];

	while (1)
	{
		if (ptr == NULL) {
			send(_sock, "fin", 3, 0);

			recv(_sock, msg, 100, 0);
			msg[4] = 0;

			printf("last msg : %s\n", msg);
			break;
		}

		sprintf(msg, "%d,%s,%d\n", ptr->index, ptr->name, ptr->size);

		if (send(_sock, msg, StringLength(msg), 0) == -1) {
			puts("send err");
			break;
		}
		printf("send data is : %s\n", msg);
		
		ptr = ptr->link;
	}
	return;
}

읽어온 디렉토리를 전달하는 함수이다.

 

앞서 읽어서 저장한 파일 목록을 차례대로 연결된 소켓에 전송하면 된다.

 

  • 전송 포맷은 "인덱스, 파일명, 파일크기"으로 csv 포맷으로 보내도록 하겠다.
void Receiver_Directory(SOCKET sock) {
	char msg[100];
	int msgsz;
	while (1) {
		msgsz = recv(sock, msg, 100, 0);
		msg[msgsz] = 0;

		char** files_ = StringParser(msg, ",");
		printf("recv is : %s,%s,%s\n", files_[0], files_[1], files_[2]);
		matfree(files_);
	}
}

수신 쪽은 더 간단하다. 목록을 받아내기만 하면 된다. 

 

  • 송수신 포맷은 당연히 csv포맷을 사용할 것이기 때문에 데이터를 전처리하는 로직으로 구현하진 않아도 되겠다.

 

간단하게 동작시켜 보자

 

// 송신자(서버) 몸체
void Sender() {
	SOCKET sock = Sender_Connector();
	char msg[100];
	char path[] = "C:\\Users\\OJJJ\\Desktop\\";

	filedata* files_ = GetDirectory(path);
	Sender_Directory(sock, files_);

	int len = recv(sock, msg, 100, 0);
	msg[len] = 0;
	printf("msg rcv : %s\n", msg);

	int index;
	sscanf(msg, "%d", &index);
	printf("file searching : %d\n", index);
	filedata* ptr = files_;
	for (int i = 1; i <= index; i++) {
		ptr = ptr->link;
	}
	printf(".... is %s", ptr->name);

	Sender_Data(sock, path, ptr->name);
}

파일을 주는 쪽이다. 상대방에게 디렉토리를 보낸 후 요청 받은 파일을 전송하도록 한다.

 

// 수신자(클라이언트) 몸체
void Receiver() {
	SOCKET sock = Receiver_Connector();
	char msg[100];
	char path[] = "C:\\Users\\OJJJ\\Desktop\\";
	char filename[] = "copy.jpg";

	Receiver_Directory(sock);

	int num;
	printf("select >> ");
	scanf("%d", &num);

	sprintf(msg, "%d\n", num);

	send(sock, msg, StringLength(msg), 0);

	Receiver_Data(sock, path, filename);
}

파일을 받는 쪽이다. 받은 파일 목록을 보고 해당 파일을 선택해서 상대방에게 요청하도록 한다.

 

 

 

받은 디렉토리에서 lena.jpg를 요청해서 다운 받는데 성공했다.

 


송/수신 프로세스 속도에 따라서 약간의 문제가 발생한다.

송신 측이 늦는 건 상관이 없는데 수신 측에 딜레이가 발생하면 송신 패킷이 쌓이는 문제를 야기할 수 있다.

 

위 실행결과를 보면 11, 14번 파일이 정상적으로 수신되지 않은 것을 볼 수 있다. 이러한 문제가 발생하지 않도록 해결해야한다.

 

------- 송신부
while (1) {
	if (send(sock_, msg, StringLength(msg), 0) == -1) break;
	len = recv(sock_, ans, PACKSZ, 0);
	if (len == -1) break;

	ans[len] = 0;
	if (StringCompare(ans, "go") == COMPARE_SAME) break;
}
        
-------- 수신부
msgsz = send(sock_, "go", 2, 0);

그냥 단순하게 수신부에서 정상적으로 수신했을때만 송신부가 다음 파일을 송신하게 만들면 되겠다.

 

 

또한 상위, 하위 폴더로의 경로 이동이 가능해야한다.

 

Comments