일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- FILE TRANSFER
- lua interpreter
- meta table
- 엑스퍼트2주년
- 청량리역한양수자인192
- 프리미어 영상저장
- 중소규모택지
- C++ API
- TCP/IP
- 수도권주택공급
- QT TCP
- file read
- 티몬삼겹살데이
- C API
- file open
- 국토교통부
- 등록임대주택
- 월세
- QTcpServer
- 프리미어 영상변환
- lua setup
- #부동산전자거래 #부동산전자계약 #부동산계약 #부동산전자계약방법 #부동산전자계약하는법 #부동산계약방법 #부동산중개수수료 #부동산중개수수료아끼기 #부동산복비아끼기
- lua for windows
- Lua
- object
- #신혼부부 #결혼준비 #신혼부부희망타운신혼부부특별공급
- 찾다죽는줄
- lua install
- file write
- 엑스퍼트생일축하해
- Today
- Total
Value Creator의 IT(프로그래밍 / 전자제품)
네트워크 프로그래밍 팁(1) 본문
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);
'1. 프로그래밍 > 4) Network' 카테고리의 다른 글
(멀티) 프로세스, (멀티) 쓰레드, 임계영역, 뮤텍스, 세마포어 (0) | 2020.01.08 |
---|---|
네트워크 바이트 순서 (0) | 2019.12.04 |
네트워크 관련 유용한 강의 자료 사이트 (0) | 2019.11.14 |
Chapter 18 멀티 쓰레드 기반의 서버 구현 (0) | 2019.11.11 |
Chapter 17 epoll 기반 IO 멀티플렉싱 서버 구현 (0) | 2019.11.07 |