🧼C, C++/네트워크

소켓 파일전송 C++ #3

Mawile 2021. 1. 20.
728x90

 

개발환경 >> Visual Studio

언어 >> C++20

운영체제 >> Windows10

 


 

안녕하세요!!!

이번에는 엄청난 파일전송속도와 안정성을 가지고 왔습니다.

나중에는 이것보다 더 체계적으로 설계해서 속도와 안정성을 더 올려보겠습니다!

일단 현재 안정성은 최고치구요.

속도만 더 올리고싶네요!~~

 


{[ 서버 ]}

/*
* RECEIVER
*/

#define PKT 1024 //기본 데이터패킷사이즈
#define FILEMAX 100000 //파일데이터 한 단위별 패킷사이즈

#pragma comment(lib, "ws2_32.lib") //ws2_32.a 링킹

#include <winsock2.h> //윈속
#include <windows.h> //윈도우api
#include <iostream> //c++표준입출력
#include <string> //getline함수와 string클래스

int main() {
	WSADATA wsa;
	if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return -1; //wsa !(0)일시 종료

	SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //소켓 tcpip
	if (server == INVALID_SOCKET) return -1; //종료

	SOCKADDR_IN addr = { 0 }; //소켓구조체
	addr.sin_addr.s_addr = htonl(INADDR_ANY); //주소
	addr.sin_port = htons(8080); //포트
	addr.sin_family = AF_INET; //데이터 체계방식

	if(bind(server, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR) return -1; //바인딩
	if (listen(server, SOMAXCONN) == SOCKET_ERROR) return -1; //리스닝

	SOCKADDR_IN clientaddr = { 0 }; //클라이언트 수용구조체
	int clientsize = sizeof(clientaddr); //클라이언트 크기
	SOCKET client = accept(server, (SOCKADDR*)&clientaddr, &clientsize); //클라이언트 오브젝트
	if (client == INVALID_SOCKET) return -1; //종료

	FILE* fp = 0;
	std::string str;
	char buffer[PKT],
		FileR[FILEMAX],
		name[PKT];

	while (true) {
		ZeroMemory(name, PKT);
		ZeroMemory(buffer, PKT);
		str.clear();

		std::cout << "Type to load filename by client path : "; //클라이언트쪽 받아올 파일절대경로 입력
		std::getline(std::cin, str); //(띄어쓰기포함o)
		fflush(stdin);

		std::cout << "Set the filename will save : "; //서버쪽 생성될 파일이름과 확장자 입력
		std::cin >> name; //(띄어쓰기포함x)
		fflush(stdin);

		send(client, str.c_str(), PKT, 0); //클라이언트쪽으로 파일경로 전송
		recv(client, buffer, PKT, 0); //파일크기 받기
		fopen_s(&fp, name, "wb"); //쓰기전용으로 열기

		int filesize = atoi(buffer); //형변환

		while (true) {
			ZeroMemory(FileR, FILEMAX);
			ZeroMemory(buffer, PKT);

			recv(client, buffer, PKT, 0); //파일데이터크기 받기
			recv(client, FileR, atoi(buffer), 0); //파일데이터 받기
			fwrite(FileR, sizeof(char), atoi(buffer), fp); //파일 데이터 쓰기

			std::cout << "received " << buffer << " bytes." << std::endl;

			filesize -= atoi(buffer); //전체파일사이즈를 받아온 파일사이즈만큼 줄인다
			if (filesize <= 0) break; //만약 파일사이즈가 0보다 작거나 같을경우 break
		}
		fclose(fp);
	}

	closesocket(client);
	closesocket(server);
	WSACleanup();
}

 

 

{[ 클라이언트 ]}

/*
* SENDER
*/
////////////////////////아래 서버쪽에서 설명한쪽은 주석을 생략하겠습니다.
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define PKT 1024
#define FILEMAX 100000

#pragma comment(lib, "ws2_32.lib")

#include <winsock2.h>
#include <windows.h>
#include <iostream>

int main() {
	WSADATA wsa;
	if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return -1;

	SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (server == INVALID_SOCKET) return -1;

	SOCKADDR_IN addr = { 0 };
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	addr.sin_port = htons(8080);
	addr.sin_family = AF_INET;

	while (connect(server, (SOCKADDR*)&addr, sizeof(addr)));

	FILE* fp = 0;
	char buffer[PKT],
		FileR[FILEMAX];
	int filesize, recvdsize;

	while (true) {
		ZeroMemory(buffer, PKT);
		recv(server, buffer, PKT, 0);

		if (WSAGetLastError()) return -1; //서버나갔을때 발생할 fseek에러 방지

		std::cout << "filepath >> " << buffer << std::endl;

		fopen_s(&fp, buffer, "rb"); //읽기전용으로 열기

		fseek(fp, 0, SEEK_END); //파일포인터 제일끝으로가서
		filesize = ftell(fp); //사이즈재기
		fseek(fp, 0, SEEK_SET); //파일포인터 다시 처음으로오기

		std::cout << "filesize >> " << filesize << std::endl;

		ZeroMemory(buffer, PKT); //현재 buffer에 파일경로값들어있으니까 초기화
		sprintf_s(buffer, "%d", filesize); //형변환
		send(server, buffer, PKT, 0); //파일크기 전송

		while (true) {
			ZeroMemory(buffer, PKT);
			ZeroMemory(FileR, FILEMAX);
			//실제로 받아온크기 = fread(파일데이터받아올객체, 그 객체의 한개단위, 총크기, 파일포인터)
			recvdsize = fread(FileR, sizeof(char), FILEMAX, fp);
			_itoa_s(recvdsize, buffer, 10); //형변환
			send(server, buffer, PKT, 0); //데이터크기전송
			send(server, FileR, recvdsize, 0); //데이터전송

			std::cout << "sending " << recvdsize << " bytes." << std::endl;

			filesize -= recvdsize; //파일 전체사이즈는 읽어들인 사이즈만큼 뺀다
			if (filesize <= 0) break; //파일전체사이즈가 0보다 작거나같을시 break
		}

		fclose(fp);
	}

	closesocket(server);
	WSACleanup();
}

 

 

 


 

 

그럼 봐주셔서

감사합니다!

궁금한 부분이 있다면 댓글로 질문주세요!

 

방금 생각났는데 다음 4번째 파일전송 시리즈는 쓰레드를 이용해서 병렬로 파일전송을 하도록해봐야겠네요~!

그때 사용될 소켓개념은 다중스트림서버 정도 되겠네요!

 

 

 

 

 

 

728x90

댓글