티스토리 뷰
프로그램 설명 및 구현 모습
서버와 클라이언트가 파일 바이너리 데이터를 그대로 읽어 송수신하는 프로그램이다.
파일 바이너리 데이터를 읽는다는 의미는 바이너리 복사를 진행한다는 의미이다.
서버는 _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();
}
결과
프로그램을 이용하여 파일 복사가 잘 되는 것을 볼 수 있다.