kakasoo

[TCP/IP] 멀티 플렉싱 기반의 서버 전체 코드 및 주석 본문

프로그래밍/네트워크

[TCP/IP] 멀티 플렉싱 기반의 서버 전체 코드 및 주석

카카수(kakasoo) 2020. 7. 18. 17:20
반응형
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
 
#define BUF_SIZE 1024
 
void ErrorHandling(char* message) {
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
 
int main(int argc, char* argv[]) {
    WSADATA wsaData;
    SOCKET hServSock, hClntSock; // 서버 소켓과 데이터 송수신 소켓 생성
    SOCKADDR_IN servAdr, clntAdr; // 서버 소켓과 데이터 송수신 소켓의 주소를 의미하는 변수 생성
    TIMEVAL timeout; // timeout 값을 저장할 변수 생성
    fd_set reads, cpyReads;
 
    int adrSz; // 서버 주소의 크기
    int strLen, fdNum;
    char buf[BUF_SIZE];
 
    if (argc != 2) { // port 값을 입력받지 않았을 경우, 즉 main 함수의 data가 1인 경우
        printf("Usage : %s <port> \n", argv[0]);
        exit(1);
    }
 
    /* 1. SOCKET 관련 함수 호출, WSAStartup() */
    {
        if (WSAStartup(MAKEWORD(22), &wsaData) != 0// 실패 시 에러 메세지 출력
            ErrorHandling("WSAStartup() error!");
    }
 
    /* 2. server socket 생성 후, server socket 주소 정보 입력 */
    {
        hServSock = socket(PF_INET, SOCK_STREAM, 0);
        memset(&servAdr, 0sizeof(servAdr));
        servAdr.sin_family = AF_INET; // 주소 체계 정보 저장
        servAdr.sin_addr.s_addr = htonl(INADDR_ANY); // 접근 가능한 주소 저장, ANY
        servAdr.sin_port = htons(atoi(argv[1])); // 접근 가능한 port 저장
    }
 
    /* 3. server socket과 주소 정보 간의 결합 */
    {
        if (bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
            ErrorHandling("bind() error!");
    }
 
    /* 4. 소켓을 대기 상태로 변경 */
    {
        if (listen(hServSock, 5== SOCKET_ERROR)
            ErrorHandling("listen() error!");
        FD_ZERO(&reads); // fd_set을 모두 0으로 초기화
        FD_SET(hServSock, &reads); // hServSock의 관찰 대상을 저장
    }
 
    /* 5. 데이터 송수신 */
    {
        while (1) {
            cpyReads = reads; // cpyReads에 reads를 저장, select 이후에 변화가 없는 소켓은 모두 0으로 초기화되니 원본 사용 X
            timeout.tv_sec = 5// timeout을 5 초로 설정
            timeout.tv_usec = 5000// timeout을 5000 마이크로 sec로 설정
 
            if ((fdNum = select(0&cpyReads, 00&timeout)) == SOCKET_ERROR)
                /*
                    1번째 매개변수 : 리눅스와의 호환성을 위해 존재하는 의미없는 값.
                    2번째 매개변수 : 수신된 데이터의 존재 여부를 확인하고자 cpyRead를 넣었다
                    3번째 매개변수 : 블로킹 없는 데이터 전송 가능 여부를 확인할 의사가 없어 0을 넣었다
                    4번째 매개변수 : 예외 상황의 발생 여부를 확인할 의사가 없어 0을 넣었다
                    5번째 매개변수 : timeout을 설정
                    반환 값은 변화가 발생한 FD의 숫자인데, 오류 발생 시에는 -1이 발생하여 반복문을 빠져나간다.
                */
                break;
 
            if (fdNum == 0// 변화가 없을 경우에는 contiune;
                continue;
 
            // 변화가 있을 경우 전체 reads를 순환하며 변화가 발생한 FD를 탐색한다.
            for (int i = 0; i < reads.fd_count; i++) {
                if (FD_ISSET(reads.fd_array[i], &cpyReads)) // reads.fd_array[i]의 값이 cpyReads에 포함된다면, 표준 입력
                {
                    if (reads.fd_array[i] == hServSock) { // 그 표준 입력이 server socket이라면 연결 요청이 발생한 것
                        adrSz = sizeof(clntAdr);
                        hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &adrSz); // 연결한다
                        FD_SET(hClntSock, &reads); // reads에 client socket을 저장한다, 새로 생성한 것
                        printf("conneced client: %d \n", hClntSock);
                    }
                    else {    // 서버 소켓이 아닌 경우에 변화 발생 시 read message
                        strLen = recv(reads.fd_array[i], buf, BUF_SIZE - 10);
                        if (strLen == 0) { // 읽어들인 것이 0이라면, 연결을 종료한다.
                            FD_CLR(reads.fd_array[i], &reads);
                            closesocket(cpyReads.fd_array[i]);
                            printf("closed client : %d \n", cpyReads.fd_array[i]);
                        }
                        else { // 읽어들인 것이 있다면 echo 한다.
                            send(reads.fd_array[i], buf, strLen, 0);
                        }
                    }
                }
            }
        }
    }
    closesocket(hServSock);
    WSACleanup();
    return 0;
}
cs

 

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <WinSock2.h>

 

#define BUF_SIZE 1024

 

void ErrorHandling(char* message) {

    fputs(message, stderr);

    fputc('\n', stderr);

    exit(1);

}

 

int main(int argc, char* argv[]) {

    WSADATA wsaData;

    SOCKET hServSock, hClntSock; // 서버 소켓과 데이터 송수신 소켓 생성

    SOCKADDR_IN servAdr, clntAdr; // 서버 소켓과 데이터 송수신 소켓의 주소를 의미하는 변수 생성

    TIMEVAL timeout; // timeout 값을 저장할 변수 생성

    fd_set reads, cpyReads;

 

    int adrSz; // 서버 주소의 크기

    int strLen, fdNum;

    char buf[BUF_SIZE];

 

    if (argc != 2) { // port 값을 입력받지 않았을 경우, 즉 main 함수의 data가 1인 경우

        printf("Usage : %s <port> \n", argv[0]);

        exit(1);

    }

 

    /* 1. SOCKET 관련 함수 호출, WSAStartup() */

    {

        if (WSAStartup(MAKEWORD(22), &wsaData) != 0// 실패 시 에러 메세지 출력

            ErrorHandling("WSAStartup() error!");

    }

 

    /* 2. server socket 생성 후, server socket 주소 정보 입력 */

    {

        hServSock = socket(PF_INET, SOCK_STREAM, 0);

        memset(&servAdr, 0sizeof(servAdr));

        servAdr.sin_family = AF_INET; // 주소 체계 정보 저장

        servAdr.sin_addr.s_addr = htonl(INADDR_ANY); // 접근 가능한 주소 저장, ANY

        servAdr.sin_port = htons(atoi(argv[1])); // 접근 가능한 port 저장

    }

 

    /* 3. server socket과 주소 정보 간의 결합 */

    {

        if (bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)

            ErrorHandling("bind() error!");

    }

 

    /* 4. 소켓을 대기 상태로 변경 */

    {

        if (listen(hServSock, 5== SOCKET_ERROR)

            ErrorHandling("listen() error!");

        FD_ZERO(&reads); // fd_set을 모두 0으로 초기화

        FD_SET(hServSock, &reads); // hServSock의 관찰 대상을 저장

    }

 

    /* 5. 데이터 송수신 */

    {

        while (1) {

            cpyReads = reads; // cpyReads에 reads를 저장, select 이후에 변화가 없는 소켓은 모두 0으로 초기화되니 원본 사용 X

            timeout.tv_sec = 5// timeout을 5 초로 설정

            timeout.tv_usec = 5000// timeout을 5000 마이크로 sec로 설정

 

            if ((fdNum = select(0&cpyReads, 00&timeout)) == SOCKET_ERROR)

                /*

                    1번째 매개변수 : 리눅스와의 호환성을 위해 존재하는 의미없는 값.

                    2번째 매개변수 : 수신된 데이터의 존재 여부를 확인하고자 cpyRead를 넣었다

                    3번째 매개변수 : 블로킹 없는 데이터 전송 가능 여부를 확인할 의사가 없어 0을 넣었다

                    4번째 매개변수 : 예외 상황의 발생 여부를 확인할 의사가 없어 0을 넣었다

                    5번째 매개변수 : timeout을 설정

                    반환 값은 변화가 발생한 FD의 숫자인데, 오류 발생 시에는 -1이 발생하여 반복문을 빠져나간다.

                */

                break;

 

            if (fdNum == 0// 변화가 없을 경우에는 contiune;

                continue;

 

            // 변화가 있을 경우 전체 reads를 순환하며 변화가 발생한 FD를 탐색한다.

            for (int i = 0; i < reads.fd_count; i++) {

                if (FD_ISSET(reads.fd_array[i], &cpyReads)) // reads.fd_array[i]의 값이 cpyReads에 포함된다면, 표준 입력

                {

                    if (reads.fd_array[i] == hServSock) { // 그 표준 입력이 server socket이라면 연결 요청이 발생한 것

                        adrSz = sizeof(clntAdr);

                        hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &adrSz); // 연결한다

                        FD_SET(hClntSock, &reads); // reads에 client socket을 저장한다, 새로 생성한 것

                        printf("conneced client: %d \n", hClntSock);

                    }

                    else {    // 서버 소켓이 아닌 경우에 변화 발생 시 read message

                        strLen = recv(reads.fd_array[i], buf, BUF_SIZE - 10);

                        if (strLen == 0) { // 읽어들인 것이 0이라면, 연결을 종료한다.

                            FD_CLR(reads.fd_array[i], &reads);

                            closesocket(cpyReads.fd_array[i]);

                            printf("closed client : %d \n", cpyReads.fd_array[i]);

                        }

                        else { // 읽어들인 것이 있다면 echo 한다.

                            send(reads.fd_array[i], buf, strLen, 0);

                        }

                    }

                }

            }

        }

    }

    closesocket(hServSock);

    WSACleanup();

    return 0;

}

반응형