넌블럭킹 소켓
socket() 으로 생성되는 소켓은 기본값으로 Blocking 소켓이다. 하지만 이미 생성된 소켓을 fcntl() 함수를 사용하여 nonblocking socket으로 변경 가능하다.
※ Blocking Socket(B)/Nonblocking Socket(N)
(여기서 errno는 errno.h를 인클루드해야 이용할수 있다.)
- read
- B : read 버퍼가 비어있을때 block
- N : read 버퍼가 비어있을때 -1 return, errno==EWOULDBLOCK/EAGAIN
* Blocking socket의 경우에 read 버퍼에 존재하는 데이터의 크기가 read시 요청한 데이터의 크기보다 작은 경우라도 read 버퍼에 존재하는 데이터만큼 리턴되며 block 되지 않음.
- write
- B : write 버퍼가 꽉 차있을때 block
- N : write 버퍼가 꽉 차있을때 -1 return, errno==EWOULDBLOCK/EAGAIN
- accept
- B : backlog( 현재의 connection 요청 큐 )가 비어있을때 block
- N : backlog( 현재의 connection 요청 큐 )가 비어있을때 -1 return, errno==EWOULDBLOCK/EAGAIN
- connect
- B : connection이 완전히 이루어질때까지 block
- N : connection이 완전히 이루어지 않더라도 곧바로 return. 나중에 getsockopt로 connection이 완전히 이루어졌는지 확인가능.
※ Nonblocking 소켓의 장점/단점
- 장점 : 멀티스레드를 사용하지 않고도 다른 작업을 할 수 있다.
- 단점 : 프로그램이 복잡해지며, CPU 사용량이 증가한다.
- 멀티쓰레드 기반에서 nonblock socket 을 사용하면 cpu 사용량이 엄청나게 증가한다.
이건 성능상의 차이는 확연한데 gprof 를 돌려도 안나온다. 멀티쓰레드 환경에서는 절대 사용하지말것.
※ Nonblocking 소켓으로 만드는 방법 : fcntl()함수를 이용한다.
int flag;
flag = fcntl( sock_fd, F_GETFL, 0 );
fcntl( sock_fd, F_SETFL, flag | O_NONBLOCK );
파일 입력과 출력
이 절은 파일 기술자상의 기본 입력과 출력 명령을 수행하기 위한 함수들을 설명하고 있다:
read, write, 그리고 lseek. 이들 함수들은 헤더파일 'unistd. h'에 선언되어 있다.
read함수는 기술자 filedes의 파일로부터 size 바이트를 읽고, 그 결과를 버퍼에 저장한다. (이것은 문자 스트링이 필요하지 않고 그곳에는 부가된 널 종료문자가 없다)
ssize_t read (int filedes, void *buffer, size_t size)
반환 값은 실제로 읽은 바이트의 수이다.
이것은 size보다 적을수도 있다;
예를 들어, 만일 파일에 남겨진 바이트의 수가 적거나 즉시 유용한 바이트의 수가 적은 경우 등이 있다.
정확한 동작은 파일의 종류가 무엇인지에 따라 의존한다.
size 바이트보다 덜 읽는 것은 에러가 아님을 기억하라.
0의 값은 파일의 끝을 지적한다. ( 만일 size 인수의 값이 0인 경우를 제외하고. . ) 이것은 에러로 간주하지 않는다.
만일 당신이 파일의 끝인 상태에서 read를 호출하면, 그것은 0을 반환하는 것 외에 아무 일도 하지 않는다.
만일 read가 적어도 한 문자를 반환한다면, 당신이 파일의 끝에 도달했는지를 알 수 있는 아무런 방법이 없다.
그러나 만일 당신이 끝에 도달해 있었다면 다음 read의 호출은 0을 반환해서 파일의 끝임을 지적해줄 것이다.
에러가 발생한 경우에, read는 -1을 반환한다.
다음의 errno는 이 함수에서 정의된 에러의 상황이다.
EAGAIN 일반적으로, 즉시 유용한 입력이 없을 때, read는 입력을 기다린다.
그러나 만일 그 파일에서 O_NONBLOCK가 설정되면 read는 아무런 데이터도 기다리지 않고 즉시 반환하고, 이 에러를 보고한다.
호환성 노트 : BSD Unix의 대부분의 버전은 이것을 위한 다른 에러코드를 사용한다:
EWOULDBLOCK. GNU 라이브러리에서는, EWOULDBLOCK은 EAGAIN의 다른 이름이다. 그래서 당신이 어떤 이름을 사용해도 문제가 발생되지 않는다.
어떤 시스템들은, 특별한 문자 파일로부터 데이터의 큰 덩어리를 읽으려 할 때, 만일 커널(kernal)이 당신의 것을 담을 수 있는(to lock down the user's pages), 충분한 물리적 메모리를 얻을 수 없는 경우에 EAGAIN의 에러를 내고 실패했음을 지적한다.
디바이스가 사용자의 메모리 영역을 직접적으로 억세스 하는 것이 제한되어 있는 것은 그들은 커널내부의 분리된 버퍼를 사용하기 때문이다. 그것에 터미널들은 포함되지 않는다,
EBADF filedes 인수에 주어진 것이 유용한 파일 기술자가 아니다.
EINTR read가 입력을 기다리고 있는 동안 시그널에 의해 인터럽트 되어졌다.
EIO 많은 디바이스들, 그리고 디스크 파일들을 위하여, 이 에러는 하드웨어 에러를 지적한다.
EIO는 또한 제어 중인 터미널로부터 배경 프로세스가 읽기를 시도하고, SIGTTIN의 신호가 아무런 동작도 하지 않고 보내짐에 의해 멈춘 프로세스의 일반적 동작에 대해 발생한다.
이것은 만약 신호가 블록되어지거나 무시되거나, 프로세스 그룹이 부모 프로세스를 잃어 버렸다면 발생되어질 것이다.
ssize_t write (int filedes, const void *buffer, size_t size)
write함수는 기술자 filedes 파일에 버퍼에 있는 size 바이트의 데이터를 쓰는 함수이다. 버퍼에 있는 데이터는 문자 스트링과 널 문자가 필요하지 않다. 반환 값은 실제로 쓰여진 바이트들의 개수이다.이것은 보통은 size와 같지만, 더 적을수도 있다 ( 예를 들어, 만일 물리적 매체가 채워져 있는 경우 ). 에러가 발생하면 write는 -1을 반환한다.
다음의 errno는 이 함수에서 정의한 에러상황이다.
EAGAIN 일반적으로 write 명령하에서 블록 쓰기 동작은 완벽하다.
그러나 만일 그 파일에서 O_NONBLOCK 플래그가 설정되어 있다면, 그것은 어떤 데이터도 쓰지 않고 곧바로 반환하고, 에러를 발생한다.
그 상황에 대한 하나의 예는 프로세스가 출력하려는 블록을 STOP 문자를 받아들임으로 인해 출력이 일시 중단되고, 흐름제어를 지원하는 터미널 디바이스에 쓰기를 시도할 때 발생한다.
EWOULDBLOCK. GNU 라이브러리에서는, EWOULDBLOCK은 EAGAIN의 다른 이름이다.
그래서 당신이 어떤 이름을 사용해도 문제가 발생되지 않는다.
어떤 시스템들은, 특별한 문자 파일로부터 데이터의 큰 덩어리를 쓰려 할 때, 만일 커널(kernal)이 당신의 것을 담을 수 있는( to lock down the user's pages ), 충분한 물리적 메모리를 얻을 수 없는 경우에 EAGAIN의 에러를 내고 실패했음을 지적한다.
디바이스가 사용자의 메모리 영역을 직접적으로 억세스 하는 것이 제한되어 있는 것은 그들은 커널내부의 분리된 버퍼를 사용하기 때문이다. 그것에 터미널들은 포함되지 않는다,
EBADF filedes 인수는 유용한 파일 기술자가 아니다.
EFBIG 파일의 크기가 그 실행에서 지원할 수 있는 것보다 크다.
EINTR write 오퍼레이션은 명령이 완전히 수행될 때까지 기다리는 동안 신호에 의해 인터럽트 되어졌다.
EIO 많은 디바이스들, 그리고 디스크 파일들을 위하여, 이 에러는 하드웨어 에러를 지적한다.
EIO는 또한 제어 중인 터미널로부터 배경 프로세스가 읽기를 시도하고, SIGTTIN의 신호가 아무런 동작도 하지 않고 보내짐에 의해 멈춘 프로세스의 일반적 동작에 대해 발생한다.
이것은 만약 신호가 블록되어지거나 무시되거나, 프로세스 그룹이 부모 프로세스를 잃어 버렸다면 발생되어질 것이다. ENOSPC 디바이스가 차 있다.
EPIPE 이 에러는 어느 프로세스에 의해서 읽기 위해 개방되지 않는 파이프나 FIFO에 쓰려 시도할 때 반환된다.
이것이 발생될 때, SIGPIPE 신호를 프로세스에 보낸다.
당신이 EINTR 실패를 방지하기 위해 조정하지 않았다면, 당신은 실패한 write의 호출에 대해서 errno를 체크해야할 것이다. 그리고 만일 errno가 EINTR 이라면, 그냥 간단하게 다시 호출해주면 된다.
이것을 하는 쉬운 방법으로 매크로 TEMP_FAILURE_RETRY 가 있다. 다음처럼:
nbytes = TEMP_FAILURE_RETRY (write (desc, buffer, ount));
write 함수는 fputc처럼 스트림에 쓰는 모든 함수들에 기본적으로 포함되어 있다.
출처: http://sthyun.tistory.com/entry/EWOULDBLOCKEAGAIN [개발자를 넘어 과학자로!!]