select 함수로 여러 소켓의 상태를 감시할 경우.

byulparan의 이미지


구글에서 여러번 검색을 해봤는데 비슷한 질문은 많은데 시원한 답변이 없어서
질문드려봅니다.

select 함수로(혹은 다른 멀티플렉싱 도구들로..) 여러 소켓의 입출력을 관리할때
대게 소켓은 논블로킹으로 설정한다는 설명을 보았습니다.
libevent 의 예제들을 봐도 소켓을 논블록으로 설정을 하더군요.

그 이유가 무엇일까요?
select 라면, readable 혹은 writable 하기때문에 리턴되고, 때문에 해당 읽기/쓰기가
블로킹 되지 않는다는 걸 보장 하지 않나요?

검색중에 다음을 보고
http://superkkt.com/81

아..이경우 write 가 블로킹 될 수도 있겠구나…..함을 알았습니다.^^;;

그런데 읽기 전용의 경우 select 가 readable 하다고 판단하여 리턴되었음에도
read 가 블로킹 될 수 있는 예가 있는지요? 소켓버퍼에 데이터가 들어오고, 감시하던
select 가 리턴되었다면 read 함수를 한 번 만 호출할 경우 현재 버퍼에 있는 데이터 만큼은,
혹은 read 함수에 지정한 사이즈 만큼은 읽어올 수 있음을 보장하지 않나요?

자바의 nio 가 논블로킹 소켓을 강제하고(맞나요??), 많은 분들이 멀티플렉싱을 이용할 경우 논-블로킹 소켓과
함께 쓰기를 권장하는 이유가 궁금합니다.

harion01의 이미지

블로킹 모드에서 read 함수로 10바이트를 읽어오게 할 경우, 버퍼에 5바이트만 있다면?

소켓 버퍼에 10바이트가 찰때까지 대기하게 됩니다.

시스템이 read 함수에서 멈추는거죠.

반면에 논블로킹으로 read 함수를 호출 할 경우, 버퍼에 데이터가 적건, 많건, 없건 일단 read 함수는 결과값을 리턴하게됩니다.

즉 논블로킹일 경우 read 함수에서 멈추는 일은 없죠.

여기에 select 의 개념을 더한다면

블로킹 read 의 경우

1. socket 버퍼에 1 바이트 데이터가 옴

2. select 에서 감지 -> read 함수 호출

3. read 함수에서 10바이트를 읽으려 함

4. 9바이트가 모자라기 때문에 read 함수에서 9바이트가 socket 버퍼에 찰 때까지 기다림

논 블로킹 read 의 경우

1. socket 버퍼에 1 바이트 데이터가 옴

2. select 에서 감지 -> read 함수 호출

3. read 함수에서 10바이트를 읽으려 함

4. 9바이트가 모자라지만 read 함수에서 대기를 안하고 적절한 리턴값을 주고 계속 진행

다만 논블로킹을 쓰게 될 경우, read 함수에서 리턴된 값은 어떤식으로 리턴될지 보장 못하기에, 예외처리는 프로그래머의 몫이됩니다.

byulparan의 이미지

바보같이 read() 함수의 동작을 잘못 파악하고 있었네요.
감사합니다 :-) 확실하게 이해했습니다.

>>수정
가 아니라...=.= 집에 오는 길에 생각해봤는데

read() 함수는 블로킹/논블로킹 상관없이 읽을 게 1바이트라도 있으면 1바이트만 읽고, 읽은
만큼을 리턴하지 않나요? 그래서 tcp 의 경우 항상 read 함수의 리턴값으로 얼마만큼을 읽었는지
확인해가며 진행해야 하는 걸로 알고 있었는데.....

10바이트를 읽으려고 시도 할때 소켓버퍼에 1바이트만 있으면 나머지 9바이트를 기다리나요?
recv() 함수의 MSG_WAITALL 플래그를 준 경우가 아니라도요?

익명 사용자의 이미지

커널 소스
net/ipv4/tcp.c 의 tcp_recvmsg() 함수를 참조하시면 recv() 작동방식을 알 수 있습니다.

요약하면,

MSG_WAITALL 가 아니고 특별히 지정한 recv water mark 가 없는 이상 1입니다.
1 바이트만 수신을 해도 리턴한다는 말입니다. (수신했을 당시 백로그 큐에 더 이상 세그먼트가 없으면..)
즉, recv 는 한번 요청에 많은 바이트를 수신하려고 노력하지만 여의치 않으면 그냥 리턴 해버립니다.

즉 harion01 님이 말씀하신 블록 모델일 때 recv(10) 요청한다고 무조건 10바이트 수신 때 까지 기다린다는 건 틀린 내용입니다.
그런 방식으로 작동하게 하려면 MSG_WAITALL 이나 SO_RCVLOWAT 소켓 옵션을 수정해야 합니다.

harion01의 이미지

막연히 socket 옵션을 non-block 으로 설정 안한상태에서 read 함수를 쓸 경우 블록된다고 생각하고 있다가, 익명님의 답변을 보고 좀더 검색해봤습니다.

그결과, non-block 으로 설정 안한상태에서 read 함수를 쓸 경우 socket 옵션에 따라 블록될 수도, 안될 수도 있다는걸 알게됬습니다.

검색한 내용 링크입니다.

http://kldp.org/node/26136 - read / recv(msg_waitall) 비교

http://kldp.org/node/57755 water mark 관련

http://211.221.225.175/~comsys/NP_PDF/chapter5.pdf 소켓 옵션 pdffile

http://www.nxmnpg.com/ko/2/setsockopt get/setsockopt 메뉴얼

테스트를 위해 제작해본 소스입니다.

*************************************
int main(){

struct sockaddr_in addr, addr_cla;
int fd, fd_cla, select_ret, get_ret;
int ret_buf, opt_len, change_size;
int ret = 0;
char read_buf[100];
fd_set read_fds;

fd = socket(AF_INET, SOCK_STREAM, 0);

opt_len = sizeof(int);
get_ret = getsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, &ret_buf, &opt_len);
printf("get_ret (%d) ret_buf (%d)\n", get_ret, ret_buf);

change_size = 5;
get_ret = setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, &change_size, sizeof(int));
printf("SO_RCVLOWAT size changed get_ret (%d)\n", get_ret);

get_ret = getsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, &ret_buf, &opt_len);
printf("get_ret (%d) ret_buf(%d)\n", get_ret, ret_buf);

memset((void *)&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons((u_short)9090);

ret = bind(fd , (struct sockaddr *)&addr, sizeof(addr)) ;
printf("bind ret (%d) \n", ret);
if(ret < 0 )
return 0;

ret = listen(fd, 5);
printf("listen ret (%d) \n", ret);

while(1){
fd_cla = accept(fd, NULL, NULL);
printf("fd_cla (%d) \n", fd_cla);

memset(read_buf, 0x00, sizeof(read_buf));
ret = read(fd_cla, read_buf, 100);

printf("errno (%d) errmsg (%s) \n", errno, strerror(errno));

printf("read buf (%s) ret (%d)\n", read_buf, ret);
}
}

********************************************************
테스트는 다음과 같았습니다.

1. 테스트용 클라이언트로 3바이트(5바이트 이하) 데이터를 전송한 경우.
-> read 함수에서 블록되고, 한번 더 데이터를 보내서 버퍼에 5바이트 이상 차게 될 경우 read 함수는 블로킹에서 풀린다.

2. 테스트용 클라이언트로 6바이트(5바이트 이상) 데이터를 전송한 경우.
-> read 함수에서 블록되지 않고, 수신한 6바이트 데이터를 출력.

즉, read 함수에서의 block 은 read 함수 인자값이 아니라 생성된 socket 의 SO_RCVLOWAT 옵션에 의존됩니다.

잘 모르는상태에서 답변을 적어서 헷갈리게해서 죄송합니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.