본문 바로가기
IT창고/Network

파일전송 서버/클라이언트

by 창구창고 2007. 1. 22.
반응형

//file server

#define WIN32_LEAN_AND_MEAN // windows.h 에서 자주 사용하지 않은 것은 컴파일에서 제외 한다.
							// winsock2.h외의 충돌을 막아 준다.

#include<stdio.h>
#include<windows.h>
#include<winsock2.h>
#include<vector>// STL의 vector
using namespace std;

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

char filename[260] = "C:\\a.zip"; //전송할 파일

struct FILE_INFO
{
	char FileName[260];	//전송할 파일 이름
	int size;			//파일 크기
};
//전속한 클라이언트에게 파일을 보낸다.
DWORD WINAPI FileServer(void * p)
{
	SOCKET s  = (SOCKET)p;

	//전송할 파일 오픈
	HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ,
								0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if(hFile == INVALID_HANDLE_VALUE)
	{
		printf("Can't Open File\n");
		closesocket(s);
		return 0;
	}

	//크기를 구하고 파일의 정보를 전송한다.
	DWORD size1;
	DWORD size2 = GetFileSize(hFile, &size1);

	FILE_INFO fi;
	strcpy(fi.FileName, filename);
	fi.size = size2;

	send(s,(char *)&fi, sizeof(fi), 0);
	//-----------------------------------------------------
	//파일 전송
	int total = size2; //전송할 전체 크기
	int current = 0 ;	//전송한 크기
	int nRead = 0;
	char buf[4096];  //4K버퍼

	while(total > current)
	{
		DWORD len;
		nRead = ReadFile(hFile, buf, 4096, &len, 0);

		if(len <= 0)break;

		int nSend = send( s, buf, len, 0 );
		if(nSend <= 0) break;

		current += nSend;
	}
	if(total != current )		printf("전송 에러\n");
	else						printf("전송완료]\n");
	closesocket(s);
	return 0;
}

void main()
{
	WSADATA wsadata;

	//if(WSAStartup(0x0202, &wsadata) != 0)			//0x0202는 뒤에서부터 읽어서 2.2버젼을 쓰라는 이야기다.
	if(WSAStartup(MAKEWORD(2,2), &wsadata) != 0)	//많이 사용... 앞에서 부터 읽는다.
	{
		printf("Can't Initialize Socket !\n");
		return ;
	}
	//------------------------------------------
	//1. socket생성 : TCP: SOCK_STREAM : UDP :SOCK_DGRAM
	SOCKET s = socket(AF_INET,  SOCK_STREAM, 0);

	//2. 생성된 소켓에 주소를 지정.
	SOCKADDR_IN addr = { 0 };
	addr.sin_family = AF_INET;	// 주소 종류 (AF_INET : IP 주소라는 의미..
	addr.sin_port = htons(5000);	//Port #
	addr.sin_addr.s_addr = INADDR_ANY;//inet_addr("61.81.99.54");
	
	if(bind(s,(SOCKADDR*)&addr, sizeof(addr)) == -1)	//서버의 닫혀 있는 상태를 대기 상태로 바꿔준다. 이순간 소켓은 내부적으로 접속요청 큐와 접속 완료 큐가 만들어진다.
	{
		printf("Can't bind \n"); 
		return;
		
	}
	
	//3. 소켓을 대기 상태로 전환한다.
	if(listen(s, 10) == -1)
	{
		printf("Can't Listen\n");
		closesocket(s); // 소켓 닫기
		return;
	}
	
	printf("클리이언트를 대기합니다. \n");
	
	//4. 이제 Client 에서 부터 전달된 요청을 허용한다.
	while(1)
	{
		SOCKADDR_IN c_addr;
		int size = sizeof(c_addr);
		
		SOCKET c_s = accept(s, (SOCKADDR*)&c_addr, &size);	//억셉트를 눌렀는데 대기 큐에 아무도 없으면 대기한다.
		//억셉트를 누르는 순간 새로운 소켓을 만들어서 
		//상태를 연걸상태로 
		//IP는 자신의 IP로 포트는 놀고있는 임의의 포트로 설정해준다.->4000번 포트로 접속해서 다른포트로 통신
		//상대 IP와 상대 포트를 지정... 상대의 포트도 접속후에는 통신하는 포트로 지정한다.
		//->접속하는 소켓은 대기만하고 접속하면 새로운 소켓이 통신을 한다.
		
		printf("클라이언트가 접속했습니다. IP : %s\n", inet_ntoa(c_addr.sin_addr));

		
		//새로운 스레드를 생성해서 클라이언트의 요청을 처리한다.
		HANDLE hThread = CreateThread(0 , 0, FileServer, (void *)c_s, 0,0);
		CloseHandle(hThread);
	}
	
	//---------------------------------------------------------------------------
	closesocket(s); // 소켓 닫기
	
	//--------------------------------------------------------------
	
	//------------------------------------------
	WSACleanup();
}
 
//client
#define WIN32_LEAN_AND_MEAN
#include<winsock2.h>
#include<windows.h>
#include<commctrl.h>
#include<stdio.h>

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

struct FILE_INFO
{
	char FileName[260];
	int size;
};

FILE_INFO g_info;

LRESULT CALLBACK WndProc(HWND hwnd,UINT iMessage,WPARAM wParam,LPARAM lParam);
DWORD WINAPI FileClient(void * p)
{
	HWND hwnd = (HWND)p;

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

	SOCKET s = socket( AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addr;
	addr.sin_family = AF_INET;
	addr.sin_port	= htons(5000);
	addr.sin_addr.s_addr = inet_addr("61.81.99.138");

	if ( connect(s, (SOCKADDR *)&addr, sizeof(addr)) == -1)
	{
		MessageBox( 0, "Connect Error !!","",MB_OK);
		return 0;
	}

	//-------------------------------------------------------------
	// 파일 정보를 수신한다.
	recv(s ,(char *)&g_info, sizeof(g_info), 0);

	//주 스레드에 알려준다. -> 주스레드가 UI를 업데이트 한다.!
	PostMessage(hwnd, WM_USER+100,0,0);
	//-------------------------------------------------------------

	char name[260];
	strcpy(name, g_info.FileName);
	strcat(g_info.FileName, "_bak");

	HANDLE hFile = CreateFile(name, GENERIC_READ, FILE_SHARE_READ,
								0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	int total = g_info.size;
	int current = 0;
	char buf[4096];
	while(total > current)
	{
		int nRecv = recv(s, buf, 4096, 0);
		if( nRecv <= 0)break;

		DWORD len;
		WriteFile(hFile, buf, nRecv, &len, 0);

		current += nRecv;
		PostMessage(hwnd, WM_USER+200,0,(LPARAM)current);
		//------------------------------------------------------------
	}
	if(total != current)	MessageBox(0,"Error","",MB_OK);
	else					MessageBox(0,"OK","",MB_OK);
	closesocket(s);
	CloseHandle(hFile);
	WSACleanup();
	return 0;
}

TCHAR lpszClass[] = TEXT("first");

int WINAPI WinMain(HINSTANCE hInstance,
				   HINSTANCE hPrev,
				   LPSTR lpCmdLine,
				   int nShowCmd)
{
	//AllocConsole();	//	콘솔창 생성

	//freopen("CONOUT$",	//콘솔을
	//	"wt",			//text write모드로
	//	stdout);		//stdoutrhk 연결
	MSG Message;
	//1. 위도우 클래스 만들기 (10가지를 다 정확히 입력하지 않으면 화면이 뜨지 않는다.)
	WNDCLASS wc;		//미리정의된 10개 항목을 채우는 클래스 (WNDCLASSEX 12개 항목 10개 항목에 작은 아이콘, 구조체 크기를 포함한다.)
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground	=	(HBRUSH)GetStockObject(	WHITE_BRUSH);

	wc.hCursor		=	LoadCursor	(	0, IDC_CROSS);
	wc.hIcon		=	LoadIcon	(	0, IDI_WINLOGO	);
	wc.hInstance	=	hInstance;
	wc.lpfnWndProc	=	WndProc;	//<~ 이부분을 자신이 만드는 함수로 바꿔줘야 한다.	메시지 함수등록
	wc.lpszClassName=	lpszClass;
	wc.lpszMenuName	=	0;
	wc.style		=	0;

	//2. 윈도우 클래스 등록하기.
	RegisterClass( & wc	);	// 등록을 하는 함수도 2가지가 있다. RegisterClass와 RegisterClassEx가 있다.
	//WNDCLASS로 만들었으면 RegisterClass로 등록하고 WNDCLASSEX로 만들었으면 RegisterClassEx로 등록

	//3. 등록된 클래스를 사용해서 윈도우 만들기.
	// typedef long HWND;	//	윈도우의 번호를 담을때 사용
	HWND hwnd	=	CreateWindowEx(0 ,			// 확장 윈도우 스타일
									lpszClass,				// 윈도우 클래스 이름
									TEXT("첫시작"),					// 창이름
									WS_OVERLAPPEDWINDOW,	// 기본 Window Styles : & ~ 연산으로 기존스타일 제거하기
															// WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX
									CW_USEDEFAULT,	0,	CW_USEDEFAULT,	0,	// X,Y,W,H
									0,						// 부모 윈도우 핸들( 번호 )
									0,						// 메뉴 핸들( 번호 )
									hInstance,				// 인스턴스 핸들
									0);						// 생성인자.( MDI 만들때 사용 )
	
	//4. 윈도우 보여주기
	ShowWindow(hwnd, nShowCmd);

	while(GetMessage(&Message,0,0,0))	//메시지 큐에 가서 메시지를 꺼내오고 가져온 메시지를 윈도우 클래스에 등록된 메세지 처리함수로 전달한다.
	{
		//여기서 메시지를 처리하면 부모뿐 아니라 모든 자식윈도우로 전달되는 메시지도 먼저 처리할 수 있다.

		TranslateMessage(&Message);
		if (Message.message	==	WM_SYSKEYDOWN && Message.wParam == 'X')
		{
			PostQuitMessage(0);
			MessageBox(hwnd, "종료","질문",MB_OK);
			//SendMessage(hwnd, WM_CLOSE,0,0);
			continue;
		}

		//PreTranslateMessae(&Message);
		DispatchMessage(&Message);//가져온 메시지를 윈도우 클래스에 등록된 메시지 처리함수로 전달한다.
	}
	
	//MessageBox(0, "이제 알겠죠?","물음",MB_OKCANCEL);//종료되지 않게 하기 위해
	return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
	static HWND hEdit1, hEdit2, hPrg;
	switch(iMessage){
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0 ;
	case WM_CREATE:
		hEdit1 = CreateWindow("edit","",
			WS_CHILD | WS_VISIBLE | WS_BORDER,// | WS_VSCROLL | ES_MULTILINE,
			10,10,200,20,hwnd,(HMENU)1,0,0);
		hEdit2 = CreateWindow("edit","",
			WS_CHILD | WS_VISIBLE | WS_BORDER,// | WS_VSCROLL | ES_MULTILINE,
			10,40,200,20,hwnd,(HMENU)2,0,0);
		hPrg = CreateWindow(PROGRESS_CLASS,"",
			WS_CHILD | WS_VISIBLE | WS_BORDER | PBS_SMOOTH,
			10,70,400,20,hwnd,(HMENU)3,0,0);
		return 0 ;
	case WM_USER+100:
		{
			SetWindowText(hEdit1, g_info.FileName);
			
			char buf[256];
			wsprintf(buf, "%d", g_info.size);
			SetWindowText(hEdit2, buf);

			//프로그래스 초기화
			SendMessage(hPrg , PBM_SETRANGE32, 0, g_info.size);
		}
		return 0;
	case WM_USER+200:
		{
			SendMessage(hPrg, PBM_SETPOS,lParam,0);
		}
		return 0;
	case WM_LBUTTONDOWN:
		{
			CloseHandle(CreateThread(0, 0, FileClient, (void *)hwnd, 0,0));
		}
		return 0;
	}
	//꺼내온 메시지를 처리하지 않은경우 -> 바드시 아래 함수를 처리한다.
	return(DefWindowProc(hwnd,iMessage,wParam,lParam));
}
반응형

"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."