ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 멀티쓰레드를 이용한 소켓 통신
    C_C++ 프로그래밍/TCP_IP 2019. 6. 4. 21:30


    개발 환경

     OS

     Ubuntu 18.04.2

     컴파일러

     gcc version 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04)




    서버 측에 pthread를 사용하여 1개가 아닌 여러 클라이언트의 접속이 가능하도록 하였고

    현재 방문자의 수가 몇 명인지 체크하도록 하였다.

    이때 방문자 수는 서로 다른 Thread가 공동으로 접근 할 수 있기 때문에

    Mutex를 사용하여 관리 하도록 하였다.


    thread를 통해서 client의 정보를 전달하기 위해서는

    서버와 accept를 통해서 나온

    client의 접근 지정자를 void 형식으로 캐스팅하여

    인자로 보내주면 된다.


    앞으로 추가 사항

    이후에 클라이언트가 채팅한 내용을

    서버에서 각각의 클라이언트들에게 뿌려주는 기능과

    파일 전송을 추가할 계획이다.



    소스코드

    Server.c

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

    #define MAXLINE 1024
    #define PORTNUM 3600

    // init mutex
    pthread_mutex_t mutex_lock;

    // share data
    int visitor = 0;

    void *thread_function(void *data){
            int sockfd = *((int *) data);
            int readn;
            socklen_t addrlen;
            char buf[MAXLINE];
            struct sockaddr_in client_addr;
            memset(buf, 0x00, MAXLINE);
            addrlen = sizeof(client_addr);
            getpeername(sockfd, (struct sockaddr *) &client_addr, &addrlen);

            while((readn = read(sockfd, buf, MAXLINE)) > 0){
                    printf("Read Data : %s (%d) : %s",
                                    inet_ntoa(client_addr.sin_addr),
                                    ntohs(client_addr.sin_port),
                                    buf);
                    write(sockfd, buf ,strlen(buf));
                    memset(buf, 0x00, MAXLINE);
            }
            close(sockfd);
            printf("Client thread end\n");

            // critical section
            pthread_mutex_lock(&mutex_lock);
            visitor--;
            pthread_mutex_unlock(&mutex_lock);
            // end

            return 0;
    }

    int main(int argc, char **argv){
            int listen_fd, client_fd;
            socklen_t addrlen;
            int readn;
            char buf[MAXLINE];
            pthread_t thread_id;

            struct sockaddr_in server_addr, client_addr;

            if( (listen_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
                    return 1;
            }

            memset((void *) &server_addr, 0x00, sizeof(server_addr));
            server_addr.sin_family = AF_INET;
            server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
            server_addr.sin_port = htons(PORTNUM);

            if(bind(listen_fd, (struct sockaddr *) &server_addr,
                                    sizeof(server_addr)) == -1){
                    perror("bind error");
                    return 1;
            }

            printf("Server Start.....\n");

            if(listen(listen_fd, 5) == -1){
                    perror("listen error");
                    return 1;
            }

            while(1){
                    addrlen = sizeof(client_addr);
                    client_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addrlen);
                    if(client_fd == -1){
                            printf("accept error\n");
                    }
                    else{
                            pthread_create(&thread_id, NULL, thread_function, (void *) &client_fd);
                            // critical section
                            pthread_mutex_lock(&mutex_lock);
                            printf("Current Client is %d\n", ++visitor);
                            pthread_mutex_unlock(&mutex_lock);
                            // end
                            // Main server will continue even after thread isn't finished
                            pthread_detach(thread_id);
                    }
            }
            return 0;
    }


    Client.c
    #include <sys/socket.h>  /* 소켓 관련 함수 */
    #include <arpa/inet.h>   /* 소켓 지원을 위한 각종 함수 */
    #include <sys/stat.h>
    #include <stdio.h>      /* 표준 입출력 관련 */
    #include <string.h>     /* 문자열 관련 */
    #include <unistd.h>     /* 각종 시스템 함수 */

    #define MAXLINE    1024

    int main(int argc, char **argv)
    {
        struct sockaddr_in serveraddr;
        int server_sockfd;
        int client_len;
        char buf[MAXLINE];

        if ((server_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
        {
            perror("error :");
            return 1;
        }

        /* 연결요청할 서버의 주소와 포트번호 프로토콜등을 지정한다. */
        server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = inet_addr("127.0.01");
        serveraddr.sin_port = htons(3600);

        client_len = sizeof(serveraddr);

        /* 서버에 연결을 시도한다. */
        if (connect(server_sockfd, (struct sockaddr *)&serveraddr, client_len)  == -1)
        {
            perror("connect error :");
            return 1;
        }
        while(1){
            memset(buf, 0x00, MAXLINE);
            read(0, buf, MAXLINE);    /* 키보드 입력을 기다린다. */
            if(strncmp(buf, "quit\n",5) == 0){
                    break;
            }
            if (write(server_sockfd, buf, MAXLINE) <= 0) /* 입력 받은 데이터를 서버로 전송한다. */
            {
                perror("write error : ");
                return 1;
            }
                memset(buf, 0x00, MAXLINE);
                /* 서버로 부터 데이터를 읽는다. */
                if (read(server_sockfd, buf, MAXLINE) <= 0)
                {
                       perror("read error : ");
                    return 1;
                }
            printf("Read : %s", buf);
        }
        close(server_sockfd);
        return 0;
    }



    실행결과

     Client.c

     Server.c

     

     

    실제 실행 시에는 7개의 클라이언트를 실행했습니다.


    ※ 본 글은 개인 포트폴리오 혹은 공부용으로 사용하기 때문에, 무단 복사 유포는 금지하지만, 개인 공부 용도로는 얼마든지 사용하셔도 좋습니다




    반응형
Designed by Tistory.