관리 메뉴

Value Creator의 IT(프로그래밍 / 전자제품)

네트워크 프로그래밍 팁(1) 본문

1. 프로그래밍/4) Network

네트워크 프로그래밍 팁(1)

valuecreatort 2020. 3. 5. 17:54
반응형

I/O 이벤트 통지 모델

이벤트 통지 모델은 Non-Blocking에서 제기된 문제를 해결하기 위해서 고안되었다. I/O처리를 할 수 있는 소켓(혹은 파일 디스크립터)을 가려내서 가르쳐준다면, 다시말해 입력 버퍼에 데이터가 수신되어서 데이터의 수신이 필요하거나, 출력버퍼가 비어서 데이터의 전송이 가능한 상황을 알려준다면, 위에서 이야기한 구조보다 더 단순하고 효과적으로 다중 I/O모델을 처리할 수 있을 것이다. I/O 이벤트를 통지하는 방법은 크게 동기형 통지모델과 비동기형 통지모델로 나눌 수 있다.

위의 모든 반환이 I/O의 진행시간과는 관계없이 빠르게 동작하기 때문에, 유저 프로세스는 자신의 작업을 오랜시간 중지하지 않고도 I/O 처리를 수행할 수 있다.

 

 

동기형 통지모델

동기(synchronous)와 비동기(asynchronous)는 서로 메시지를 주고받는 상대방이 어떤 방식으로 통신을 하는가에 대한 개념이다. I/O 통지모델에서 대화하는 주체들은 커널과 프로세스이다. 프로세스는 커널에게 I/O처리를 요청하고,커널은 프로세스에게 I/O 상황을 통지한다. 우선 I/O 요청은 반드시 동일하게 처리될 수 밖에 없는 부분이고, 결국에 커널이 프로세스에게 어떤 방식으로 통지하느냐에 따라 동기형이냐 비동기형이 결정될 것이다.

동기형 통지모델의 프로세스는 커널에게 지속적으로 현재 I/O 준비 상황을 체크한다. 즉 커널이 준비되었는지를 계속 확인하여 동기화 하는 것이다. 따라서 동기형 통지모델에서 Notify를 적극적으로 진행하는 주체는 유저의 프로세스가 되며 커널은 수동적으로 유저 프로세스의 요청에 따라 현재의 상황을 보고한다.

 

 

비동기형 통지모델

이와 반대로 비동기형 통지모델은 일단 커널에게 I/O작업을 맡기면 커널의 작업 진행사항에 대해서 프로세스가 인지할 필요가 없는 상황을 말한다. 유저의 프로세스가 I/O 동기화를 신경쓸 필요가 없기에 비동기형이라고 부를 수 있다. 따라서 비동기형 통지모델에서 Notify의 적극적인 주체는 커널이 되며, 유저 프로세스는 수동적인 입장에서 자신이 할일을 하다가 통지가 오면 그때 I/O 처리를 하게 된다.

 

 

 

 

 

Socket에서 Non-Blocking 구현

socket 프로그래밍에서 해당 소켓의 I/O Blocking/Non-Blocking 모드를 결정할 수 있다.

 

두 번째 인자에 어떤 값을 넣느야에 따라서 설정 값의 의미가 달라지긴 하지만, Non-Blocking으로 만드는데에는 이 호출만으로 충분하다. 소켓은 초기 설정이 Blocking으로 되어있으므로 Non-Blocking 모드로 진행하기 위해서는 이 함수 호출이 필수적이다.

 

//ioctlsocket함수: 소켓의 I/O 상태를 변경하는 함수
//리눅스에서는 ioctl함수가 같은 기능을 지원한다.
ULONG isNonBlocking = 1;
ioctlsocket(socket,        //Non-Blocking으로 변경할 소켓
            FIONBIO,       //변경할 소켓의 입출력 모드
            &isNonBlocking //넘기는 인자, 여기서는 nonblocking설정 값
           );




char str[BUF_SIZE] = {0,}; //받을 버퍼
unsigned int strLen = 0; //받고싶은 데이터의 길이
unsigned int length = 0; //지금까지 받은 데이터의 길이
unsigned int recvLen = 0; //이번에 받은 데이터의 길이
...
while(TRUE){
   if ( recvLen = recv( sock, str+length, strLen, 0) < 0 ){
   //recvfrom 호출하여 결과값을 받는다. 0보다 작으면 받을 수 있는 데이터가 없는것
      if ( WSAGetLastError() == WSAEWOULDBLOCK ){
         //WOULDBLOCK이라면 대기시킨다.
         Sleep( 2000 ); /* Sleep for 2 milliseconds */
      }
      else{
         printf("No data Error.\n");
         break;
      }
   }
   else {
       length += recvLen;
       if(length >= strLen) 
       //다받았으면 중지
           break;
   }
}

 

 

 

ioctlsocket함수를 사용하면 버퍼에 데이터가 쌓여 있는지 알 수 있습니다.  

참고로, 버퍼에 rsize만큼의 데이터가 들어 있을 수도 있고, 더 많이 들어 있을 수도 있습니다. ioctlsocket 호출 중이나 이후에도 네트워크에서 데이터가 더 들어와 있을 수 있기 때문에 ioctlsocket 값이 0일 때까지 계속 recv함수를 호출에서 버퍼를 비워주면 됩니다. 

unsigned long rsize; 
if( ioctlsocket( sock, FIONREAD, &rsize ) == SOCKET_ERROR ){ 
//소켓 오류 
}else if( rsize > 0 ){ 
//버퍼에 자료가 있음. 
} 
 
 
do{ 
//ioctlsocket으로 버퍼 검사 
//recv로 버퍼 비움. 
}while( rsize > 0 ); 
char ch;
u_long cntRecvBuf = 0;
 
if (ioctlsocket(hSocket, FIONREAD, &cntRecvBuf) != SOCKET_ERROR) {
    for (long i = 0; i<cntRecvBuf; i++) {
        recv(hSocket, &ch, sizeof(ch), 0);
    }
}

 

 

//write error 검출

 

 

 
65
     char message[bufSize * 2]; 
66
     for(int i = 0; i < 10; i ++) 
67
     { 
68
         ssize_t nBytes = send(clientSocket, message, sizeof(message), 0); 
69
         if(nBytes == -1) 
70
         { 
71
             errorHandling("write error"); 
72
         } 
73
         int remainSize; 
74
         if (ioctl(clientSocket, SIOCOUTQ, &remainSize) == -1) 
75
         { 
76
             errorHandling("ioctl error"); 
77
         } 
78
         printf("write message:%zd, remain buf size: %d\n", nBytes, remainSize); 
79
     } 

 

 

 

 

 

 

ret = read(fd, buf, len)에서 반환하는 값인 ret은 다음과 같이 4가지 경우가 있을 수 있습니다.

(1) ret == len 인 경우: len 크기의 모든 바이트를 읽어서 buf에 저장함.
(2) 0 < ret < len 인 경우: 읽는 도중에 중단된 경우 혹은 len 크기를 모두 읽기전에 EOF에 도달한 경우.
(3) ret == 0 인 경우: EOF 이거나 읽을 데이터가 없는 경우.
(4) ret == -1 인 경우: 오류 발생.

참고로, 아래는 read()를 좀더 완벽하게 하는 코드 입니다.

 

ssize_t ret;
 
while (len != 0 && (ret = read (fd, buf, len)) != 0) {
	if (ret == -1) {
		if (errno == EINTR) continue;
		perror ("read(): error");
		break;
	}
	len -= ret;
	buf += ret;
}

 

 

 

socket의 descriptor를 fd라구 하구 보낼 문자열을 str, 크기를 size라구
할때,

FILE *fp;

ret = write(fd, str, size) ;
fp = fdopen(fd, "rw");
fflush(fp);

 

 

 

반응형
Comments