[C++] Windows Socket Programming 예제 2

[개발환경]
Windows 10 64bit

[개발도구]
MS Visual Studio 2022(v143, SDK 10.0)
Release x86

Bind Socket

bind() 함수는 소켓을 특정 주소 및 포트에 바인딩하는데 사용할 수 있다.

connect()함수와 유사하게 sockaddr_in 구조체가 필요하다.

int __cdecl main(int argc, char** argv)
{
    WSADATA wsa;
    SOCKET s;
    struct sockaddr_in server;
    const char* message;
    char server_reply[5000];
    int recv_size;

    //Intialising Winsock
    wprintf(L"\\nInitialising Winsock...\\n");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        wprintf(L"Failed. Error code : %d", WSAGetLastError());
        return 1;
    }

    wprintf(L"Initialised.\\n");

    //Create Socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        wprintf(L"Could not create socket : %d", WSAGetLastError());
    }

    wprintf(L"Socket Created\\n");
    
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    if (bind(s, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        wprintf(L"Bind failed with error code : %d ", WSAGetLastError());
    }
    puts("Bind done");

    closesocket(s);
    WSACleanup();

    return 0;
}

Listen

소켓을 포트에 바인딩한 후 다음 작업은 연결을 수신하는 것이다. 이를 위해 소켓을 리스닝 모드로 설정한다.

//Listen
listen(s , 3);

Accept connection

int __cdecl main(int argc, char** argv)
{
    WSADATA wsa;
    SOCKET s, new_socket;
    struct sockaddr_in server, client;
    int c;
    

    //Intialising Winsock
    wprintf(L"\\nInitialising Winsock...\\n");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        wprintf(L"Failed. Error code : %d", WSAGetLastError());
        return 1;
    }

    wprintf(L"Initialised.\\n");

    //Create Socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        wprintf(L"Could not create socket : %d", WSAGetLastError());
    }

    wprintf(L"Socket Created\\n");
    
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //Bind
    if (bind(s, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        wprintf(L"Bind failed with error code : %d ", WSAGetLastError());
    }
    puts("Bind done");

    //Listening to incoming connections
    listen(s, 3);

    //Accept and incoming connection
    puts("Waiting for incoming connections...");

    c = sizeof(struct sockaddr_in);
    new_socket = accept(s, (struct sockaddr*)&client, &c);
    if (new_socket == INVALID_SOCKET) {
        wprintf(L"accept failed with error code : %d", WSAGetLastError());
    }

    puts("Connection accepted");

    closesocket(s);
    WSACleanup();

    return 0;
}
  • 실행결과

연결 대기 중인 8888포트로 telnet 연결을 시도하면 클라이언트 프로그램은 “Connection accepted” 메시지와 함께 프로세스가 종료된다.

!참고 연결 시점에 클라이언트의 IP 정보를 알 수 있다.

클라이언트의 sock 구조체에 IP 주소가 담긴다. 아래의 코드를 활용한다

char *client_ip = inet_ntoa(client.sin_addr);
int client_port = ntohs(client.sin_port);

포트를 바인딩하고 연결을 대기한 후 연결이 되었을 때 즉시 종료하는 프로그램을 작성하였다. 하지만 이는 생산적이지 못하다. 연결이 성립된 이후에는 많은 동작을 수행할 수 있다. 클라이언트에서 응답을 전송하도록 수정한다.

puts("Connection accepted");

    message = "Hello Client! I have received your connection. But I have to go now, bye\\n";
    send(new_socket, message, strlen(message), 0);

    getchar();

    closesocket(s);
    WSACleanup();

Live Server

int __cdecl main(int argc, char** argv)
{
    WSADATA wsa;
    SOCKET s, new_socket;
    struct sockaddr_in server, client;
    int c;
    const char* message;

    //Intialising Winsock
    wprintf(L"\\nInitialising Winsock...\\n");
    if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) {
        wprintf(L"Failed. Error code : %d", WSAGetLastError());
        return 1;
    }

    wprintf(L"Initialised.\\n");

    //Create Socket
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        wprintf(L"Could not create socket : %d", WSAGetLastError());
    }

    wprintf(L"Socket Created\\n");
    
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //Bind
    if (bind(s, (struct sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) {
        wprintf(L"Bind failed with error code : %d ", WSAGetLastError());
    }
    puts("Bind done");

    //Listening to incoming connections
    listen(s, 3);

    //Accept and incoming connection
    puts("Waiting for incoming connections...");

    c = sizeof(struct sockaddr_in);

    while ((new_socket = accept(s, (struct sockaddr*)&client, &c)) != INVALID_SOCKET) {
        puts("Connection accepted");

        //reply to the client
        message = "Hello Client! I have received your connection. But I have to go now, bye\\n";
        send(new_socket, message, strlen(message), 0);
    }

    if (new_socket == INVALID_SOCKET) {
        wprintf(L"accept failed with error code : %d", WSAGetLastError());
        return 1;
    }

    closesocket(s);
    WSACleanup();

    return 0;
}

이 서버는 다운되지 않고 이제 항상 동작한다. 여러 연결 요청이 들어와도 새로운 소켓을 생성하여 연결을 유지할 수 있다.

반응형

'Reversing' 카테고리의 다른 글

[Frida] Windows Socket Program hooking  (0) 2022.05.26
[Cheat Engine] Socket Program 분석  (0) 2022.05.21
[C++] Windows Socket Programming 예제  (0) 2022.05.13
[Frida] Hooking send(), recv()  (0) 2022.05.10
[Frida] Binary Hooking 2  (0) 2022.05.03