티스토리 뷰

IT/Network

[네트워크] half-close를 이용한 파일 전송 서버 및 클라이언트

주인장 진빼이

프로그램 설명 및 구현 모습

서버와 클라이언트가 파일 바이너리 데이터를 그대로 읽어 송수신하는 프로그램이다.

파일 바이너리 데이터를 읽는다는 의미는 바이너리 복사를 진행한다는 의미이다.

 

서버는 _TCPHalfClose.cpp 파일(서버 소스코드) 바이너리 데이터를 읽어 클라이언트에게 송신한다.

클라이언트는 송신한 데이터를 수신하여 파일 스트림을 이용해 새로운 파일을 만들고 수신한 파일 바이너리 데이터를 기록한다. (바이너리 복사) 기존 파일이 UTF-8로 인코딩 되어 있고 바이너리 복사를 진행했으므로 새로운 파일도 UTF-8 속성을 가지고 있다.

 

서버는 파일 데이터를 모두 송신했으면 파일을 더 이상 송신하지 않겠다는 의미로 shutdown() 함수를 호출하여 EOF(End of File) 신호를 클라이언트에게 보낸다. shutdown() 함수가 호출되어 서버는 더 이상 출력 스트림을 사용할 수 없다.

 

클라이언트는 서버가 보낸 파일 바이너리 데이터를 모두 송신했으면 " Thank you " 메시지를 서버에게 송신한다.

서버는 " Thank you " 메시지를 받고 프로그램을 종료한다.

 

서버 클라이언트는 통신을 주고받을 때 송수신할 BUF_SIZE보다 작은 경우, 남은 데이터 크기를 구해서 송수신한다.

흐름만 설명했으니. 파일 스트림을 닫거나, 자세한 디테일은 소스코드를 참고하자.

 

 

 

 

 

소스코드 (Source Code)

//==== Server ====
#include <iostream>

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <conio.h>
#include <fstream>
#include <tchar.h>
#include <string>
using namespace std;
#define BUF_SIZE 30

int main()
{

	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	_wsetlocale(LC_ALL, L"");
	setlocale(LC_ALL, "");

	SOCKET hServSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (hServSock == INVALID_SOCKET)
	{
		cout << "socket error!";
		_getch();
		exit(0);
	}

	SOCKADDR_IN servAddr = { 0, };
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(9000);
	InetPton(AF_INET, _T("127.0.0.1"), &servAddr.sin_addr);
	if (bind(hServSock, (LPSOCKADDR)&servAddr, sizeof(servAddr)) == SOCKET_ERROR)
	{
		cout << "bind error!";
		_getch();
		exit(0);
	}

	Sleep(500);
	listen(hServSock, SOMAXCONN);
	SOCKADDR_IN clntAddr{ 0, };
	int addrSize = sizeof(clntAddr);
	SOCKET hClntSock;
	if ((hClntSock = accept(hServSock, (LPSOCKADDR)&clntAddr, &addrSize)) == SOCKET_ERROR)
	{
		cout << "accept error!";
		_getch();
		exit(0);
	}

	ifstream file(".\\_TCPHalfClose.cpp", ios::in | ios::binary);
	if (!file.is_open())
	{
		cout << "file not found";
		_getch();
		exit(0);
	}
	int read_cnt = 0;
	char buf[BUF_SIZE];
	int sendLength = 0;
	int totalLength = 0;
	do
	{
		Sleep(50);
		file.read(buf, BUF_SIZE);
		read_cnt = file.gcount();

		if (read_cnt < BUF_SIZE)
			sendLength = send(hClntSock, buf, read_cnt, 0);
		else
			sendLength = send(hClntSock, buf, BUF_SIZE, 0);


		totalLength += sendLength;
		cout << "sendLength " << sendLength << ", totalLength: " << totalLength << "\n";
	} while (read_cnt >= BUF_SIZE);
    
	shutdown(hClntSock, SD_SEND);
    file.close();
    
	recv(hClntSock, buf, BUF_SIZE, 0);
	cout << buf << "\n";
    
	closesocket(hClntSock);
	WSACleanup();
}

 

//==== Client =====
#include <iostream>

#include <WinSock2.h>
#include <conio.h>
#include <WS2tcpip.h>
#include <fstream>
#include <tchar.h>

using namespace std;
#define BUF_SIZE 30
int main()
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	SOCKET hClntSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (hClntSock == INVALID_SOCKET)
	{
		cout << "clnt error: socket error!";
		_getch();
		exit(0);
	}

	SOCKADDR_IN connectAddr = { 0, };
	connectAddr.sin_family = AF_INET;
	connectAddr.sin_port = htons(9000);
	_getch();
	InetPton(AF_INET, _T("127.0.0.1"), &connectAddr.sin_addr);
	if (connect(hClntSock, (LPSOCKADDR)&connectAddr, sizeof(connectAddr)) == SOCKET_ERROR)
	{
		cout << "clnt error: connect error!";
		_getch();
		exit(0);
	}

	char buf[BUF_SIZE];
	// receive data
	ofstream file(".\\test.txt", ios::out | ios::binary);
	if (!file.is_open())
	{
		cout << "clnt: file not found";
		_getch();
		exit(0);
	}

	int recvLength = 0;
	while ((recvLength = recv(hClntSock, buf, BUF_SIZE, 0)) != 0) // !!
	{
		cout << "recvLength: " << recvLength << "\n";
		file.write(buf, recvLength);
	}
	file.close();
	send(hClntSock, "Thank you", 10, 0);
    
	closesocket(hClntSock);
	WSACleanup();
}

 

결과

프로그램을 이용하여 파일 복사가 잘 되는 것을 볼 수 있다.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함