251028

lililllilillll·2025년 10월 28일

개발 일지

목록 보기
338/350

✅ 한 것들


  • 이력서, 포폴 수정
  • 윤성우의 열혈 TCP/IP 소켓 프로그래밍


📖 윤성우의 열혈 TCP/IP 소켓 프로그래밍


Chapter 20 Windows에서의 쓰레드 동기화

20-1 동기화 기법의 분류와 CRITICAL_SECTION 동기화

윈도우 운영체제 연산 방식 : dual-mode operation

  • 유저 모드
    • 응용 프로그램 실행되는 기본 모드
    • 물리 영역 접근 불가
    • 접근 메모리 영역 제한
  • 커널 모드 : 운영체제 실행될 때의 모드
    • 메모리, 하드웨어 접근 제한 없음

유저모드 동기화 : 커널 모드 전환 안 해서 속도 빠르다
커널모드 동기화 : 기능 더 많다

  • Dead-lock 막기 위해 타임아웃 지정 가능.
  • 서로 다른 프로세스 두 쓰레드 간 동기화도 가능
    • 커널 오브젝트 기반이기 때문에 가능

CRITICAL_SECTION 기반의 동기화

CRITICAL_SECTION 오브젝트

  • CRITICAL_SECTION 기반 동기화할 때 생성되어 활용됨
  • 커널 오브젝트와는 다르다
  • 임계영역 진입에 필요한 일종의 열쇠
CRITICAL_SECTION cs;

int main(int argc, char *argv[])
{
    HANDLE tHandles[NUM_THREAD];
    int i;

    InitializeCriticalSection(&cs);
    for (i = 0; i < NUM_THREAD; i++)
    {
        if(i%2)
            tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadInc, NULL, 0, NULL);
        else
            tHandles[i] = (HANDLE)_beginthreadex(NULL, 0, threadDes, NULL, 0, NULL);
    }

    WaitForMultipleObjects(NUM_THREAD, tHandles, TRUE, INFINITE);
    DeleteCriticalSection(&cs);
    printf("result: %lld \n", num);
    return 0;
}

unsigned WINAPI threadInc(void* arg)
{
    int i;
    EnterCriticalSection(&cs);
    for (i = 0; i < 500000; i++)
        num += 1;
    LeaveCriticalSection(&cs);
    return 0;
}

20-2 커널모드 동기화 기법

Mutex 오브젝트 기반 동기화

관련 함수

  • CreateMutex()
  • CloseHandle()
  • ReleaseMutex()
  • WaitForSingleObject() : Mutex는 이거 반환될 때 자동으로 non-signaled 상태 되는 auto-reset 모드 커널 오브젝트임

Semaphore 오브젝트 기반 동기화

관련 함수

  • CreateSemaphore()
  • CloseHandle()
  • ReleaseSemaphore()
  • WaitForSingleObject()

Event 오브젝트 기반 동기화

Event 동기화 오브젝트는 auto-reset 모드와 manual-reset 모드 택일 가능
관련 함수

  • CreateEvent() : 두 번째 인자 TRUE면 manual-reset
  • ResetEvent() : non-signaled 상태로 변경
  • SetEvent() : signaled 상태로 변경
// 둘 이상의 쓰레드가 동시에 대기상태 빠져나오는 예제
int main(int argc, char *argv[])
{
    HANDLE hThread1, hThread2;
    hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // manual-reset 모드로 non-signal 상태인 Event 생성
    hThread1 = (HANDLE)_beginthreadex(NULL, 0, NumberOfA, NULL, 0, NULL);
    hThread2 = (HANDLE)_beginthreadex(NULL, 0, NumberOfOthers, NULL, 0, NULL);

    fputs("Input string: ", stdout);
    fgets(str, STR_LEN, stdin);
    SetEvent(hEvent);
    WaitForSingleObject(hThread1, INFINITE);
    WaitForSingleObject(hThread2, INFINITE);
    ResetEvent(hEvent); // 여기서 hEvent 해금하면 쓰레드1, 쓰레드2 둘 다 실행됨
    CloseHandle(hEvent);
    return 0;
}

unsigned WINAPI NumberOfA(void* arg)
{
    int i, cnt = 0;
    WaitForSingleObject(hEvent, INFINITE);
    for (i = 0; str[i] != 0; i++)
    {
        if(str[i]=='A')
            cnt++;
    }
    printf("Num of A: %d \n", cnt);
    return 0;
}

Chapter 21 Asynchronous Notification IO 모델

21-1 비동기 Notification IO 모델의 이해

윈도우에서 데이터 입출력할 때 send, recv로 동기화된 입출력 진행했다.
send는 출력 버퍼에 데이터 전송돼야 반환,
recv는 원하는 만큼 데이터 읽은 후에 반환.

비동기 입출력 : 함수 반환 시점과 데이터 송수신 완료시점 일치 하지 않음

Notification IO : IO 관련 상황 발생했는가?

  • select는 IO 필요하거나 가능한 상황일 때만 반환
  • 비동기로 만들면 IO 관찰 후 다른 일 하다가 상태 변화 확인 가능

21-2 비동기 Notification IO 모델의 이해와 구현

Nofitication : IO 상태 변화 알림

IO 상태 변화

  • 소켓에 대한 IO의 상태변화
  • 소켓에 대한 IO 관련 이벤트의 발생

WSAEventSelect()

  • 임의의 소켓 대상 이벤트 발생 관찰
  • 인자로 전달된 소켓에서 INetworkEvents에 전달된 이벤트 중 하나 발생하면,
  • hEventObject에 전달된 핸들의 커널 오브젝트를 signaled 상태로 바꾼다
  • 즉, Event 오브젝트와 소켓을 연결한다
  • 이벤트 발생유무 상관 없이 바로 반환. 비동기 Notification임.

WSACreateEvent() : manual-reset 모드면서 non-signaled 상태인 Event 오브젝트 생성
WSACloseEvent() : Event 오브젝트 종료
WSAWaitForMultipleEvents() : 이벤트 발생 유무 확인
WSAEnumNetworkEvents() : 해당 오브젝트가 signaled된 원인 확인. 확인한 오브젝트는 non-signaled로 되돌려준다.

    hSockArr[numOfClntSock] = hServSock;
    hEventArr[numOfClntSock] = newEvent;
    numOfClntSock++;

    while(1)
    {
        posInfo = WSAWaitForMultipleEvents(numOfClntSock, hEventArr, FALSE, WSA_INFINITE, FALSE);
        startIdx = posInfo - WSA_WAIT_EVENT_0;

        for (i = startIdx; i < numOfClntSock; i++)
        {
            // hEventArr[i]이 시그널 상태인지 non block으로 확인. signal이면 WSA_WAIT_EVENT_0 반환.
            int sigEventIdx = WSAWaitForMultipleEvents(1, &hEventArr[i], TRUE, 0, FALSE);
            if((sigEventIdx==WSA_WAIT_FAILED || sigEventIdx==WSA_WAIT_TIMEOUT)) 
            {
                continue;
            }
            else
            {
                // 어떤 네트워크 이벤트(FD_ACCEPT/FD_READ/FD_CLOSE)가 발생했는지 확인
                sigEventIdx = i;
                WSAEnumNetworkEvents(hSockArr[sigEventIdx], hEventArr[sigEventIdx], &netEvents);
                if(netEvents.lNetworkEvents & FD_ACCEPT) // 연결 요청 시
                {
                    if(netEvents.iErrorCode[FD_ACCEPT_BIT]!=0)
                    {
                        puts("Accept Error");
                        break;
                    }
                    clntAdrLen = sizeof(clntAdr);
                    hClntSock = accept(hSockArr[sigEventIdx], (SOCKADDR *)&clntAdr, &clntAdrLen);
                    newEvent = WSACreateEvent();
                    // 새 소켓에 이벤트 연결
                    WSAEventSelect(hClntSock, newEvent, FD_READ | FD_CLOSE);

                    hEventArr[numOfClntSock] = newEvent;
                    hSockArr[numOfClntSock] = hClntSock;
                    numOfClntSock++;
                    puts("connected new client...");
                }

                if(netEvents.lNetworkEvents & FD_READ) // 데이터 수신 시
                {
                    if(netEvents.iErrorCode[FD_READ_BIT]!=0)
                    {
                        puts("Read Error");
                        break;
                    }
                    strLen = recv(hSockArr[sigEventIdx], msg, sizeof(msg), 0);
                    send(hSockArr[sigEventIdx], msg, strLen, 0);
                }

                if(netEvents.lNetworkEvents & FD_CLOSE) // 종료 요청 시
                {
                    if(netEvents.iErrorCode[FD_CLOSE_BIT]!=0)
                    {
                        puts("Close Error");
                        break;
                    }
                    WSACloseEvent(hEventArr[sigEventIdx]);
                    closesocket(hSockArr[sigEventIdx]);

                    numOfClntSock--;
                    CompressSockets(hSockArr, sigEventIdx, numOfClntSock);
                    CompressEvents(hEventArr, sigEventIdx, numOfClntSock);
                }
            }
        }
    }
    WSACleanup();
    return 0;
}

Event를 활용한 서버



profile
너 정말 **핵심**을 찔렀어

0개의 댓글