7.4.3 EH 메신저 가입 라이브러리 설계 및 구현 [TCP/IP 소켓 프로그래밍 with 윈도우즈]

가입 서비스에서 제공하는 기능은 가입 요청과 탈퇴 요청입니다. 이 때 Peer와 가입 서버가 공통적으로 필요한 라이브러리를 설계 및 구현합시다.

[그림 7.15] 가입 관련 노드 컴포넌트 다이어그램

 가입 관련 노드는 가입 요청 및 탈퇴 요청을 시도하는 Client와 가입 서버가 있습니다. Client에는 Peer 프로그램이 가입 라이브러리와 EHPacket 라이브러리를 사용하고 가입 라이브러리는 EHPacket 라이브러리를 사용합니다. 그리고 가입 서버는 RegSVC 프로그램이 가입 라이브러리와 EHPacket 라이브러리를 사용하고 가입 라이브러리는 EHPacket 라이브러리를 사용합니다.

이제 가입 라이브러리를 작성합시다. 가입 라이브러리는 RegLib 이름으로 만들게요. 가입 라이브러리에는 가입 요청하는 RegReq 클래스와 RegReponse 클래스, 탈퇴 요청하는 UnRegReq 클래스로 구성할게요.

먼저 EH 메신저 솔루션에 RegLib 이름으로 Win2 프로젝트를 추가하고 DLL 형태로 만드세요.

아키텍쳐링 단계에서 정의한 메시지 종류를 구분하기 위한 메시지 아이디를 정의할 헤더 파일을 Common 프로젝트에 추가합니다. 헤더 파일 이름은 ehmsg.h 라고 정할게요.

#pragma once

가입 관련 메시지 아이디를 정의합시다.

#define MID_REGREQ        0x00010001
#define MID_REGRES        0x00010002
#define MID_UNREGREQ      0x00010003

로긴 관련 메시지 아이디를 정의합시다.

#define MID_LOGINREQ      0x00020001
#define MID_LOGINRES      0x00020002
#define MID_LOGOUTREQ     0x00020003

상태 관련 메시지 아이디를 정의합시다.

#define MID_KEEPALIVE     0x00030001
#define MID_USERINFO      0x00030002
#define MID_IDEXIST       0x00040001
#define MID_IDEXISTACK    0x00040002

데이터 계층 관련 메시지 아이디를 정의합시다.

#define MID_ADDUSERINFO   0x00040003
#define MID_USERSTS       0x00040004
#define MID_USERSTSACK    0x00040005
#define MID_ISCORRECT     0x00040006
#define MID_ISCORRECTACK  0x00040007
#define MID_CHANGESTS     0x00040008
#define MID_REMOVEUSER    0x00040009

서비스에서 사용할 포트를 정의합시다.

#define FEND_PORT         10200
#define REG_PORT          10202
#define LOG_PORT          10204
#define STS_PORT          10206
#define DBM_PORT          10208

가입 라이브러리에는 가입 요청 메시지와 가입 요청 응답 메시지, 탈퇴 요청 메세지를 정의할 것입니다. 이들에서 공통으로 포함해서 사용할 Reg.h 파일을 Common 프로젝트에 추가합니다.

#pragma once

EHPacketLib를 사용해야 하므로 EHPacket.h 파일을 포함하고 EHPacketLib 파일을 참조 추가합니다.

#include "..\\common\\EHPacket.h"
#pragma comment(lib,"..\\Debug\\EHPacketLib")

가입 라이브러리 내부와 사용할 곳에 맞게 __declspec 표현을 할 수 있게 매크로 REG_DLL를 정의합니다.

#ifdef REG038IUPHDFJKDIFEWFKDSIFHWELKJFHOISUDFHK
#define REG_DLL  __declspec(dllexport)
#else
#define REG_DLL  __declspec(dllimport)
#endif

그리고 가입 요청에 관한 클래스를 정의할 RegReq.h 파일을 Common 프로젝트에 추가합니다.

#pragma once
#include "Reg.h"
#pragma warning(disable:4251)
#include <string>
using namespace std;

RegReq 클래스를 추가합니다.

class REG_DLL RegReq
{

가입 요청에는 아이디, 비밀 번호, 이름이 있어야 합니다.

    string id;
    string pw;
    string name;
public:

가입 요청 메시지를 보내는 곳에서 사용할 생성자를 선언합니다.

    RegReq(string id,string pw,string name);

받은 패킷을 가지고 RegReq 개체를 만드는 생성자를 선언합니다.

    RegReq(EHPacket *ep);

아이디와 이름, 비밀 번호를 반환하는 메서드를 선언합니다.

    string GetId()const;
    string GetName()const;
    string GetPW()const;

RegReq 메시지를 소켓으로 전송하는 메서드를 선언합니다.

    void Serialize(SOCKET sock);
};

RegLib 프로젝트에 RegReq.cpp 소스 파일을 추가합니다.

매크로 REG_DLL이 __declspec(dllexport) 표현으로 정의할 수 있도록 약속한 매크로를 정의합니다.

#define REG038IUPHDFJKDIFEWFKDSIFHWELKJFHOISUDFHK

RegReq 클래스를 정의한 헤더 파일과 메시지 아이디를 정의한 헤더 파일을 포함합니다.

#include "..\\common\\RegReq.h"
#include "..\\Common\\ehmsg.h"

가입 요청을 보내는 곳에서 사용할 생성자를 구현합시다.

RegReq::RegReq(string id,string pw, string name)
{

입력 인자로 전달받은 값으로 멤버 필드를 설정합니다.

    this->id = id;
    this->pw = pw;
    this->name = name;
}

수신한 EHPacket으로 RegReq 개체를 만드는 생성자를 구현합시다.

RegReq::RegReq(EHPacket *ep)
{

id 길이를 얻어온 후에 id를 얻어옵니다.

    int idlen = 0;
    char id[256]="";
    ep->DePacketize(&idlen,sizeof(idlen));
    ep->DePacketize(id,idlen);

같은 방법으로 비밀 번호와 이름을 얻어옵니다.

    int pwlen=0;
    char pw[256]="";
    ep->DePacketize(&pwlen,sizeof(pwlen));
    ep->DePacketize(pw,pwlen);
    int namelen = 0;
    char name[256]="";
    ep->DePacketize(&namelen,sizeof(namelen));
    ep->DePacketize(name,namelen);

얻어온 값으로 멤버 필드를 설정합니다.

    this->id = id;
    this->pw = pw;
    this->name = name;
}

아이디와 이름 비밀 번호를 반환하는 메서드를 구현합시다.

string RegReq::GetId()const
{
    return id;    
}
string RegReq::GetName()const
{
    return name;    
}
string RegReq::GetPW()const
{
    return pw;    
}

소켓으로 전송하는 메서드를 구현합시다.

void RegReq::Serialize(SOCKET sock)
{

먼저 MID_REGREQ 아이디를 인자로 EHPacket을 생성합니다.

    EHPacket ep(MID_REGREQ);

아이디 길이를 얻어온 후에 아이디 길이와 아이디를 패킷에 캡슐화합니다. 여기에서는 종료 문자를 포함하여 캡슐화하기 위해 아이디 길이를 1 더하였습니다.

    int idlen = id.length() + 1;
    ep.Packetize(&idlen,sizeof(idlen));
    ep.Packetize((void *)id.c_str(),idlen);

비밀 번호와 이름도 같은 원리로 패킷에 캡슐화합니다.

    int pwlen = pw.length()+1;
    ep.Packetize(&pwlen,sizeof(pwlen));
    ep.Packetize((void *)pw.c_str(),pwlen);
    int namelen = name.length() + 1;
    ep.Packetize(&namelen,sizeof(namelen));
    ep.Packetize((void *)name.c_str(),namelen);

패킷을 소켓으로 직렬화합니다. 참고로 직렬화란 프로세스의 데이터를 다른 물리 매체에 선형 방법으로 전달하는 것을 말합니다.

    ep.Serialize(sock);
}

가입 요청의 응답인 RegResponse 메시지를 정의합시다. 먼저 common 프로젝트에 RegRes.h 파일을 추가합시다.

#pragma once
#include "Reg.h"

가입 요청의 결과는 성공과 실패 두 가지가 있습니다. 이를 매크로 상수로 정의합시다.

#define REG_RES_OK   0
#define REG_RES_FAIL 1

RegRes 클래스를 추가합니다.

class REG_DLL RegRes
{

RegRes 메시지에는 가입 요청 결과가 필요합니다.

    int result;
public:

가입 요청 결과를 보내는 곳에서 사용할 생성자를 선언합니다.

    RegRes(int result);

수신한 패킷으로 RegRes 개체를 생성하는 생성자를 선언합니다.

    RegRes(EHPacket *ep);

결과를 반환하는 메서드를 선언합니다.

    int GetResult()const;

소켓으로 메시지를 전송하는 메서드를 선언합니다.

    void Serialize(SOCKET sock);
};

RegLib 프로젝트에 RegRes.cpp 소스 파일을 추가하세요.

#define REG038IUPHDFJKDIFEWFKDSIFHWELKJFHOISUDFHK
#include "..\\common\\RegRes.h"
#include "..\\Common\\ehmsg.h"

생성자를 구현합시다.

RegRes::RegRes(int result)
{
    this->result = result;
}
RegRes::RegRes(EHPacket *ep)
{
    ep->DePacketize(&result,sizeof(result));
}

가입 요청 결과를 반환하는 메서드를 구현합시다.

int RegRes::GetResult()const
{
    return result;
}

가입 요청 결과를 소켓으로 직렬화하는 메서드를 구현합시다.

void RegRes::Serialize(SOCKET sock)
{
    EHPacket ep(MID_REGRES);
    ep.Packetize(&result,sizeof(result));
    ep.Serialize(sock);
}

같은 원리로 탈퇴 요청 메시지도 작성합니다. 다음은 Common 프로젝트에 추가할 UnRegReq.h 파일의 내용입니다.

#pragma once
#include "Reg.h"
#pragma warning(disable:4251)
#include <string>
using namespace std;
class REG_DLL UnRegReq
{
    string id;
public:
    UnRegReq(string id);
    UnRegReq(EHPacket *ep);
    string GetId()const;
    void Serialize(SOCKET sock);
};

다음은 RegLib 프로젝트에 추가할 UnRegReq.cpp 파일의 내용입니다.

#define REG038IUPHDFJKDIFEWFKDSIFHWELKJFHOISUDFHK
#include "..\\common\\UnRegReq.h"
#include "..\\Common\\ehmsg.h"
UnRegReq::UnRegReq(string id)
{
    this->id = id;
}
UnRegReq::UnRegReq(EHPacket *ep)
{
    int idlen = 0;
    char id[256]="";
    ep->DePacketize(&idlen,sizeof(idlen));
    ep->DePacketize(id,idlen);
    this->id = id;
}
string UnRegReq::GetId()const
{
    return id;
}
void UnRegReq::Serialize(SOCKET sock)
{
    EHPacket ep(MID_UNREGREQ);
    int idlen = id.length() + 1;
    ep.Packetize(&idlen,sizeof(idlen));
    ep.Packetize((void *)id.c_str(),idlen);
    ep.Serialize(sock);
}