반응형
728x90
반응형

오늘은 C++에서의 쓰레드이다

지겹게 기억을 더듬어가면서 C기반의 포스팅을 했던 적이 있는데 C++에서도 지원을 한다니 새로웠다.

 

C기반 I/O Multithreading - 12. 멀티 프로세싱? 멀티 쓰레딩?

멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거리도 만만치 않기 때문에 좀 상세히 볼 필요가 있다ㅋ

typingdog.tistory.com

위 링크는 쓰레드

C++ 에서의 쓰레드

원래 쓰레드의 생성과 사용은 OS에 종속적이었고, API가 C 기반이었다. 하지만 C++ 11 표준에 쓰레드 라이브러리가 따로 들어가면서 OS에 독립적이고, 프로그래밍 언어 차원에서 지원이 된다.

임계 영역과 관련된 문제는 Mutex를 통해서 상호배제를 처리할 것인데 이 또한 C++ 프로그래밍 언어 차원에서 지원이 된다는 것이다. 그래서 C 기반의 코드를 그대로 C++ 코드로 변경하도록 해보겠다. 

일단 간단하게 쓰레드 사용과 그에 동반하는 문제까지 설명하자면,

공용 데이터 영역의 변수(전역 변수)

쓰레드에서 공통으로 접근할 수 있는 데이터 영역의 변수를 두 쓰레드에서 임계영역에서 사용이 되면서 기대하는 값과 다른 결과가 나오는 것에서부터 문제가 된다.

쓰레드 A와 쓰레드 B에 해당하며 동일한 횟수만큼(백만) 증감

쓰레드 A에서는 동일한 횟수만큼 (1,000,000) 공용 Data 영역 변수를 1씩 증가시키고, 쓰레드 B에서는 동일한 횟수만큼 (1,000,000) 공용 Data 영역 변수를 1씩 감소시킨다.

기대하는 값은 0 이겠지만(동일한 횟수만큼 1씩 증가시키고, 감소시켰기 때문) 쓰레드 동기화가 이루어지지 않은 탓에 0이 아닌 다른 값이 나온다.

문제에 대한 자세한 설명은 아래의 링크를 통해서 확인하면 된다.

 

C기반 I/O Multithreading - 14. 쓰레드의 치명적인 문제점

C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거

typingdog.tistory.com

C언어 기반 쓰레드 구현(Mutex)

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
pthread_mutex_t mutex;
int common_value = 0;
 
void* t_main_plus(void *arg);
void* t_main_minus(void *arg);
 
int main(void)
{
    pthread_t tid1, tid2;
 
    pthread_mutex_init(&mutex, NULL);
 
    pthread_create(&tid1, NULL, t_main_plus, NULL); // 쓰레드 생성
    pthread_create(&tid2, NULL, t_main_minus, NULL); // 쓰레드 생성
 
    pthread_detach(tid1); // tid1 에 해당하는 쓰레드가 종료됨과 동시에 소멸.
    pthread_detach(tid2); // tid2 에 해당하는 쓰레드가 종료됨과 동시에 소멸.
 
    sleep(7); // 종료되지 않도록 대기.
    pthread_mutex_destroy(&mutex);
    printf("메인함수가 종료됩니다. [common_value의 최종 값 : %d]\n",common_value);
    return 0;
}
 
void* t_main_plus(void *arg)
{
    int i = 0;
 
    printf("t_main_plus 쓰레드가 연산을 시작합니다. \n");
 
    pthread_mutex_lock(&mutex);
    for(i=0; i<1000000; i++// for을 중복해서 쓴 것은 100번 type을 검사하는 것보단 났다고 생각.
        common_value+=1;
    pthread_mutex_unlock(&mutex);
 
    printf("t_main_plus 쓰레드가 종료됩니다.\n");
    return NULL;
}
 
 
void* t_main_minus(void *arg)
{
    int i = 0;
 
    printf("t_main_minus 쓰레드가 연산을 시작합니다. \n");
 
    pthread_mutex_lock(&mutex);
    for(i=0; i<1000000; i++// for을 중복해서 쓴 것은 100번 type을 검사하는 것보단 났다고 생각.
        common_value-=1;
    pthread_mutex_unlock(&mutex);
 
    printf("t_main_minus 쓰레드가 종료됩니다.\n");
    return NULL;
}
cs

C++언어 기반 쓰레드 구현(Mutex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <thread>
#include <mutex>
 
std::mutex mtx;
int comm_value = 0;
 
int main(void)
{
    std::thread t1([]() { mtx.lock(); for (int i = 0; i < 1000000; i++)    comm_value--; mtx.unlock(); });
    std::thread t2([]() { mtx.lock();  for (int i = 0; i < 1000000; i++)    comm_value++; mtx.unlock(); });
 
    t1.join();
    t2.join();
 
    std::cout << "comm_value의 값은 : " << comm_value << std::endl;
    return 0;
}
cs

 

위의 코드가 아래의 코드로 변환되었다고 생각하면 된다. 너무 간단하게 몇 줄이면 끝났다ㅋㅋㅋㅋ

끝.

728x90
반응형
728x90
반응형
 

C기반 I/O Multithreading - 12. 멀티 프로세싱? 멀티 쓰레딩?

멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거리도 만만치 않기 때문에 좀 상세히 볼 필요가 있다ㅋ

typingdog.tistory.com

 

 

C기반 I/O Multithreading - 13. 쓰레드의 생성과 소멸까지

C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거

typingdog.tistory.com

 

 

C기반 I/O Multithreading - 14. 쓰레드의 치명적인 문제점

C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거

typingdog.tistory.com

 

 

C기반 I/O Multithreading - 15. 뮤텍스와 세마포어(1)

C기반 I/O Multithreading - 14. 쓰레드의 치명적인 문제점 C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사

typingdog.tistory.com

 

 

C기반 I/O Multithreading - 16. 뮤텍스와 세마포어(2)

C기반 I/O Multithreading - 15. 뮤텍스와 세마포어(1) C기반 I/O Multithreading - 14. 쓰레드의 치명적인 문제점 C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프..

typingdog.tistory.com

멀티 스레드와 이전 포스팅 시리즈(?)들이다.

이번에는 멀티 스레드를 기반으로 하며 Mutex 쓰레드 동기화 방법이 사용된 서버를 작성해볼 것이다.

위와 같은 쓰레드들 간에 공유가 가능한 데이터 영역의 변수들에 동기화 되지 않은 접근은 허용하지 않는다

마찬가지로 데이터 메모리 영역에 접근하는 부분은 모두 동기화 시킨다.

코드 및 실행 결과

 

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
 
#include <pthread.h>
 
#define BUFSIZE 30
#define MAXCLIENTS 30
 
pthread_mutex_t mutex;
int client[MAXCLIENTS] = { 0, };
int now = 0;
 
void * HandleClient(void * arg);
void ErrorMsg(const char *msg);
 
int main(void)
{
    pthread_t tid;
 
    int serv_sock, clnt_sock;
    struct sockaddr_in serv_addr, clnt_addr;
 
    socklen_t addr_size;
 
 
    pthread_mutex_init(&mutex, NULL); // 뮤텍스 id 생성
 
    serv_sock = socket(PF_INET,SOCK_STREAM,0);
    if(serv_sock == -1)
        ErrorMsg("socket() error");
 
    memset(&serv_addr,0,sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(7010);
 
    if(bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
        ErrorMsg("bind() error");
 
    if(listen(serv_sock, 5== -1)
        ErrorMsg("listen() error");
 
    while(1)
    {
        addr_size = sizeof(clnt_addr);
        clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &addr_size);
        if(clnt_sock == -1)
            continue;
 
 
        pthread_mutex_lock(&mutex);
        client[now++= clnt_sock;
        pthread_mutex_unlock(&mutex);
 
        pthread_create(&tid, NULL, HandleClient, (void*)&clnt_sock);
        pthread_detach(tid);
 
        printf("클라이언트 연결 [%d]\n", clnt_sock);
    }
    return 0;
}
 
void * HandleClient(void * arg)
{
    int clnt_sock = *((int*)arg);
    int strLen, state;
    char buf[BUFSIZE];
    int i = 0;
 
    while((strLen=read(clnt_sock, buf, sizeof(buf)))!=0)
    {
        pthread_mutex_lock(&mutex);
        for (i = 0; i < now; i++)
            write(client[i], buf, strLen);
        pthread_mutex_unlock(&mutex);
    }
 
    pthread_mutex_lock(&mutex);
    for (i = 0; i < now; i++)
    {
        if(clnt_sock == client[i])
        {
            printf("클라이언트 연결 종료 [%d]\n", client[i]);
            for(;i<now-1;i++)
                client[i] = client[i+1];
            break;
        }
    }
    now--;
    pthread_mutex_unlock(&mutex);
    close(clnt_sock);
    return NULL;
}
 
void ErrorMsg(const char *msg)
{
    fputs(msg, stderr);
    fputc('\n',stderr);
    exit(1);
}
 
cs

문제 없이 통신이 잘 되는 모습이다. 접속한 클라이언트의 수가 너무 적어서 사실 확실히 확인한 케이스는 절대 아니지만 코드적으로만 보아도 의미가 있기 때문에 이것으로 마무리 짓겠다.

728x90
반응형
728x90
반응형
 

C기반 I/O Multithreading - 15. 뮤텍스와 세마포어(1)

C기반 I/O Multithreading - 14. 쓰레드의 치명적인 문제점 C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사

typingdog.tistory.com

쓰레드를 동기화 하는 방법으로 뮤텍스에 이어서 세마포어다! 

좀 더 구체적으로 정확히 이야기하자면 바이너리 세마포어이다.

바이너리 세마포어란

이 바이너리 세마포어는 0과 1을 사용하여 실행 순서를 컨트롤 한다. 다음은 바이너리 세마포어가 임계영역에 대한 접근 순서를 동기화하는 과정을 적어놨다.

쓰레드 A가 먼저 sem_wait() 함수를 호출하여 임계 영역에 접근한 상태에서 쓰레드 B가 sem_wait() 함수를 호출하여 블로킹에 걸렸다가, 쓰레드 A가 sem_post() 함수를 호출하여 쓰레드 B가 sem_wait() 함수 처리 가능 상태가 되어서 이를 호출하고, 쓰레드 B가 임계 영역에 접근한 뒤, sem_post() 함수를 호출하여 쓰레드 B가 임계 영역에서 빠져나오는 과정을 설명한 것이다.

좀 설명이 복잡했지만 다음과 같다.

위에서 나온 함수들의 원형을 살펴보면 다음과 같다.

sem_init, sem_destroy, sem_wait, sem_post

이어서 간단한 예제를 확인하겠다. 지난 예제의 보완이다.

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
 
sem_t semaphore;
int common_value = 0;
 
void* t_main_plus(void *arg);
void* t_main_minus(void *arg);
 
int main(void)
{
    pthread_t tid1, tid2;
 
    sem_init(&semaphore, 01);
    // 세 번째 인자를 0으로 넣으면 sem_wait을 먼저 호출한 쓰레드에서 블로킹이 발생하는 것을 확인할 수 있다.
 
    pthread_create(&tid1, NULL, t_main_plus, NULL); // 쓰레드 생성
    pthread_create(&tid2, NULL, t_main_minus, NULL); // 쓰레드 생성
 
    pthread_detach(tid1); // tid1 에 해당하는 쓰레드가 종료됨과 동시에 소멸.
    pthread_detach(tid2); // tid2 에 해당하는 쓰레드가 종료됨과 동시에 소멸.
 
    sleep(7); // 종료되지 않도록 대기.
    sem_destroy(&semaphore);
 
    printf("메인함수가 종료됩니다. [common_value의 최종 값 : %d]\n",common_value);
    return 0;
}
 
void* t_main_plus(void *arg)
{
    int i = 0;
 
    printf("t_main_plus 쓰레드가 연산을 시작합니다. \n");
 
    sem_wait(&semaphore);
    for(i=0; i<1000000; i++// for을 중복해서 쓴 것은 100번 type을 검사하는 것보단 났다고 생각.
        common_value+=1;
    sem_post(&semaphore);
 
    printf("t_main_plus 쓰레드가 종료됩니다.\n");
    return NULL;
}
 
 
void* t_main_minus(void *arg)
{
    int i = 0;
 
    printf("t_main_minus 쓰레드가 연산을 시작합니다. \n");
 
    sem_wait(&semaphore);
    for(i=0; i<1000000; i++// for을 중복해서 쓴 것은 100번 type을 검사하는 것보단 났다고 생각.
        common_value-=1;
    sem_post(&semaphore);
 
    printf("t_main_minus 쓰레드가 종료됩니다.\n");
    return NULL;
}
cs

여러 차례 실행해도 common_value 값은 딱 0으로 맞아 떨어진다. 세마포어가 교착 상태도 없이 잘 수행해낸 것으로 확인되는 실행 결과이다. 

이로서 쓰레드 동기화에 관련된 내용은 마무리이고 다음 포스팅에는 뮤텍스를 이용한 서버 프로그램 작성이다.

728x90
반응형
728x90
반응형
 

C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩?

멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거리도 만만치 않기 때문에 좀 상세히 볼 필요가 있다ㅋ

typingdog.tistory.com

 

 

C기반 I/O MultiThreading - 13. 쓰레드의 생성과 소멸까지

C기반 I/O MultiThreading - 12. 멀티 프로세싱? 멀티 쓰레딩? 멀티 프로세싱에 이어서, 멀티 프로세싱의 단점이 보완되는 멀티 쓰레딩 개념이다. 사실 단점이 보완되기는 하는데 함께 딸려오는 문제 거

typingdog.tistory.com

쓰레드 이전 설명들이다. ㅋㅋ

이렇게 프로세스의 자식 프로세스의 반환 회수, 프로세스끼리 통신의 까다로움, 많은 메모리 자원 소모, 부담스러운 컨텍스트 스위칭의 오버 헤드 문제들이 거의 해결이 되는 전지전능한 쓰레드일 것이라고 생각했다.

그러나 언제나 그렇듯, 하나가 되면 하나가 문제가 생기는게 이치이듯이 쓰레드 또한 치명적인 문제가 있었다.

통신을 손쉽게 하기 위해서 힙이나 데이터 등의 공유 메모리 영역을 두었다. 아주 잘한 것이다. 그런데 이 공유 메모리 영역에서 여러 다른 기능을 하는 쓰레드들이 동일한 변수 등에 접근하여 CRUD가 일어나는 경우 문제가 생기는 것이다! 왜냐하면 다음과 같은 상황인 것이다.

후 그리느라 너무 힘들었다. 위와 같은 상황을 이야기하는 것이다. 순서대로 읽으면 된다. 그림에서 나타내는 바를 설명하자면 같은 메모리 영역을 동시에 혹은 순서의 정함이 없이 읽고 쓰는 경우, 해당 메모리 영역의 값에 대한 기대값을 보장할 수 없게 된다. 왜 그럴까? 이 또한 그림으로 준비해보았다. 

위의 순서대로 읽으면 된다.

결국, 쓰레드를 통한 병렬 처리가 진행될 때에는 프로세스와는 달리 공통적인 메모리 영역이 존재한다. 그래서 여기서 선언된 공통적인 변수에 동일한 임계 영역을 갖는 쓰레드들이 순서의 정함이 없거나, 동시에 접근을 하는 경우, 해당 변수로부터의 기대값을 보장할 수 없는 것이다. 다음 예를 통해서 확인해보겠다. 

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
 
int common_value = 0;
 
void* t_main_plus(void *arg);
void* t_main_minus(void *arg);
 
int main(void)
{
    pthread_t tid1, tid2;
 
    pthread_create(&tid1, NULL, t_main_plus, NULL); // 쓰레드 생성
    pthread_create(&tid2, NULL, t_main_minus, NULL); // 쓰레드 생성
 
    pthread_detach(tid1); // tid1 에 해당하는 쓰레드가 종료됨과 동시에 소멸.
    pthread_detach(tid2); // tid2 에 해당하는 쓰레드가 종료됨과 동시에 소멸.
 
    sleep(7); // 종료되지 않도록 대기.
    printf("메인함수가 종료됩니다. [common_value의 최종 값 : %d]\n",common_value);
    return 0;
}
 
void* t_main_plus(void *arg)
{
    int i = 0;
 
    printf("t_main_plus 쓰레드가 연산을 시작합니다. \n");
    for(i=0; i<1000000; i++// for을 중복해서 쓴 것은 100번 type을 검사하는 것보단 났다고 생각.
        common_value+=1;
 
    printf("t_main_plus 쓰레드가 종료됩니다.\n");
    return NULL;
}
 
 
void* t_main_minus(void *arg)
{
    int i = 0;
 
    printf("t_main_minus 쓰레드가 연산을 시작합니다. \n");
    for(i=0; i<1000000; i++// for을 중복해서 쓴 것은 100번 type을 검사하는 것보단 났다고 생각.
        common_value-=1;
 
    printf("t_main_minus 쓰레드가 종료됩니다.\n");
    return NULL;
}
cs

서로 다른 연산을 하는 두 t_main_plus, t_main_minus 함수가 존재한다. 그리고 이 함수들은 전역 변수인 common_value 변수의 값을 +1, -1 조작하는 임계 영역을 가지고 있다.

그래서 각각 백만 번씩 1을 더하거나 빼도록 쓰레드를 동시에 실행하고 있는 상황이다.

t_main_plus에서는 백만번을 더하고, t_main_minus에서는 백만번을 빼니까 결국 둘이 상쇄되서 0이 되는 것이 정상 아닌가? 싶지만 결과를 확인해보자.

첫 번째 실행
두 번째 실행
세 번째 실행

아니 값이 이렇게도 다를까 싶다. 임계 영역으로 인해 기대 값이 전혀 보장되고 있지 않는 상태이다.

이 정도 예제이면 확실히 임계영역, 즉 쓰레드의 문제가 어떤 것인지 확실하게 이해가 될 것이라고 생각한다. 다음 포스팅에서는 이를 해결하는 방법에 대해서 올릴 것이다.

근데 사실 이를 해결하려면 방법은 하나다. 동시에 접근을 막거나 연산의 순서를 직접 정하는 것 뿐이다. 이 기본적인 해결 방법을 토대로 여러 방법들이 등장할 것이다.

728x90
반응형
728x90
반응형

최근, 아니 비교적 최근에 어떤 게임 클라이언트를 활용한 게임 서버를 만드는 프로젝트를 진행했던 적이 있고, 지금도 조금씩 조금씩 하고 있으나, 우선 순위에 밀려 소홀하긴 하지만 진행하고 있다. 

아무래도 게임 서버인 만큼 소켓 프로그래밍이 빠질 수가 없는 영역인데, 이를 개발하면 바로바로 기록해뒀어야 하는데 그러지 못해 지금 땅을 치고 후회하고 있다 휴...

그 프로젝트에서 사용된 소켓 모델은 I/O Multiplexing이다. 원래 처음에는 Multi Processing 기반으로 구현해보고, 한 100명 접속할 미래를 김칫국 마시 듯 생각해서 Multi Threading 기법으로 간단히 구현했다가, 신경써야 할 부분이 많아서 Multi plexing 기반으로 구현을 해보았다.

먼저 각각의 차이를 알아야 하는데, Thread 개념은 Processing 포스팅 때 비교하도록하고, 지금은 완전 다른 양상을 보이는 Plexing과 Processing을 비교하도록 하겠다.

나만의 비유가 들어간다.

A라는 과목을 가리치는 교사들이 있다. 이 클래스에는 A 과목만 들으러 학생들이 온다. 이는 전제이다.

멀티 프로세싱 모델을 비교한 것이다. 학생에게 마치 과외 선생님처럼 교사가 할당되어, 학생에게서 질문이나 요구를 받아서 그에 따른 대답을 해준다.

하지만 이는 매~우 비효율적이다. 전제처럼 같은 수업 내용을 듣는 학생들인데 교사가 굳이 일대 일로 붙는다는 것은 교사라는 재능, 인적 자원이 너무나도 낭비되는 것이다! 왜냐? 같은 과목을 듣는 학생을 한데 모아서, 질문이 있는 사람들에게만 대답해주고, 가르치는 것은 일방적으로, 기본적으로 수행하면 되기 때문이다! 

그래서 또 그려왔다.

얼마나 효율적인가, 학생들 입장에서는 특화된 교육을 못 받아서 질이 떨어졌다고 생각할 수 있지만 정말 실력이 좋은 교사로 채용할수록 교육의 질을 확실하게 커버할 수 있다.

바로 이 비유에서 볼 수 있듯이 여러 학생들에게 교사 한 명이 배치되어 '가르침'이라는 기본 동작을 수행하고, 질문을 하는 학생들을 대상으로 따로 처리를 한다.

이번에 기록하는 모델 또한 마찬가지이다.

여러 학생들에게 -> 접속해 온 여러 클라이언트들에게
교사 한 명 -> 서버 하나
기본 동작을 수행 -> 접속 요청 수락 및 접속 종료 등
질문을 하는 학생들 -> 패킷을 송신하는 클라이언트

접속해 온 여러 클라이언트들에게 서버 하나가 배치되어 '접속 요청 수락 및 접속 종료 등의 송 수신'이라는 기본 동작을 수행하고, 패킷을 송신하는 클라이언트들을 대상으로 따로 처리를 한다.

이렇게 바꿔서 서버-클라이언트 관점에서 이야기해볼 수 있다. 100% 기술적으로 맞지 않을 수 있지만 크게 보았을 때 그렇다는 것이다.

나는 그래서 앞으로 저 두 번째 그림에서의 교사에 대한 설명을 기록하고 만들 것이다! ㅋㅋ

728x90
반응형

+ Recent posts