1. 소켓의 개념과 생성, 프로토콜의 설정

MFC 소켓 클래스에 들어가기 전에 윈속 소켓 프로그래밍에 대해 간략하게 정리 해보겠습니다.

1.1 소켓의 개념
   네트워크를 이용한 통신은 전화망을 이용한 통신과 그다지 다르지 않습니다. 전화망을 이용한 통신에서 전화기는 사람과 사람을 이어주는 역할을 하는데요. 이를 일종의 소켓이라고  할 수 있겠습니다.
  단도직입적으로 말하면, 소켓이란 떨어져 있는 두 개체를 연결해 주는 일종의 도구라고 할 수 있겠네요.
1.2 소켓 구현
1.2.1 윈속(Winsock) 초기화 하기
  윈속 프로그래밍시에는 반드시 윈속 초기화를 해야 합니다. 이는 프로그램에서 사용하는 윈속버전을 표시함으로써 이 버전의 윈속 사용을 위한 라이브러리 초기화 작업을 위함 입니다.

#include <winsock2.h>

int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);

  
  첫번째 파라미터에는 프로그램에서 사용하는 윈속의 버전을 WORD 형으로 변환한 값이 들어갑니다. 상위 8비트에는 부버전을 하위 8비트에는 주버전을 표시합니다. 예를 들어 2.2 버전을 사용한다면 이 파라미터에는 0x0202를 사용하고 3.2 버전을 사용한다면 0x0203을 사용하게 되는 것 입니다. 이를 편리하게 변환하기 위해 MAKEWORD 함수(매크로 함수)를 사용하는 것이 일반적이에요.

  두번째 파라미터에는 WSADATA 타입 변수의 포인터가 들어갑니다. 함수 호출이 성공적으로 이루어지면 이 변수에는 로딩한 DLL의 정보가 채워지게 됩니다.

프로그램 종료시에는 WSADATA를 해제해 주어야 하는데요. 이때 사용하는 함수가 WSACleanup 함수 입니다.

int WSACleanup(void); // 성공시 0, 실패시 SOCKET_ERROR 리턴

1.2.2 소켓 생성
 소켓 생성이란 전화망을 이용한 통신에 비유해 볼때 전화기를 마련하는 것과 같습니다.

SOCKET socket(int af, int type, int protocol); // 성공시 소켓 핸들, 실패시 INVALID_SOCKET 리턴

  첫번째 파라미터에는 주소의 계열을 표시합니다. 
       PF_INET : IP v4의 주소 체계
       PF_INET6 : IP v6의 주소 체계
       PF_LOCAL : Local 통신을 위한 유닉스 프로토콜
       PF_PACKET : Low level socket을 위한 인터페이스
       PF_IPX : IPX 노벨 프로토콜

  두번째 파라미터에는 소켓의 타입을 지정합니다.
       SOCK_STREAM : TCP프로토콜을 사용시
       SOCK_DGRAM :  UDP프로토콜을 사용시

  세번째 파라미터에는 이 소켓에서 데이터를 전송할때 사용할 프로토콜을 지정합니다.

       IPROTO_TCP : TCP 프로토콜 사용시
       IPROTO_UDP : UDP 프로토콜 사용시
  하지만 실제 프로그래밍시에는 보통 0을 써주는데, 0을 넣게 되면 주소체계와 타입을 보고 자동으로 설정하게 됩니다.

1.2.3 주소와 포트 할당 
  전화기만 있고 전화번호가 없다면 우리는 통화를 할 수 없겠죠. 전화기에 전화번호를 할당해주는 역할을 하는 함수가 바로 bind 함수 입니다.

int bind(SOCKET s, const struct sockaddr FAR *name, int namelen); // 성공시 0, 실패시 SOCKET_ERROR 리턴

  첫번째 파라미터에는 사용할 소켓의 객체가 들어갑니다.

  두번째 파라미터에는 서버의 주소가 들어가는데 이에 대해서는 다음 포스팅에 자세히 설명하도록 하겠습니다.

  세번째 파라미터에는 서버 주소의 길이가 들어갑니다.

1.2.4 연결 요청 대기(listen) 상태로 진입
  이제 서버에서는 클라이언트가 연결을 요청할 수 있는 상태로 만들어 줘야 하겠죠? 이 역할을 하는 함수가 listen 함수 입니다.

int listen(SOCKET s, int backlog); // 성공시 0, 실패시 SOCKET_ERROR 리턴

  첫번째 파라미터에는 사용할 소켓의 객체가 들어갑니다.

  두번째 파라미터에는 버퍼의 크기가 들어갑니다. 서버에 연결 신호가 동시에 많이 왔을 때는 특정 버퍼에 저장을 해두고 연결을 해 줘야 하는 경우가 생기게 되는데 소켓에 그 버퍼의 크기를 지정하는 부분이 되겠습니다.
1.2.5 연결 수락
  클라이언트가 서버에 연결 요청을 하면 이를 수락해 주는 부분입니다. 이 역할은 담당하는 함수가 바로 accept 함수 입니다.

SOCKET accept(SOCKET s,  struct sockaddr FAR *addr, int FAR *addrlen); // 성공시 소켓 핸들, 실패시 INVALID_SOCKET 리턴

  파라미터의 형태는 bind 함수와 동일하나 bind 함수는 서버의 주소를 사용하고 accept 함수는 클라이언트의 주소를 사용합니다.

1.2.6 클라이언트에서의 연결 요청
  클라이언트에서 서버로 연결 요청을 할 때는 connect 함수를 사용합니다.

int connect(SOCKET s, const struct sockaddr FAR *name, int namelen); // 성공시 0, 실패시 SOCKET_ERROR 리턴



1. sockaddr_in 구조체
  소켓을 사용하기 위해선 소켓에 서버의 IP와 Port를 바인딩 해주어야 합니다. 이 과정에서 IP와 Port를 저장하는 sockaddr_in 구조체를 보셨을 겁니다. sockaddr_in 구조체는 IPv4의 주소 체계를 저장할 수 있는 구조체 입니다.

struct sockaddr_in {
    sa_family_t       sin_family;    
   
// 주소의 계열 (이전 포스팅에서 나왔던 socket 생성 함수의 첫 인자 값과 동일)
    uint16_t             sin_port;        
   
// 16bit TCP / UDP Port
    struct in_addr    sin_addr;        
    
// 32bit IPv4 Address
    char                 sin_zero[9];    
    
// 사용하지 않습니다.
};
           - sockaddr_in 구조체의 원형-

struct in_addr(
    uint32_t        s_addr;
};
           - in_addr 구조체의 원형 -
2. Byte Order 변환
  네트워크 상에서는 Big-Endian 방식으로 데이터를 전송한다. sockaddr_in 구조체에 저장되는 값들은 모두 Big-   Endian 방식으로 저장되어야 합니다. byte order 변환 함수에는 다음과 같은 것이 있습니다.

- htons : host byte order를 short 형의 network byte order로 변환
- ntohs : network byte order를 short 형의 host byte order로 변환
- htonl : host byte order를 long 형의 network byte order로 변환
- ntohl : network byte order를 long 형의 host byte order로 변환

 

3. 주소 문자열을 Big-Endian 32bit 값으로 변환
주소 문자열을 Big-Endian 32bit 값으로 변환하기 위해서 inet_addr() 함수를 사용합니다.

unsigned long inet_addr(const char *string);
                 - inet_addr 함수의 원형 -

INI 파일의 구조는 보통 다음과 같이 되어있습니다.

예) setup.ini

 [Title1]
Name=test.cpp
Path=C:\temp\test.cpp
Version=2.1.0.1
[Title2]
Name=test.cpp
Path=C:\temp\test.cpp
Version=2.1.0.1
.
.

위 파일을 제어(읽기/쓰기)하려면 다음과 같은 함수를 사용합니다.

GetPrivateProfileString() : 파일에서 정보(문자열)를 읽어온다.
WritePrivateProfileString() : 파일에 정보(문자열)를 쓴다. 만약에 해당 섹션과 키값이 없으면 자동으로 생성한다.

각 함수의 원형은 다음과 같습니다.


DWORD GetPrivateProfileString(
  LPCTSTR lpAppName,       // [Title1]과 같은 섹션(대제목)입니다.
  LPCTSTR lpKeyName,       // Name과 같은 키값(구성요소)입니다.
  LPCTSTR lpDefault,           // 섹션이름이나 키이름을 찾지 못하면 디폴트로 출력할 스트링입니다.
  LPTSTR lpReturnedString// Name과 같은 키값(구성요소)에서 가리키는 값(test.cpp)을 저장할 버퍼입니다.
  DWORD nSize,                  // 키값을 저장할 버퍼의 사이즈입니다.
  LPCTSTR lpFileName        // INI 파일(setup.ini)의 경로입니다.
);
 BOOL WritePrivateProfileString(
  LPCTSTR lpAppName,     // [Title1]과 같은 섹션(대제목)입니다.
  LPCTSTR lpKeyName,     // Name과 같은 키값(구성요소)입니다.
  LPCTSTR lpString,           // Name과 같은 키값(구성요소)에 작성할 정보(문자열)입니다.
  LPCTSTR lpFileName      // INI 파일(setup.ini)의 경로입니다.
);


사용법은 각 함수의 파라메터에 맞게 값을 넣어주면 됩니다.

예를 들면..

GetPrivateProfileString( "Title1", "Name", "No Info", inbuffer, sizeof(inbuffer), "C:\\temp\\setup.ini" );
//설명 : C:\\temp\\setup.ini 파일에서 Title1의 Name 값을 inbuffer 사이즈만큼 읽어와라,
         Name값이 없으면 No Info값을 inbuffer에 저장하라.

WritePrivateProfileString( "Title1", "Name", outbuffer, "C:\\temp\\setup.ini" );
//설명 : C:\\temp\\setup.ini 파일에서 Title1의 Name 값을 outbuffer에 정보(문자열)를 써라.

 

APS
리소스 파일에 대한 바이너리 파일. 리소스 파일을 로딩하는데 사용한다.(로딩  속도 향상)

BSC
소스 브라우저 정보 파일(Browser database file). 이 파일은 BSCMAKER.EXE를 실행할 때 SBR 파일로부터 생성된다.

C
C언어 소스 파일. C 방식으로 컴파일된다. 만약 소스 내에 C++ 코드가 있다면 확장자를 CPP로 변경하거나 컴파일시 옵션을 /TP로 설정한다.

CLW(CLassWizard status file)
클래스 위저드 상태 파일. 프로젝트에 사용된 파일과 클래스에 관한 정보를 담고 있다. 클래스위저드를 사용하여 추가되는 메시지 핸들링 함수 등과 관련된 정보들이 포함된다.

CPP, CXX
C++ 언어 소스 파일. C++방식으로 컴파일된다.만약 확장자만 CPP이고 실제 내용이 C 코드이면 확장자를 C로 변경하거나 컴파일시 옵션을 /Tc로 설정한다.

DEF(Module Definition File)
모듈 정의 파일. 프로젝트 유형에 따라 사용 목적이 다른데 윈도우즈 또는 윈도우즈 NT 기반의 프로그램인 경우 익스포트되는 함수의 리스트, 힙(Heap) 크기, 세그먼트 속성 등을 지정한다.

DSP(Microsoft Developer Studio Project File)
프로젝트 정보를 포함하고 있는 파일. 단일 프로젝트 또는 서브 프로젝트를 build하기 위해 사용된다. 다른 사용자가 이 파일을 공유할 수 있지만 지역적으로(locally) makefiles을 익스포트해야 한다.

DSW(Developer Studio Workspace file)
Developer Studio의 환경 정보 파일. 워크스페이스 내의 각 프로젝트에 대한 엔트리 정보를 포함한 파일이다.

EXP(EXPort file)
익스포트되는 힘수와 데이터 정보를 포함한다.

H, HPP, HXX
헤더 파일. 이 파일은 함수의 원형 선언, 클래스 정의, 상수 정의를 위해 사용된다.

HPJ(context-sensitive Help ProJect file)
문맥 감지형 도움말 파일. AppWizard 단계 4 에서 [Context-sensitive help]를 체크하면 자동 생성된다.

INL(INLine function file)
인라인 함수 파일. 이 파일에는 인라인 함수가 정의된다.

MAP(MAP file)
프로그램의 엔트리 포인트, 심볼 이름, 시작 주소, 프로그램에 링크된 정보를 포함한다.

MDP(Microsoft DeveloPer studio file)
VC++ 2.X 버전에서 사용한 VCP 파일을 대체한다.

NCB(Parser information file)
클래스뷰와 컴포넌트 갤러리를 지원하기 위한 정보를 포함한다.

OPT
워크스페이스 환경 설정

PCH(PreCompiled Header file)
컴파일 속도를 향상 시키며 컴파일 옵션 /Yc, /Yu 또는 /YX를 사용할 때 생성된다.

PDB(Program DataBase file)
디버깅 정보 포함. 이 바이너리 파일은 컴파일링, 링킹 과정을 통해 얻어지는 디버깅 정보를 포함한다.

PLG(Program build LoG file)
빌드 로그 파일

RC, RC2(ResourCe source file)
프로그램에서 사용하는 리소스가 정의된 파일. 리소스 컴파일러(RC.EXE)에 의해 컴파일된다. 다이얼로그, 아이콘, 메뉴, 커서 등과 같은 리소스에 대한 정보가 기술되어 있다. RC2 또한 리소스에 대한 정보가 포함된다.

RCT(ResourCe Template file)
리소스 템플릿 파일

RES(RESource compiled file)
리소스 파일(RC)이 컴파일된 바이너리 파일

SBR(Source BRowser file)
소스 브라우저 파일. 이 파일은 소스 파일에 대한 상세한 정보들이 포함된다.

VCP
Workspace information file(VC++ 2.X 버전 또는 이후 버전)은 통합 개발 환경이 종료될 때의 상태 정보를 포함한다.

WSP
Workspace information file(16비트 버전)은 통합 개발 환경이 종료될 때의 상태 정보를 포함한다.


일단 인터넷 프로그래밍을 하기전에 winsock.h를 include하시길 바랍니다.
인터넷에서 서로 정보를 공유하는데 서로의 문(門)역할을 하는것으로 Socket이라는 것이 있다.
FTP서버를 접속할때 주소치고 들어갈수 있는것은 그곳의 소켓이 열려있기 때문이다.
설명하기 힘들군... 걍 대충 컴퓨터간의 문 이라고 생각해라. 강좌 첨쓰는거라 설명어케하는지
몰겠다. 그리구 이 모든것에서는 MFC에서만 된다. API가지구 식은땀 흘리지 말라. 그리구
아래꺼 외울라구 하지말구 걍 보며 따라해라. 안외워진다. 그리구 여기에 오타가 있을지도
--; 있으면 망하는데. 어쨋든 있으면 email로... 이거 쓰고싶어서 쓴게 아니라 협박 받아서
쓰는 거니까 넘 많은걸 바라지 마시고 묻지도 마시오~ -.-

일단 소켓을 열어두어야한다. Create()함수를 쓴다

CSocket Socket;
Socket.Create( 포트번호 );

여기서 포트번호에 열고싶은 번호를 쓴다.
예를들어 FTP서버라면 21번을 쓴다. 많은 프로그램에서 포트를 쓰고있기때문에
약간 흔하지 않은 포트번호를 쓰는것도 좋다.
이제는 다른 곳으로 접속을 하기전에 SOCKADDR_IN으로 접속할 곳의 정보를 제공한다.
설명이 엉성하징... 여기서 SOCKADDR_IN은 winsock.h를 include하지 않았다면 에러난다.
이 아래서 Cokenburger는 아무걸로나 대체해도 된다. 헷갈리지 않도록 색깔로 표시를 했음.

SOCKADDR_IN Cokenburger
memset(&Cokenburger, 0, sizeof (SOCKADDR_IN));
Cokenburger.sin_family=AF_INET;
Cokenburger.sin_port=htons(123); // 포트번호
Cokenburger.sin_addr.s_addr=inet_addr("123.123.123.1"); // IP 주소

이젠 Connect()함수를 이용해서 접속만 하면된다.

if(Socket.Connect((SOCKADDR)
&Cokenburger,sizeof(SOCKADDR_IN)) // 만약 접속이 되었으면
AfxMessageBox("접속 성공이당!");
else
AfxMessageBox("접속 실패당-.-");

아참 또한가지 알아두면 좋은게 있다. 도멘네임에서 IP어드레스를 구하는 것이다.
예를들어 ftp.microsoft.com은 207.46.133.140이다 (맞나?)

HOSTENT *Cokenburger_HostName;
Cokenburger_HostName=gethostbyname("telnet.cokenburger.com"); // 여기다가 도멘네임을
char *Cokenburger_IPaddress = (char *)inetntoa(*(LPIN_ADDR)*(Cokenburger_HostName -> h_addr_list));
// Cokenburger_IPaddress에 IP주소가 실림흠......

그러면 남이 여기로 접속을 하려고 하면 어떻게 받을까. Listen()함수를 쓰면 된다.

역시 MFC가 다 해놔서 정말 간단하다.

if(Socket.Listen())
AfxMessageBox(" 어떤넘이 접속했어 ");
else
AfxMessageBox(" 소켓 에러당 ");

이 Listen()함수는 루프를 돌면서 어떤넘이 접속을 하거나 소켓이 빠가질때까지 계속 돌기 때문에
아주 유용하다.

이제 두번째 CSocket 객체를 맹글어야 한다. 아까 맹근 CSocket객체는 Listen()함수로 더 많은 콜을

받을때 쓸 수가 있다. 여기선 만들때 Accept()함수를 쓴다. 접속 허용하는데 쓰는것이다.
두번째 CSocket으로 접속을 허용한다.

CSocket SecondSocket;
if(Socket.Accept(SecondSocket))
AfxMessageBox(" 접속 허용했당.");
else
AfxMessageBox(" 실패당.");

이젠 데이타를 주고받자
이런식으로 하면 된다. 엄청나게 간단하다

(받기)

int nBytesRead; //받은 것의 바이트수
char cbBuffer[2000]; // 2000바이트의 버퍼 준비다.
nBytesRead=SecondSocket.Receive(cbBuffer, sizeof(cbBuffer));
//버퍼에다가 데이타를 받구 nBytesRead에다가 바이트수를

이제 cbBuffer를 보면 받은 데이타가 있을꺼다.

(주기)

int nBytesWritten; // 쓸것의 바이트수
static char szMessage[]="안녕 소켓으로 보내구 있다. 하하!"); //보낼 것
nBytesWritten = SecondSocket.Send ( szMessage, sizeof(szMessage)); // 보내구 nBytes.Written에 바이트수를

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

// 우리가 사용할 포트를 정의한다. 이 값은 맘대로 사용해도 되지만, 대개 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타임을 독차지 하게 되어 파일 전송이 완료되기 까지 사용
자에게 응답하지 않습니다. 용량이 큰 파일을 전송할 경우에는 전송시간이 걸릴 것이
므로, 사용자는 다운된 줄 알고 프로그램을 죽일 수도 있기 때문에 골칫거리가 아닐 
수 없습니다. 또, 전송받는 부분에서 상황에 따라 무한루프에 빠지는 경우도 있는데, 
전송받은 총량과 전송받아야 할 량을 비교해서 루프를 탈출하는 코드가 추가 되어야
만 완전해 집니다.

정리가 매우 잘 되어있습니다


// Server Socket

// NameSpace 선언
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ServerSideSocket
{
    class ServerClass
    {
        public static Socket Server , Client;
       
        public static byte[] getByte = new byte[1024];
        public static byte[] setByte = new byte[1024];
       
        public const int sPort = 5000;
       
        [STAThread]
        static void Main(string[] args)
        {
            string stringbyte = null;
            IPAddress serverIP = IPAddress.Parse("127.0.0.1");
            IPEndPoint serverEndPoint = new IPEndPoint(serverIP,sPort);
           
          try
          {           
            Server= new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
            Server.Bind(serverEndPoint);
            Server.Listen(10);

            Console.WriteLine("-----------------------------------------------------");
            Console.WriteLine(" 클라이언트의 연결을 기다립니다........ ");
            Console.WriteLine("-----------------------------------------------------");
                                   
            Client = Server.Accept();
   
            if(Client.Connected)
            {
                while(true)
                {
                    Client.Receive(getByte,0,getByte.Length,SocketFlags.None);
                    stringbyte = Encoding.UTF7.GetString(getByte);

                    if (stringbyte != String.Empty)
                    {
                      int getValueLength = 0;
                      getValueLength = byteArrayDefrag(getByte);
                      stringbyte = Encoding.UTF7.GetString(getByte,0,getValueLength+1);
                      Console.WriteLine("수신데이터:{0} | 길이:{1}",stringbyte,getValueLength+1);
                      setByte = Encoding.UTF7.GetBytes(stringbyte);
                      Client.Send(setByte,0,setByte.Length,SocketFlags.None);
                    }
                   
                    getByte = new byte[1024];
                    setByte = new byte[1024];
                }
            }
          }
          catch(System.Net.Sockets.SocketException socketEx)
          {
            Console.WriteLine("[Error]:{0}", socketEx.Message);
          }
          catch(System.Exception commonEx)
          {
            Console.WriteLine("[Error]:{0}", commonEx.Message);
          }
          finally
          {
            Server.Close();
            Client.Close();
          }
        }
       
        public static int byteArrayDefrag(byte[] sData)
        {
          int endLength = 0;
         
          for(int i = 0; i < sData.Length; i++)
          {
            if((byte)sData[i] != (byte)0)
            {
              endLength = i;
            }
          }
         
          return endLength;
        }
    }
}


// Client Socket

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace ClientSideSocket
{
    class ClientClass
    {
        public static Socket socket;
        public static byte[] getbyte = new byte[1024];
        public static byte[] setbyte = new byte[1024];

        public const int sPort = 5000;

        [STAThread]
        static void Main(string[] args)
        {
            string sendstring = null;
            string getstring = null;

            IPAddress serverIP = IPAddress.Parse("127.0.0.1");
            IPEndPoint serverEndPoint = new IPEndPoint(serverIP,sPort);

            socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
            Console.WriteLine("-----------------------------------------------------");
            Console.WriteLine(" 서버로 접속을 시작합니다. [엔터를 입력하세요] ");
            Console.WriteLine("-----------------------------------------------------");
            Console.ReadLine();

            socket.Connect(serverEndPoint);

            if (socket.Connected)
            {
                Console.WriteLine(">> 정상적으로 연결 되었습니다.(전송한 데이터를 입력해주세요)");
            }

            while(true)
            {
                Console.Write(">>");
                sendstring = Console.ReadLine();
               
                if(sendstring != String.Empty)
                {
                  int getValueLength = 0;
                  setbyte = Encoding.UTF7.GetBytes(sendstring);
                  socket.Send(setbyte,0,setbyte.Length,SocketFlags.None);
                  Console.WriteLine("송신 데이터 : {0} | 길이{1}", sendstring, setbyte.Length);
                  socket.Receive(getbyte,0,getbyte.Length,SocketFlags.None);
                  getValueLength = byteArrayDefrag(getbyte);
                  getstring = Encoding.UTF7.GetString(getbyte,0,getValueLength+1);
                  Console.WriteLine(">>수신된 데이터 :{0} | 길이{1}" , getstring , getValueLength+1);
                }
               
                getbyte = new byte[1024];
             }
        }
       
        public static int byteArrayDefrag(byte[] sData)
        {
          int endLength = 0;
         
          for(int i = 0; i < sData.Length; i++)
          {
            if((byte)sData[i] != (byte)0)
            {
              endLength = i;
            }
          }
         
          return endLength;
        }
    }
}

 



파일을 선택해서 원하는 위치에 복사하는 프로그램

fileCopyDlg.cpp

void CFilecopyDlg::OnFrom()
{
 // TODO: Add your control notification handler code here
 m_ctrlListBox.ResetContent();
 char szFilter[] = "All Files(*.*)|*.*||";
 
 CFileDialog dlg(TRUE, NULL, NULL, OFN_ALLOWMULTISELECT, szFilter);
 if(IDOK == dlg.DoModal())
  for(POSITION pos = dlg.GetStartPosition(); pos!=NULL;)
   m_ctrlListBox.AddString(dlg.GetNextPathName(pos));
}

void CFilecopyDlg::OnTo()
{
 // TODO: Add your control notification handler code here
 ITEMIDLIST *pidlBrowse;
 char pszPathname[MAX_PATH];

 BROWSEINFO BrInfo;
 BrInfo.hwndOwner = GetSafeHwnd();
 BrInfo.pidlRoot = NULL;
 
 memset(&BrInfo, 0, sizeof(BrInfo));
 BrInfo.pszDisplayName = pszPathname;
 BrInfo.lpszTitle = "복사할 폴더를 선택하시오";
 BrInfo.ulFlags = BIF_RETURNONLYFSDIRS;

 //대화상자 띄우기
 pidlBrowse = ::SHBrowseForFolder(&BrInfo);

 if(pidlBrowse != NULL)
 {
  //Path를 얻어옴
  SHGetPathFromIDList(pidlBrowse, pszPathname);
  m_strDest = pszPathname;
  UpdateData(FALSE);
 }
 
}

void CFilecopyDlg::OnStartCopy()
{
 // TODO: Add your control notification handler code here
 UpdateData();

 SHFILEOPSTRUCT *pF0 = new SHFILEOPSTRUCT;
 pF0->hwnd = NULL;
 pF0->wFunc = FO_COPY;
 pF0->fFlags = FOF_NOCONFIRMMKDIR;
 pF0->fAnyOperationsAborted = TRUE;
 pF0->lpszProgressTitle = _T("파일 복사중");

 CMemFile file;
 CArchive ar(&file, CArchive::store);

 CString str;
 int nCount = m_ctrlListBox.GetCount();
 char null = 0;
 for(int i=0;i<nCount;i++)
 {
  m_ctrlListBox.GetText(i, str);
  ar.WriteString(str);
  ar.Write(&null, 1);
 }
 ar.Write(&null, 1);
 ar.Close();

 pF0->pFrom = (char *)file.Detach();
 
 //복사될 디렉토리명(Destination)
 pF0->pTo = new char[m_strDest.GetLength()+1];
 strcpy((LPSTR)pF0->pTo, (LPSTR)(LPCTSTR)m_strDest);
 ((LPSTR)pF0->pTo)[m_strDest.GetLength()]=0;

 AfxBeginThread(ThreadFunc, pF0);
}

UINT ThreadFunc(LPVOID pParam)
{
 SHFILEOPSTRUCT *pF0 = (SHFILEOPSTRUCT *)pParam;
 ::SHFileOperation(pF0);

 delete [] (char *)pF0->pFrom;
 delete [] (char *)pF0->pTo;
 delete pF0;

 return 0;
}


fileCopyDlg.h

class CFileCopyDlg : public Cdialog
외부에

UINT ThreadFunc(LPVOID pParam);
를 선언해줌

CString m_strTime;

 

void CTestView::OnTimer(UINT nIDEvent)
{
    if( nIDEvent == 0 )
    {
        time_t now;

        time( &now );

        m_strTime = ctime(&now);

        Invalidate(FALSE);
    }

}


'Programming > MFC' 카테고리의 다른 글

C# 소켓 예제 (Server, Client)  (0) 2010.11.29
MFC 쓰레드(thread) 예제  (0) 2010.11.22
Editbox 컨트롤 사용방법  (0) 2010.11.16
클래스들 간의 상호참조  (0) 2010.11.16
뷰 및 객체간의 상호참조  (0) 2010.11.16

int m_X;
int m_Y;
CString m_Str;

 

SetDlgItemInt( IDC_EDIT_X, m_X );
SetDlgItemInt( IDC_EDIT_Y, m_Y );
SetDlgItemText( IDC_EDIT_STR, m_Str );

 

m_X = GetDlgItemInt( IDC_EDIT_X );
m_Y = GetDlgItemInt( IDC_EDIT_Y );
GetDlgItemText( IDC_EDIT_STR, m_Str );

 

m_ctrEdit.EnableWindow( FALSE );      //비활성화
m_ctrEdit.EnableWindow( TURE );        //활성화

m_ctrEdit.EnableWindow( m_bCheck ); //체크박스 선택 유무에 따른 활성화/비활성화


'Programming > MFC' 카테고리의 다른 글

MFC 쓰레드(thread) 예제  (0) 2010.11.22
현재 시간 얻어오기  (0) 2010.11.16
클래스들 간의 상호참조  (0) 2010.11.16
뷰 및 객체간의 상호참조  (0) 2010.11.16
모달리스 다이얼로그  (0) 2010.11.16

+ Recent posts