본문 바로가기

Network

Socket Programming 실습

정말 간단한 에코서버를 TCP소켓과, UDP소켓으로 구현해 보았다. 소켓 API를 어떻게 사용하는지 정도만 알아보는데 참고하면 좋을듯 하다.

TCP Socket

Client

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>

#define BUF_SIZE 128

int main(int argc, char *argv[]) {
    /*
     * 1. local variable 선언
     *
     * s           : socket descriptor
     * server_addr : server 의 주소를 정의하는 구조체
     * ip_addr     : server 의 ip address
     * buf         : input, receiver buffer
     */
    int sd;
    struct sockaddr_in server_addr;
    char *ip_addr = "127.0.0.1";
    char buf[BUF_SIZE+1];

    /*
     * 2. socket 생성
     *
     * PF_INET     : 인터넷
     * SOCK_STREAM : TCP 타입의 socket
     */
    if ((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        printf("can't create socket\n");
        return -1;
    }

    /*
     * 3. setsockopt 를 통한 socket 의 옵션 설정
     */
    struct linger lingerOpt;
    lingerOpt.l_onoff = 1;
    lingerOpt.l_linger = 5;
    setsockopt(sd, SOL_SOCKET, SO_LINGER, &lingerOpt, sizeof(lingerOpt));

    /*
     * 4. server_addr 초기화
     *
     * #include <string.h>
     * void bzero(void *s, size_t n);
     * byte string s의 첫바이트부터 n 바이트까지 0으로 채운다.
     */
    bzero((char *) &server_addr, sizeof(server_addr));

    /*
     * 5. server 주소 체계 설정
     */
    server_addr.sin_family = AF_INET;

    /*
     * 6. server ip address 설정
     *
     * #include <arpa/inet.h>
     * in_addr_t inet_addr(const char *cp);
     * "a.b.c.d"형태의 IPv4 주소 체계를 network byte order의 binary 주소로 변환한다.
     * 주로, socket address family가 AF_INET 타입인 경우에 사용한다.
     */
    server_addr.sin_addr.s_addr = inet_addr(ip_addr);

    /*
     * 7. server port 번호 설정
     * #include <arpa/inet.h>
     * uint32_t htonl (uint32_t hostlong);
     * long 타입 데이터를 host 시스템의 byte order 에서 네트워크의 byte order 로 변경해주는 함수.
     */
    server_addr.sin_port = htons(8080);

    /*
     * 8. server에 존재하는 socket과 연결 시도, 성공시 0 리턴
     * 실패시 -1을 리턴하며, 전역변수 errno에 에러코드가 들어있게 된다.
     */
    if (connect(sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("can't connect : %d\n", errno);
        return -1;
    }

    /*
     * 9. connect 를 통해 성공적으로 세션이 맺어지면, 이제 Server 와 소켓 통신이 가능하다.
     */
    while(1) {
        // 사용자 입력
        printf("Input : ");
        if (fgets(buf, BUF_SIZE, stdin)) {
            buf[strlen(buf)-1] = '\0';
        } else {
            printf("fgets error\n");
            break;
        }

        if(strcmp(buf, "exit") == 0) {
            printf("tcp client close socket!\n");
            break;
        }

        // 입력값 서버로 전송
        if (send(sd, buf, strlen(buf), 0) < 0) {
            printf("send error : %d\n", errno);
            break;
        }

        // 서버로부터 데이터 수신
        if (recv(sd, buf, sizeof(buf)-1, 0) < 0) {
            printf("recv error : %d\n", errno);
            break;
        }
        buf[BUF_SIZE] = '\0';

        printf("Received Message : %s\n", buf);
    }

    //shutdown(sd, SHUT_WR);
    close(sd);
}

Server

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>

#define BUF_SIZE 128

int main(int argc, char *argv[]) {
    /*
     * 1. local variable 선언
     *
     * server_sd       : server socket 번호
     * client_sd       : client socket 번호
     * len_out         : client 로부터 받은 데이터의 길이
     * port            : socket 에 바인딩 될 서버의 port 번호
     * buf             : client 와의 통신에서 사용할 버퍼
     * server_addr     : server socket 주소정보를 담고 있는 구조체
     * client_addr     : client socket 주소정보를 담고 있는 구조체
     * client_addr_len : client_addr 변수의 길이
     */
    int server_sd, client_sd; // socket 번호
    int len_out;
    int port;
    char buf[BUF_SIZE+1];

    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;
    port = 8080;

    /*
     * 2. socket 생성
     */
    if ((server_sd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
        printf("can't open stream socket");
        return 0;
    }

    /*
     * 3. server_addr 초기화
     */
    bzero((char *)&server_addr, sizeof(server_addr));

    /*
     * 4. server 주소 체계 설정
     * 5. server ip address 설정
     * 6. server port 번호 설정
     */
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(port);


    /*
     * 7. socket 과 socket 주소를 바인딩
     */
    if (bind(server_sd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("can't bind local address\n");
        return 0;
    }

    /*
     * 8. 소켓을 수동 대기모드로 세팅
     *
     * listen()의 두번째 파라미터는, 대기가능한 총 client connection 수이다.
     * 설정된 값보다 많은 수의 연결이 왔을 때, 설정값 이후의 connection은 거절하여 클라이언트가 바로 알 수 있게 한다.
     */
    listen(server_sd, 5);

    /*
     * 9. accept()에서 block되며, client의 연결을 기다린다.
     */
    while(1) {
        printf("Waiting connection request...\n");

        client_addr_len = sizeof(client_addr);
        client_sd = accept(server_sd, (struct sockaddr *)&client_addr, &client_addr_len);
        if (client_sd < 0) {
            printf("accept failed\n");
            break;
        }

        /*
         * 9. client와 연결되고, 세션이 확립 establish 된 상태
         *    recv, send를 통해 client와 소켓 통신
         */
        printf("client connected\n");
        while(1) {
            bzero(buf, sizeof(buf));
            len_out = recv(client_sd, buf, sizeof(buf), 0);
            if (len_out < 0) {
                printf("read error : %d\n", errno);
                break;
            } else if (len_out == 0) {
                printf("client's close socket\n");
                break;
            }

            printf("client : %s\n", buf);
            write(client_sd, buf, len_out);
        }

        close(client_sd);
    }

    close(server_sd);

    return 0;
}

 

 


UDP

Client

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>

#define PORT     8080
#define BUF_SIZE 128

int main() {
    int sd;
    char buff[BUF_SIZE];
    struct sockaddr_in server_addr;

    // 소켓 생성
    if ((sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        printf("can't create socket\n");
        return -1;
    }

    // 소켓 주소 구조체 초기화
    bzero((char *)&server_addr, sizeof(server_addr));

    // 서버 소켓 주소 설정
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(PORT);

    int n;
    socklen_t len;

    while(1) {
        printf("Input : ");
        if (fgets(buff, BUF_SIZE, stdin)) {
            buff[strlen(buff)-1] = '\0';
        } else {
            printf("fgets error\n");
            break;
        }

        // UDP server 를 종료한 후, sendto()를 실행하면 프로그램이 block 되는데, 어떻게 예외처리를 해야 할 까?
        // sendto()가 block 되는 이유는 OS 마다 다르다고 한다.
        if (sendto(sd, buff, strlen(buff), 0, (const struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) {
            printf("UDP server connection error : %s\n", strerror(errno));
            break;
        }

        if (strcmp(buff, "exit") == 0) {
            printf("UDP client exit\n");
            break;
        }

        n = recvfrom(sd, (char *)buff, BUF_SIZE, MSG_WAITALL, (struct sockaddr *) &server_addr, &len);
        buff[n] = '\0';

        printf("Server(%s:%d) : %s\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port) ,buff);
    }

    close(sd);

    return 0;
}

Server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define PORT     8080
#define BUF_SIZE 128

int main() {
    int server_sd;
    char buff[BUF_SIZE];
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;

    // 소켓 생성
    if ((server_sd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        printf("can't open diagram socket\n");
        return 0;
    }

    // 소켓 주소 구조체 초기화
    bzero((char *)&server_addr, sizeof(server_addr));
    bzero((char *)&client_addr, sizeof(client_addr));

    // 서버 소켓 주소 설정
    server_addr.sin_family    = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(PORT);

    // 서버 소켓에 주소 바인딩
    if (bind(server_sd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0 ) {
        printf("can't bind socket\n");
        return 0;
    }

    int n;
    client_addr_len = sizeof(client_addr);  //len is value/resuslt

    while(1) {
        bzero(buff, sizeof(buff));
        n = recvfrom(server_sd, (char *)buff, BUF_SIZE, MSG_WAITALL, ( struct sockaddr *) &client_addr, &client_addr_len);
        buff[n] = '\0';

        if (strcmp(buff, "exit") == 0) {
            printf("UDP server stop\n");
            break;
        }

        printf("Client(%s:%d) : %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buff);
        sendto(server_sd, buff, strlen(buff), 0, (const struct sockaddr *) &client_addr, client_addr_len);
    }

    return 0;
}

 

 

'Network' 카테고리의 다른 글

Socket Programming - TCP 세션 수립 및 종료  (0) 2020.11.30
Socket Programming - Socket  (0) 2020.11.08