티스토리 뷰

지난 포스팅에서 MFC 소켓 프로그래밍을 하는데에 필요한 클래스 두개 서버와 클라이언트의 헤더파일에서 간단하게 각 멤버변수와 멤버함수의 사용법에 대해서 살펴보았다.


이번에는 본격적으로 cpp 파일의 소스를 하나하나 살펴 보겠다.

1
2
3
4
5
6
7
8
CServerSocket::CServerSocket(SOCKET sock) : mSock(sock)
{
}
 
 
CServerSocket::~CServerSocket()
{
}
cs


생성자와 소멸자이다. 소멸자는 기본적으로 주어지는 것을 그대로 사용하고 생성자는 default parameter를 사용하기 때문에 기본생성자를 따로 두지 않고 콜론 초기화를 사용하였다.

만약 생성자를 별도로 호출하지 않으면 mSock은 default parameter에 의해서 INVALID_SOCKET이 들어가게 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bool CServerSocket::Create(unsigned short port, const char*ip)
{
    if (IsValid() == true)
        return true;
 
    mSock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
    if (mSock == INVALID_SOCKET)
        return false;
 
    sockaddr_in addr{ };
 
    addr.sin_addr.S_un.S_addr    = (ip == nullptr) ? INADDR_ANY : inet_addr(ip);
    addr.sin_family                = AF_INET;
    addr.sin_port                = htons(port);
 
    ::bind(mSock, (sockaddr*)&addr, sizeof(addr));
    int ret = ::listen(mSock, SOMAXCONN);
 
    if (ret == SOCKET_ERROR)
    {
        Close();
        return false;
    }
 
    return true;
}
cs

Create 함수이다. 우선적으로 IsValid 함수를 호출하여 소켓이 생성된적이 있는지를 검사하고, 이미 생성되어있다면 반환한다.

SOCKET 변수인 mSock을 위와 같이 초기화 하고, 만약 초기화가 성공적으로 이루어지지 않았다면 false를 반환한다. 함수를 호출하는 곳에서 Create 의 반환값이 false라면 메세지 박스로 소켓이 생성되지 않았습니다 라는 메세지를 띄우면 된다.

그다음 sockaddr_in 구조체를 생성하여 초기화하고, 각각의 멤버변수를 초기화 하는데 매개변수로 전달받은 ip와 port번호를 여기서 사용한다. 삼항연산자로 코드를 짜놓았는데, ip가 널포인터라면 해당 변수에 INADDR_ANY를 대입하고, 아니라면 ip를 넣는다고 보면 된다.

inet_addr() 함수는 숫자와 점으로 이루어진 IP 문자열을 long형의 숫자 IP 주소로 바꾸어 준다.
INADDR_ANY는 간단하게 이야기하면 서버의 IP주소를 자동으로 찾아서 대입해주는 함수이다.

htons 함수는 호스트 바이트 오더를 네트웍 바이트 오더로 바꿔주는 함수인데 이 부분을 파악하려면 little-endian 형식과 big-endian 형식에 대하여 잘 알고있어야 한다. 이 포스트 에서는 쉽게말해 네트웍으로 전달하기위해 숫자를 뒤집는다 정도로만 설명을 하겠다. 반대의 경우에는 ntohs가 되겠다.

다음으로 생성된 소켓에 bind 함수를 통하여 바인드를 한다. bind() 함수는 소켓에 IP주소와 포트번호를 지정해 주는데 (위에서 세팅해놓은 sockaddr_in으로) 이로서 소켓을 통신에 사용할 수 있도록 준비가 되는 것이다.


listen 함수는 소켓에 클라이언트의 접속요청을 기다리도록 설정하는 것인데, accept함수와 헷갈릴수도 있겠다. 다시 설명하겠지만, 말 그대로 listen 함수는 클라이언트의 접속요청을 기다리는 것이고 accept함수는 클라이언트로부터의 접속요청을 승인하는 것이다.


반환값으로 전달받은 ret 변수가 SOCKET_ERROR라면 bind 혹은 listen 에서 에러가 난것이므로 소켓을 닫고 false를 반환한다.


아니라면 성공적으로 소켓을 만들었으므로 true를 반환하면 되겠다.


1
2
3
4
5
6
7
SOCKET CServerSocket::Accept(sockaddr_in *addr, int *addrSize)
{
    if (IsValid() == false)
        return INVALID_SOCKET;
 
    return ::accept(mSock, (sockaddr*)addr, addrSize);
}
cs

위에서 잠깐 언급했던 Accept함수이다. 이 멤버함수를 통해서 클래스는 accept를 하게 된다.
IsValid 함수를 통하여 소켓이 현재 생성되었는지 확인하는데, 소켓이 생성되지 않고 listen 하고 있지 않다면 accept를 할 수 없으므로 코드를 진행하지 않고 반환한다.

accept() 함수는 클라이언트의 접속 요청을 받아드리고 클라이언트와 통신하는 전용 소켓을 생성한다. 다시 이야기하지만 listen은 접속 요청을 기다리는것, accept는 접속 요청을 승인하는 것이다.

1
2
3
4
5
6
7
8
void CServerSocket::Close()
{
    if (IsValid() == true)
    {
        ::closesocket(mSock);
        mSock = INVALID_SOCKET;
    }
}
cs

이 포스트에서 마지막으로 살펴볼 Close 함수이다. Accept함수와 마찬가지로 소켓이 생성되지 않았다면 닫을 소켓도 없으므로 if문을 지나 빠져나가게 되고, 아니라면 closesocket함수를 사용하여 소켓을 닫고 다시 INVALID_SOCKET으로 만들어 준다.

오늘은 우선적으로 서버 클래스에 대한 소스코드만 살펴보았다. 다음으로는 클라이언트 클래스에 대한 소스코드를 리뷰해보겠다.


댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함