/**************************************************/
// 서버측에서 파일을 전송하는 코드부분
//*************************************************/

// 우리가 사용할 포트를 정의한다. 이 값은 맘대로 사용해도 되지만, 대개 1000 이상
의 포트 번호를 사용하자.
#define PORT 30000

// 맨처음 접속을 대기할 소켓을 생성한다.
AfxInitSocket(NULL);

CAsyncSocket listenSoc;
listenSoc.Create(PORT);

// 그리고는 접속요청을 기다리자.
listenSoc.Listen();

// 접속요청이 왔다면 받아들이자.
CSocket acceptSoc;  // 이 소켓은 Create를 호출하면 안된다.
listenSoc.Accept(&acceptSoc);

// 이제부터 클라이언트와 대화하는 것은 온전히 acceptSoc의 소관이다.
// 전송할 파일을 열자
CFile sourceFile;
sourceFile.Open((LPCTSTR)strFileName, CFile::modeRead | CFile::typeBinary);
// strFileName은 CString 객체로 파일의 이름을 가지고 있다.

// 파일을 전송하기 전에 파일이름을 클라이언트에게 알려줘야 한다. 
// 그래야 클라이언트는 전송받은 파일을 이름을 바꾸지 않고 정확히 쓸 것이다.
int nNameLen = strFileName.GetLength(); // 파일이름의 길이를 저장한다.

acceptSoc.Send(&nNameLen, 4); // 파일 이름의 길이를 전달한다.
acceptSoc.Send((LPCTSTR)strFileName, nNameLen); // 파일 이름을 전달한다.
// 데이터를 위한 임시 버퍼을 잡자.
byte * data = new byte[4096];
DWORD dwRead;
// 파일을 읽고 소켓으로 전송하자.
do
{
  dwRead = sourceFile.Read(data, 4096);
  acceptSoc.Send(data, dwRead);
}
while(dwRead > 0);
/* 위의 코드는 파일의 길이를 4k 바이트씩 잘라서 전송합니다. 소켓의 디폴트 버퍼크
기는 8k이지만, 파일의 전체를 한번에 읽어서 전송하면, 8k가 초과되는 용량에 대해서
는 데이터가 손상되지 않으리라 보장할 수 없습니다. CFile객체의 Read함수는 인수로 
지정된 바이트 수만큼 읽지 않을 수도 있습니다. 하지만 최대로 읽어들이는 양은 인수
로 전달된 바이트수를 초과하지는 않습니다. 따라서 읽어들인 양이 어느정도인지 
dwRead에 저장하고 그 크기를 소켓의 Send함수에 전달해 주어야 하는 것입니다. 소켓
의 Send함수 역시, 지정된 사이즈를 전부 한번에 전송하지 않을 수도 있습니다. 전송
당시의 TCP망이 속도가 저속이면 인수로 지정된 양보다 작은 양을 전송하게 되지만, 
그것은 TCP가 알아서 추가 전송을 할 것이므로 위 코드의 루프에서는 신경써주지 않아
도 됩니다. */

// 메모리를 해제하고 파일핸들을 닫는다
delete data;
sourceFile.Close();


/***************************************/
// 클라이언트에서 파일을 받는 부분
/***************************************/

// 포트를 정의하는데, 서버측과 같은 포트이어야 한다.
#define PORT 30000

// 소켓을 초기화 한다.
AfxInitSocket(NULL);

// 소켓을 만들자
CSocket connectSoc;
connectSoc.Create(); // 여기서는 인수를 전달하지 않는다

// 접속을 요청하자
connectSoc.Connect("210.120.150.111", PORT);
/* Connect함수의 첫번째 인자는 서버측 컴퓨터의 IP주소이다. 이것은 (.)을 찍는 방
식으로 써도 되고, 도메인네임으로 써도 된다.*/
if(connectSoc.GetLastError() == 0) // 접속에 실패하였다면..
{
  MessageBox("접속에 실패하였습니다");
  // 기타 접속실패 처리
  connectSoc.Close();
  return;
}

// 접속에 성공했다면 데이터를 받아 들이자
char *strFileName;
int nNameLen;

connectSoc.Receive(&nNameLen, 4);
connectSoc.Receive(strFileName, nNameLen);

CFile targetFile;
targetFile.Open(strFileName, CFile::modeCreate | CFile::modeWrite | 
CFile::typeBinary);

byte *data = new byte[4096];
DWORD dwRead;

do
{
  dwRead = connectSoc.Receive(data, 4096);
  targetFile.Write(data, dwRead);
}
while(dwRead > 0);

delete data;
targetFile.Close();

위의 코드는 가장 핵심적인 기능만 구현되어 있습니다. 실제로는 이 코드를 애플리케
이션에 붙이고 실행하면 CPU타임을 독차지 하게 되어 파일 전송이 완료되기 까지 사용
자에게 응답하지 않습니다. 용량이 큰 파일을 전송할 경우에는 전송시간이 걸릴 것이
므로, 사용자는 다운된 줄 알고 프로그램을 죽일 수도 있기 때문에 골칫거리가 아닐 
수 없습니다. 또, 전송받는 부분에서 상황에 따라 무한루프에 빠지는 경우도 있는데, 
전송받은 총량과 전송받아야 할 량을 비교해서 루프를 탈출하는 코드가 추가 되어야
만 완전해 집니다.

+ Recent posts