무슨 일로 C 하셨습니까?

[C] 소켓 프로그래밍::FTP - UI(2) 본문

C - 이걸 굳이?/FTP

[C] 소켓 프로그래밍::FTP - UI(2)

OJJJ 2021. 4. 25. 00:05

송신부가 디렉토리를 전송하면 반대쪽 수신부에서는 디렉토리 내용을 받아서

 

사용자가 원하는 파일을 선택할 수 있도록 UI로 구현해야한다.

 

디렉토리 내용을 전송하는 함수를 만들었고, 콘솔 창에서의 UI를 만들었으니

 

두가지 함수를 합쳐서 구현해보자.


우선 디렉토리에 포함된 파일을 UI에 그릴 수 있도록 문자열 형태로 변환시키는 함수를 만든다.

char* File2String(filedata file_) {
	// [  ] n: | filename | extension | size 
	char temp[100];
	char* _extension = FileExtension(file_);
	sprintf(temp, "%2d: | %16.16s | %6s | %-d",
		file_.index, file_.name, _extension, file_.size);
	
	char* _result = StringCopy(temp);

	autofree(_extension);
	return _result;
}

// [  ] n: | filename | extension | size 

  • [  ]           : 해당 파일이 선택됐음을 알리는 포인터가 들어갈 자리
  •  n            : 파일의 인덱스(순번) 
  • filename   : 파일의 이름
  • extension  : 파일의 확장자
  • size         : 파일의 크기 ( 디렉토리 파일 : size == 0 )

파라미터로 입력된 파일 구조체를 문자열 형태로 변환하는 함수를 통해 해당 파일을 UI에 그릴 수 있게 된다.

 

위 함수를 사용하여 디렉토리(파일 구조체 배열)를 입력하여 UI를 구성할 화면에 추가시키는 함수를 만들면 화면구성은 완성된다.

void _Convert_FileToLine(filedata* files, Painter** core) {
	for (filedata* ptr = files; ptr != NULL; ptr = ptr->link) {
		char* a = File2String(*ptr);
		Painter_Input(*core, a);
	}
}

 

위 함수를 사용하면

// 수신자(클라이언트) 몸체
void Receiver() {
	SOCKET sock = Receiver_Connector();
	char msg[PACKSZ];
	char* _mypath = StringCopy("C:\\Users\\OJJJ\\Desktop\\copy\\");
	char* _urpath=NULL;
	int len;

	int flag = access(_mypath, 0);

	filedata* _files = NULL;
	send(sock, "!", 1, 0);
	Painter* _core = NULL;

	while (1) {
		len = recv(sock, msg, PACKSZ, 0);
		msg[len] = 0;

		// 디렉토리 수신 플레그
        // !C:\\Users\\user1 ==> 상대방의 기본 파일 경로
		if (msg[0] == '!') {
			autofree(_urpath);
			_urpath = StringCopy(msg + 1);
			_files = Receiver_Directory(sock);
		}
		
        	// 수신한 파일이 존재하는 경우
		if (_files != NULL) {
			_core = (Painter*)malloc(sizeof(Painter));
			_core->line = NULL;
			_Convert_FileToLine(_files, &_core);
		}
        
		select = Curser_Run(*_core,_urpath);
       		...
	}
}

다음과 같이 구성하면 송신쪽에서 디렉토리를 전송할때마다 수신쪽에서 디렉토리를 수신받아서 콘솔 UI에 출력할 수 있을 것이다.

 


콘솔 UI에서 파일을 선택할 경우 해당 파일이 전송되도록, 

 

디렉토리를 선택하면 해당 디렉토리를 열람할 수 있도록 구현해야 한다.

 

int Curser_Run(Painter Paint_,char* title_) {
	...

	while (1) {
		...

		// 엔터
		else if (key == 13) {
			Line* target = Line_Get(Paint_, selected);
			
			int result;
			sscanf(target->data, "[%d", &result);

			COORD pos = { 0,linenum + 5 };
			SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);

			return result;
		}
	}
}

지난번에 제작한 함수에서 엔터 이벤트를 추가하도록 하겠다.

더보기
Line* Line_Get(Painter Paint_, int index) {
	Line* ptr = Paint_.line;
	for (int i = 0; i < index; i++) {
		ptr = ptr->next;
	}
	return ptr;
}

출력 화면에서 선택된 특정 라인의 정보를 가져오는 함수를 만들어서 

해당 라인의 인덱스를 반환시키도록 하면 되겠다.

 

  • sscanf(target->data, "[%d", &result); : 라인 문자열에서 정수(인덱스)입력을 받아와서 인덱스를 구함
  • n: | filename | extension | size     : 라인 문자열의 형식
  • 왼쪽부터 입력을 받아서 정수만 입력으로 받음.
  • 올바른 문법은 아니니 이런 코드는 쓰지말자
// 수신자(클라이언트) 몸체
void Receiver() {
	...

	while (1) {
		...
		if (msg[0] == '!') {
			autofree(_urpath);
			_urpath = StringCopy(msg + 1);
			_files = Receiver_Directory(sock);
		}

		if (_files != NULL) {
			//_core.free;
			// 메모리 정리부분

			_core = (Painter*)malloc(sizeof(Painter));
			_core->line = NULL;
			_Convert_FileToLine(_files, &_core);
		}

		int select;
		while (1) {
			select = Curser_Run(*_core,_urpath);
			filedata target = GetFile(_files, select);


			if (target.size == 0) {
				// 디렉토리 선택한 경우
				Receiver_Directory_Request(sock, _urpath, target.name);
				break;
			}
			
            		// 파일 요청 플래그@ 와 함께 현재 경로(송신측)에서 특정 파일의 인덱스로 파일 요청
			sprintf(msg, "@%d\n", select);

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

			Receiver_Data(sock, _mypath, target.name);
		}
	}

	
}

바뀐 UI함수에 따라서 유저의 선택이 파일이나 디렉토리냐에 따라서 다르게 동작시켜준다.

더보기
filedata GetFile(filedata* files_,int index_) {
	for (filedata* ptr = files_; ptr != NULL; ptr = ptr->link) {
		if (ptr->index == index_) {
			return *ptr;
		}
	}
}

파일 리스트에서 특정 인덱스의 파일을 찾는 함수를 활용 

선택된 파일의 정보를 구조체로 가져온다.

 

void Receiver_Directory_Request(SOCKET sock_, char* path_, char* target_) {
	if (StringCompare(target_, "..") == COMPARE_SAME) {
		char* _temp = StringAdd1("!",path_);
		int* _index = StringFinder(_temp, "\\");
		
		
		if (_temp[StringLength(_temp)-1] == '\\') _index[0]--;

		_temp[_index[_index[0]]+1] = 0;

		send(sock_, _temp, StringLength(_temp), 0);
	
		autofree(_temp);
		autofree(_index);
	}
	else {
		char* _file = StringAdd1(path_, target_);
		char* _dir = StringAdd1(_file, "\\");
		char* _temp = StringAdd1("!", _dir);

		send(sock_, _temp, StringLength(_temp), 0);

		autofree(_dir);
		autofree(_temp);
		autofree(_file);
	}
}

선택된 파일이 디렉토리인 경우 상대방의 파일 경로에 해당 파일의 이름을 더해 경로로 요청한다.

디렉토리를 요청한 경우 반복문을 나와서 다시 디렉토리를 수신받아 화면을 그리고 유저의 선택을 유도하며

 

파일을 요청한 경우 파일을 수신받고 다음 선택을 유도하게 만들면 되겠다.

 


송신부의 변경된 코드는

void Sender() {
	SOCKET sock = Sender_Connector();
	char msg[PACKSZ];
	char* _path = StringCopy("C:\\Users\\"/*\\OJJJ\\Desktop\\"*/);
	int len;
	filedata* files_ = NULL;
	while (1) {
		len = recv(sock, msg, PACKSZ, 0);
		msg[len] = 0;
		printf("recv msg is : %s[%d]", msg,len);
        
        // 디렉토리 요청 플래그
        // 해당 디렉토리로 경로를 수정함
		if (msg[0] == '!') {
			printf(" - directory send process start \n");
			if (len > 2) {
				autofree(_path);
				_path = StringCopy(msg + 1);
			}
			
            // 디렉토리 안의 파일 리스트를 전송하기 전
            // 현재 경로 정보를 플래그와 함께 전송함
			sprintf(msg, "!%s", _path);
			send(sock, msg, StringLength(msg), 0);
			printf("path is : %s\n", msg);

			files_ = GetDirectory(_path);
			Sender_Directory(sock, files_);
			
		}
        // 특정 파일을 요청받은 경우
        // 파일 전송함수를 실행
		else if (msg[0] == '@') {
			int index;
			sscanf(msg, "@%d", &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);;
		}
	}
}

 

수신부가 반복문으로 선택에 따라 각각의 플래그에 따라서 각 행동을 실행하도록 만들어 주면 되겠다.

 


github.com/tojbabo/making_with_C

 

Comments