Network/Windows 소켓 프로그래밍 입문에서 고성능 서버까지!

파일 송/수신

Tony Lim 2023. 11. 23. 18:03
728x90

기본 구조에 대한 설명

client 의 운영체제 의 수신속도 (kernel -> application ) 가 application 의 read 속도 보다 빠르면 장애가 날 확률이 높다.


파일 송신 서버 제작 (프로토콜이 없는 파일 송신)

	//파일송신
	char byBuffer[65536];		//64KB
	int nRead, nSent, i = 0;
	while ((nRead = fread(byBuffer, sizeof(char), 65536, fp)) > 0)
	{
		//파일에서 읽고 소켓으로 전송한다.
		//전송에 성공하더라도 nRead와 nSent 값은 다를 수 있다!!!
		nSent = send(hClient, byBuffer, nRead, 0);
		printf("[%04d] 전송된 데이터 크기: %d\n", ++i, nSent);
		fflush(stdout);
	}

send 시에 1024 * 64 = 65536 (64KB) 만큼 전송했지만 이게 수신쪽에서 위에서 언급한것처럼 제대로 처리를 빠르게 못해서 다 안갈 수 있도 있다.

100을 보내도 50은 못보내는 경우도 있다.


파일 수신 클라이언트 제작

	//수신할 파일을 생성한다.
	puts("*** 파일 수신을 시작합니다. ***");
	FILE *fp = NULL;
	errno_t nResult = fopen_s(&fp, "Sleep away.zip", "wb");
	if (nResult != 0)
		ErrorHandler("파일을 생성 할 수 없습니다.");

	//서버가 전송하는 데이터를 반복해 파일에 붙여 넣는다.
	char byBuffer[65536];		//64KB
	int nRecv;
	while ((nRecv = ::recv(hSocket, byBuffer, 65536, 0)) > 0)
	{
		//서버에서 받은 크기만큼 데이터를 파일에 쓴다.
		fwrite(byBuffer, nRecv, 1, fp);
		putchar('#');
	}

압축파일을 송수신 하게 되면 압축파일이 내부적으로 crc 체크를 해서 송수신간에 오류가 생겼는지 확인하게 된다.

마치 MD5 해쉬 값을 보내서 이 파일이 중간 손상되었는지 받은쪽에서 파일을 해쉬해서 게산한 해쉬값하고 받은 해쉬값하고 비교를 하게 된다.

보통의 경우 recv 가 write보다 항상 빠르다. network 속도 vs hdd(ssd) 속도의 차이이다.


Win32 API 기반 파일 송/수신

	//파일송신
	if ( ::TransmitFile(
			hClient,	//파일을 전송할 소켓 핸들.
			hFile,		//전송할 파일 핸들.
			0,			//전송할 크기. 0이면 전체.
			65536,		//한 번에 전송할 버퍼 크기.
			NULL,		//비동기 입/출력에 대한 OVERLAPPED 구조체.
			&tfb,		//파일 전송에 앞서 먼저 전송할 데이터.
			0			//기타 옵션.
			) == FALSE )
		ErrorHandler("파일을 전송할 수 없습니다.");

	//클라이언트가 연결을 끊을 끊기를 대기한다.
	::recv(hClient, NULL, 0, 0);
	puts("클라이언트가 연결을 끊었습니다.");

기존에 file read -> send 의 2가지 동작이 TransmitFile에 다 들어있다.

tfb는 파일명 , 사이즈 같은 미리 어떤 데이터를 송신할것인지 일종의 header처럼 보내게 된다.

	//수신할 파일명, 크기 정보를 먼저 받는다.
	MY_FILE_DATA fData = { 0 };
	if (::recv(hSocket, (char*)&fData, sizeof(fData), 0) < sizeof(fData))
		ErrorHandler("파일 정보를 수신하지 못했습니다.");

	//수신할 파일을 생성한다.
	puts("*** 파일 수신을 시작합니다. ***");
	HANDLE hFile = ::CreateFileA(
		fData.szName,
		GENERIC_WRITE,
		0,
		NULL,
		CREATE_ALWAYS,	//파일을 생성한다.
		0,
		NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		ErrorHandler("전송할 파일을 개방할 수 없습니다.");

수신하는쪽에서는 header를 먼저 읽어서 파일을 먼저 생성한 후에 실제 데이터를 읽어드리게 된다.


대표적인 TCP 장애 유형과 연결에 대한 정의

  • Packet Loss = 패킷이 유실된 경우
  • TCP Out of Order = 패킷의 순서가 뒤바뀐 경우
  • Retransmission 과 Dup Ack
  • Zero-window = 수신측 버퍼에 여유공간이 하나도 없는 경우

Zero window는 application이 빠르게 처리하지 못해서 발생하는 문제로 개발자가 해결해야하는 영역이고

나머지 위에4개는 network영역에서 해결해야하는 문제이다.

client측 os 에서 window 가 zero가고있다고 server에게 알려준다.

 

TCP 연결이라는 착각

  • 재전송 타이머의 기본 근사 값은 대략 3초이나 대다수 운영체제들은 1초미만
  • 재전송 타이머 만료시 세그먼트를 재전송하고 RTO(Retransmission Time-Out) 값은 두배로 증가
  • 예를 들어 1초 > 2초 > 4초 > 8초 > 16초 간격으로 재전송
  • 보통 최대 5회 재전송을 시도하고 5회 이상 모두 실패할 경우 보통 전송 오류가 발생

 

 

 

 

 

728x90