[C] Echo server/client

대학교 이후로 거의 접해지 못했던 C 를 다시 시작하고 있습니다.

다시 시작하는 이유는 회사에서 C 개발자가 필요하기 때문입니다. 처음이라 진입이 쉽지는 않겠지만 포기하지 않고 계속 하다 보면 언젠가는 되리라 생각합니다.

예제는 http://www.joinc.co.kr/w/Site/win_network_prog/doc/winsock_basic 의 코드를 참조하며 모르는 부분에 주석을 추가하였습니다.

지속적으로 이전에 java로 만들었던 채팅 서버, 메신저 서버 등을 만들어볼 생각입니다.

개발환경

  • IDE: Visual Studio 2012
  • OS: Windows 10

[echo_server.c]

/*
File: echo_server.c
Desc: 에코 서버.
        서버는 클라이언트의 접속 요청을 수락한 후 송신된 메시지를 출력 및 동일 메시지 송신 후 클라이언트의 연결을 종료하는 프로그램
Date: 2016. 04. 27
Author: coozplz@gmail.com
*/
#include <winsock.h>
#include <stdio.h>

// Winsock 라이브러리 로딩을 위한 코드
#pragma comment(lib, "Ws2_32.lib");


#define MAX_PACKET_LEN 512
#define PORT 5552
#define BACK_LOG_SIZE 5

int main()
{
    WSADATA wsaData;
    int status;
    int socketLength;
    int readSize, writeSize;
    SOCKET endPointSocket, clientSocket;

    struct sockaddr_in SocketInfo, ClientSocketInfo;

    char buffer[MAX_PACKET_LEN];

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        printf("WSAStartup error \n");
        return 0;
    }

    endPointSocket = socket(AF_INET, SOCK_STREAM, 0);

    if (endPointSocket == INVALID_SOCKET) {
        printf("endPointSocket is invalid \n");
        return 0;
    }

    printf("Server is running on port %d\n", PORT);

    // 구조체를 초기화한다.
    ZeroMemory(&SocketInfo, sizeof(struct sockaddr_in));
    SocketInfo.sin_family = AF_INET;
    // htons(): Host 시스템에서 Network 로 short 형 데이터를 보낼 때 바이트 오더를 바꿔주는 함수
    SocketInfo.sin_port = htons(PORT); 

    // htonl(): long형 데이터의 바이트 오더를 바꿔주는 함수.
    // INADDR_ANY 는 OS에 설정되어 있는 아이피 어느것이나 바인딩 한다는 의미
    SocketInfo.sin_addr.S_un.S_addr = htonl(INADDR_ANY); 

    status = bind(endPointSocket, (struct sockaddr*)&SocketInfo, sizeof(struct sockaddr_in));

    if (status == SOCKET_ERROR) {
        printf("failed to bind socket \n");
        return 0;
    }

    //
    // backlog 인자는 아직 미결인 연결들에 대한 큐의 늘어날 수 있는 최대 길이를 정의한다.
    // 큐에 도착한 연결 요청들이 꽉 찬다면 클라이언트는 ECONNREFUSED를 가리키는 에러를 받거나 만일 하위 프로토콜이 재전송을 지원한다면, 요청은 재시도가 성공되도록 하기 위해 무시된다.
    //
    status = listen(endPointSocket, BACK_LOG_SIZE);

    if (status == SOCKET_ERROR) {
        printf("failed to listen socket \n");
        return 0;
    }

    while (TRUE) {
        // 클라이언트 구조체 초기화
        ZeroMemory(&ClientSocketInfo, sizeof(struct sockaddr_in));
        socketLength = sizeof(struct sockaddr_in);

        clientSocket = accept(endPointSocket, (struct sockaddr *) &ClientSocketInfo, &socketLength);
        if (clientSocket == INVALID_SOCKET) {
            printf("client socket is invalid\n");
            closesocket(clientSocket);
            continue;
        }

        // inet_ntoa() : 네트워크 바이트 순서의 32비트 값을 Dotted-Decimal notation 의 주소값으로 변환
        // 예 0x6601a8c0: 102.1.168.192
        printf("[Accepted] %s:%d\n", inet_ntoa(ClientSocketInfo.sin_addr), (int)ntohs(ClientSocketInfo.sin_port));
        ZeroMemory(&buffer, sizeof(buffer));
        readSize = recv(clientSocket, (void *)buffer, MAX_PACKET_LEN, 0);

        if (readSize > 0) {
            printf("Message from client > %s\n", buffer);
            writeSize = send(clientSocket, buffer, readSize, 0);
        }
        else {
            printf("Error on reading\n");
        }
        closesocket(clientSocket);
        printf("[Closed] %s:%d\n", inet_ntoa(ClientSocketInfo.sin_addr), (int)ntohs(ClientSocketInfo.sin_port));
    }
    closesocket(endPointSocket);
    WSACleanup();
    return 0;
}

[echo_client.c]

/*
File: echo_client.c
Desc: 에코 클라이언트.
      서버에 접속 요청을 한 후 사용자가 입력한 메시지를 서버에 송신 후 응답 메시지를 출력한 후 소켓 연결이 종료되었는지 판단하고 소켓 연결을 종료한다.
Date: 2016. 04. 27
Author: coozplz@gmail.com
*/

#include <stdio.h>
#include <WinSock2.h>

// Winsock 라이브러리 로딩을 위한 코드
#pragma comment(lib, "Ws2_32.lib");


#define PORT_NUM 5552
#define MAX_LEN 1024

int main(int argc, char **argv)
{
    SOCKET socketFd;
    WSADATA wsaData;
    struct sockaddr_in addr;

    char buffer[MAX_LEN];
    char receiveBuffer[MAX_LEN];

    int status = 0;
    int readSize = 0;
    status = WSAStartup(MAKEWORD(2, 2), &wsaData);

    if (status != NO_ERROR) {
        printf("WSAStart has error\n");
        return 1;
    }

    socketFd = socket(AF_INET, SOCK_STREAM, 0);

    if (socketFd == INVALID_SOCKET) {
        printf("socketFd is invalid\n");
        return 1;
    }
    ZeroMemory(&addr, sizeof(struct sockaddr_in));

    addr.sin_family = AF_INET;
    //
    // inet_addr() : Dotted-decimal 타입을 네트워크 32바이트 순서로 변경
    //
    addr.sin_addr.S_un.S_addr = inet_addr("192.168.10.52");
    addr.sin_port = htons(PORT_NUM);

    status = connect(socketFd, (struct sockaddr_in *)&addr, sizeof(addr));

    if (status == SOCKET_ERROR) {
        printf("connection failed...\n");
        return 1;
    }

    while (TRUE) {
        printf("Connection is established...\n");
        printf("> ");
        fgets(buffer, MAX_LEN - 1, stdin);
        if (strncmp(buffer, "quit\n", 5) == 0) {
            break;
        }

        printf("SEND: %s", buffer);
        send(socketFd, (void *)buffer, strlen(buffer), 0);
        ZeroMemory(&receiveBuffer, sizeof(receiveBuffer));
        readSize = recv(socketFd, (void *)receiveBuffer, MAX_LEN, 0);
        printf("READ = %s\n", receiveBuffer);
        //
        // 소켓연결이 종료되었는지 판단하기 위해 다시 recv() 함수를 호출한다.
        //
        ZeroMemory(&receiveBuffer, sizeof(receiveBuffer));
        readSize = recv(socketFd, (void *)receiveBuffer, MAX_LEN, 0);
        if (readSize <= 0) {
            printf("socket is disconnected by server!!\n");
            break;
        }
    }
    closesocket(socketFd);
    WSACleanup();

    printf("Press any key to exit program.");
    getchar();
    return 0;
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s