Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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 31
Archives
Today
Total
05-16 04:51
관리 메뉴

nomad-programmer

[Programming/Network] UDP를 이용한 Time Server & Client 본문

Programming/Network

[Programming/Network] UDP를 이용한 Time Server & Client

scii 2021. 2. 17. 21:20

클라이언트에서 서버로 현재 시간을 알려달라는 명령을 요청하면 서버에서 이를 처리하여 서버의 현재 시간을 클라이언트에게 보내주는 간단한 타임서버이다.

타임서버의 존재는 모든 클라이언트의 로컬 시간이 각기 다를 수 있는 문제점이 잠재적으로 있을 수 있기에 타임 서버를 둠으로써 모든 클라이언트의 시간을 동일하게 맞출 때 존재 의미가 있다.


Server 구현

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <WinSock2.h>

#pragma comment(lib, "ws2_32.lib")

#define PORT        3333
#define BUF_SIZE    100


int main(int argc, char* argv[]) {
    WSADATA wsaData;
    SOCKET servSock;
    SOCKADDR_IN servAdr, clntAdr;
    char message[BUF_SIZE];
    char curTime[BUF_SIZE];
    int strLen;
    int clntAdrSz;
    time_t t;
    struct tm tm;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        fputs("wsastartup() error\n", stderr);
        exit(1);
    }

    // 소켓 설정, SOCK_DGRAM으로 안하여 UDP로 설정됨.
    servSock = socket(PF_INET, SOCK_DGRAM, 0);
    if (servSock == INVALID_SOCKET) {
        WSACleanup();
        fputs("upd 소켓 생성 실패\n", stderr);
        exit(1);
    }

    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    servAdr.sin_addr.s_addr = htonl(INADDR_ANY);
    servAdr.sin_port = htons(PORT);
    
    if (bind(servSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR) {
        WSACleanup();
        fputs("bind error\n", stderr);
        exit(1);
    }

    while (1) {
        clntAdrSz = sizeof(clntAdr);

        // recvfrom : 데이터 수신 함수
        // servSock : 데이터를 수신할 때 사용할 소켓의 파일 디스크립터
        // message : 수신할 데이터를 저장할 버퍼
        // BUF_SIZE : 수신할 수 있는 최대 바이트 수
        // 0 : flags이며 옵션 설정하는데 필요한 인자. 일반적으로 0
        // (SOCKADDR*)&clntAdr : 주소 정보 구조체 변수의 포인터. 함수 호출이 끝나면 데이터를 전송한 호스트의 주소 정보로 채워짐
        // &clntAdrSz : (SOCKADD*)&clntAdr 포인터가 가리키고 있는 구조체 변수의 크기
        strLen = recvfrom(servSock, message, BUF_SIZE, 0, (SOCKADDR*)&clntAdr, &clntAdrSz);
        // 클라이언트의 fgets함수로 인하여 마지막 문자가 '\n' 문자이므로 이것을 null 문자로 변경
        message[strLen - 1] = '\0';
        // 만약 클라이언트에서 요청한 명령이 "show time" 이라면 서버의 현재 시각 정보를 클라이언트로 전달
        if (!strcmp(message, "show time")) {
            t = time(NULL);
            tm = *localtime(&t);
            // 서버 현재 시각을 curTime 문자열 배열에 저장
            sprintf(curTime, "now: %04d-%02d-%02d %02d:%02d:%02d\n",
                tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);

            // sendto : 데이터 송신 함수
            // 서버의 현재 시간 정보를 전송하는 부분
            // servSock : 데이터 전송 시 사용할 소켓의 파일 디스크립터 설정
            // curTime : 현재 시간 정보가 담긴 문자열 배열 전달
            // strlen(curTime) : 몇 바이트를 전송할 것인지 설정
            // 0 : flags이며 옵션 설정한느데 필요한 인자. 일반적으로 0을 설정
            // (SOCKADDR*)&clntAdr : clntAdr을 SOCKADDR포인터형으로 형 변환 후 전달 (전송하고자 하는 곳의 주소 정보)
            // sizeof(clntAdr) : (SOCKADDR*)&clntAdr포인터가 가리키고 있는 구조체의 변수 크기
            sendto(servSock, curTime, strlen(curTime), 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr));
        }
        // 클라이언트에서 들어온 명령이 "show time"이 아닐 시 에러 메시지 전송
        else {
            strcpy(message, "명령어를 잘못 입력하였습니다. (cmd ex: show time)\n");
            sendto(servSock, message, strlen(message), 0, (SOCKADDR*)&clntAdr, sizeof(clntAdr));
        }
    }

    closesocket(servSock);
    WSACleanup();

    return 0;
}

Client 구현

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

#pragma comment(lib, "ws2_32.lib")

#define PORT        3333
#define BUF_SIZE    100
#define IP_ADDR     "127.0.0.1"


int main() {
    WSADATA wsaData;
    SOCKET sock;
    SOCKADDR_IN servAdr;
    char message[BUF_SIZE];
    int strLen;

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        fputs("wsastartup() error\n", stderr);
        exit(1);
    }

    // 소켓 설정, SOCK_DGRAM으로 안하여 UDP로 설정됨.
    sock = socket(PF_INET, SOCK_DGRAM, 0);
    if (sock == INVALID_SOCKET) {
        WSACleanup();
        fputs("upd 소켓 생성 실패\n", stderr);
        exit(1);
    }

    memset(&servAdr, 0, sizeof(servAdr));
    servAdr.sin_family = AF_INET;
    servAdr.sin_addr.s_addr = inet_addr(IP_ADDR);
    servAdr.sin_port = htons(PORT);

    // 서버와 연결
    connect(sock, (SOCKADDR*)&servAdr, sizeof(servAdr));

    while (1) {
        fputs("command(q to quit): ", stdout);
        fgets(message, sizeof(message), stdin);
        if (!strcmp(message, "q\n")) {
            break;
        }

        // send : 데이터 송신 함수
        // sock : 데이터 전송 시 사용할 소켓의 파일 디스크립터 설정
        // message : 전송할 문자열 데이터
        // strlen(message) : 전송할 문자열의 크기
        // flags : 전송할 데이터 또는 읽는 방법에 대한 option.
        send(sock, message, strlen(message), 0);

        // recv : 데이터 수신 함수
        // sock : 데이터를 수신할 때 사용할 소켓의 파일 디스크립터
        // message : 수신한 데이터를 저장할 버퍼
        // sizeof(message) - 1 : 읽을 수 있는 버퍼의 최대 크기
        // 0 : 읽을 데이터 유형 또는 읽는 방법에 대한 option
        strLen = recv(sock, message, sizeof(message) - 1, 0);

        // 서버에서 보내온 문자열 데이터에 null 문자 삽입
        message[strLen] = '\0';
        printf("server에서 보내온 시간: %s\n", message);
    }

    closesocket(sock);
    WSACleanup();

    return 0;
}


/* 실행 결과

command(q to quit): asdflk
server에서 보내온 시간: 명령어를 잘못 입력하였습니다. (cmd ex: show time)

command(q to quit): 12oiweuio
server에서 보내온 시간: 명령어를 잘못 입력하였습니다. (cmd ex: show time)

command(q to quit): show time
server에서 보내온 시간: now: 2021-02-17 21:14:18

command(q to quit): show time
server에서 보내온 시간: now: 2021-02-17 21:14:29

command(q to quit): q

*/

clnt_main.c
0.00MB
serv_main.c
0.00MB

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

[Programming/Network] TCP/IP 스택  (0) 2020.10.04
Comments