[질문] 네트웍 recv(...) 함수의 동작

alsgo123의 이미지

       if(connect(sockfd,(void*)&servaddr,sizeof(servaddr)) < 0) {
                /* step2: Connect(...); */
                perror("Connect:");
                close(sockfd);
                exit(EXIT_FAILURE);
        }

        sprintf(sbuf,"GET %s%s\n\n",SURL,argv[1]); /* set HTTP request String */
        send(sockfd,sbuf,strlen(sbuf),0);

        if((recv(sockfd,buf,BUF_SIZE,0/*MSG_PEEK*/)) <= 0) {
                perror("Recieve:");
                exit(-1);
        }

        printf("buffer : %s \nChar ! Count : %d\n ",buf,strlen(buf));

code 중의 일부입니다.
전달인자로 받은 웹페이지를 받아오려고 합니다.

buf 의 사이즈는 50000 입니다.
그런데 실제 웹페이지는 3~40000 만 byte 인데... 받은 바이트를 출력해보면
1400 여 byte 밖에 되지 않습니다.

왜 전체페이지를 받아오지 못하는지 모르겠습니다. 조언 부탁드립니다.

감사합니다. ^^

alsgo123의 이미지

이것마저 잘못된 정보라면 지적해주세요.. ^^

지정한데로 다 받아옵니다. 근데 printf(...)나 strlen(...) 을 하게되면
받아온 페이지중에 null 종료문자열이 있는가 봅니다. 그래서 출력을 해보면
거기까지만, 거기 사이즈까지만 출력하게 되는것 같습니다.

감사합니다. :D

낙엽의 이미지

recv로 한번에 다 가지고 올 수도 있고, 안가져 올수도 있습니다.(후자의 경우가 대부분입니다.)

packet을 교환할 때, header에 data size를 명시하시고, 다 받을때까지 계속 받으세요.

그렇게만 처리해서는 정상적으로 받으려는 만큼 받을 수 없습니다.

그리고, 대부분 이런 질문에 관한것들이 예전부터 많이 올라와 있으니 검색해 보시면 더 참조하실수 있겠네요.

mach의 이미지

recv()함수를 보다 자세히 보셔야겠습니다.

recv()하면 지정한 사이즈만큼 다받는것은 아닙니다.
n바이트를 수신하는 recv()를 호출하면, 수신한 바이트 수를 ret라고 가정하면
즉,

    int ret;
    if(([b]ret[/b]=recv(sockfd,buf,BUF_SIZE,0/*MSG_PEEK*/)) < 0) { 
                perror("Recieve:"); 
                exit(-1); 
    }

하면
이 결과는 다음과 같을 수 있습니다.
1) ret < 0 : 에러
2) ret == 0 : EOF
3) 0 < ret < n :절반의 성공?
4) ret == n : 성공

절반의 성공이란, recv()함수는 사용자가 지정한 바이트 만큼 무조건 읽어서 주는것이 아니라, 현재 이 프로그램이 수행하는 운영체제가 원격지에서 수신한 바이트 수를 리턴하게 되어 있기때문입니다.
마치 read()함수로 파일을 읽는 상황을 테스트해도 같습니다. 파일크기가 10바이트인데 100바이트를 읽어라라고 한다면? 어찌될까요? 100바이트를 다 읽어줍니까? 이와 같은 시나리오라고 봐야합니다.

3의 경우에는 상대가 다 안보내서 안올수도 있고, 통신선로가 느려서 안올수도 있습니다. 때로 실험실에서 코딩할때는(즉, 통신선로가 우수한곳,다른말로는 빠른곳) 조금 큰사이즈를 보내도 한번에 전송되고, 한번에 수신이 되기도 합니다만, 실전에서는 사용할 수 없는 것이지요.
즉, 일반적인 통신프로그램을 작성할때는 3번의 경우를 항상 고려해야합니다.

따라서, 님은 recv()함수의 기능에 대한 자세한 설명을 읽어보시고 다시 접근하심이 옳습니다. recv()함수는 다른 함수들 이를테면 max()라던가 하는 것처럼 단순하지 않고, 대충 몇몇개 보고 쓸수 있는 함수는 아니거던요. 잘~ 읽어보셔야 합니다. man페이지 번역하는 수준으로 하셔도 무리가 아닙니다. 토시하나 빼먹지마시길.

그럼, 어찌해야 하는가?
"안되면 될때까지!"
바로 이말대로 하시면 됩니다. n바이트가 될때까지 받아라. 단순무식한 방법으로는 이방법이 있습니다. 흔히, readn() 모 이런 사용자함수를 만들어서 사용을 합니다. 즉, 이함수는 n바이트 읽을때까지 계속 읽어라 모 이런것입니다. 검색해 보시면 쉽게 찾을 수 있습니다.
다른 여러 방법들은 이 문제를 다 아시고 난후에 시도해보심이 좋을듯합니다.

참고로, 통신모드가 blocking말고 non-blocking인 경우는 이와는 처리가 조금 달라집니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

charsyam의 이미지

당연히 한번에 가져올 수도 있고 못 올 수 도 있습니다.

recv 라는 것은 현재 recv 소켓 버퍼에 있는 것을 읽어 오는 함수입니다.

tcp 센드에서 상대 측이 데이터를 보내면 recv 버퍼에 데이터가 쌓이게 되고

이걸 recv 를 통해서 읽어오게 됩니다. recv 버퍼에 데이터가 조금 만 쌓여있는

상태에서 recv를 호출한다면, 당연히 그 만큼만 가져오겠죠. 보통, recv 한

번으로 모든 데이터를 받으려고 하는 것은 힘든일입니다.

=========================
CharSyam ^^ --- 고운 하루
=========================

맹고이의 이미지

ssize_t readn(int fd, void *vptr, size_t n) {
        size_t  nleft;
        ssize_t nread;
        char    *ptr;

        ptr = vptr;
        nleft = n;
        while(nleft > 0) {
                if((nread = read(fd, ptr, nleft)) < 0) {
                        if(errno == EINTR)
                                nread = 0; 
                        else
                                return (-1);
                } else if(nread == 0)
                        break;                  
                nleft -= nread;
                ptr += nread;
        }
        return(n - nleft);                  
}

제가 써먹고 있는 readn()입니당.
제가 만든건 아니고.. 스티븐스 아저씨께서 만든거지만;
그냥 참고하시라고 올려봅니다.
이런식으로 recvn()도 만들면 되겠네용
lutanist의 이미지

recv 함수의 플래그 값으로
MSG_WAITALL 을
설정해 보시지요.

플래그에 대한 설명은
Man 페이지 또는 스티븐스
아저씨의 UNP를 참조하세요.

alsgo123의 이미지

잘 해결했습니다. Steven 아저씨 책도 참고하고.. 매뉴얼도 다시보고..

^^ 좋은 하룹니다.

lutanist의 이미지

그런데 저도 질문...

MSG_WAITALL 플래그가 Linux에서는 잘 되는데
HP-UX 11에서는 잘 동작을 안하네요.

혹시 HP-UX에서 써 보신분은 답변 좀 부탁드려요.

mach의 이미지

Quote:
MSG_WAITALL 플래그가 Linux에서는 잘 되는데
HP-UX 11에서는 잘 동작을 안하네요.

HP-UX 11.0 에서는 그 플래그를 지원하지 않습니다. 이후버전은? 글쎄요.
더 진보된 환경을 가지셨거나, 아시는 분이 답변을 부탁합니다.

* 여담으로, 플래그라던가, 특정 ioctl또는 fcntl을 가급적 사용하지 않고 프로그램을 짜야, 여기 저기기종별 포팅이 쉬운듯합니다. non-blocking을 위한 정도라면야 대부분(전부확인해보지는 못해서요) 지원하니 상관없지만, 기타 특정 플래그등은 사용시 검토과정을 거치시는게 좋은듯합니다. 여기서 검토란 작성된 프로그램이 나중에라도 수행되어야하는 환경을 고려하는 것이지요. 리눅스전용으로 만들겠다면 모르지만, sun,hp,ibm등 기종에서도 돌려야 한다면 그때는 조금 고민좀 해야합니다.

* Stevens님의 UNP 2nd edition에서 언급된 내용은 특히 더 그런 내용이 많아 보입니다. 1st edition에 비해 상세하기는 하지만 어찌보면 잡다하다고도 하겠습니다.(이크 돌맞을라~) 조금은 운영체제 , 버전 및 기종을 탈만한 내용이 종종 보여서요 --; 내용을 보고, 골라서 쓸수 있는사람에게는 좋겠지만, 초보자는 고민좀 많이 될 듯......

** 하여간 여담이었습니다. 믿거나 말거나 --;

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

lutanist의 이미지

mach님의 특정 ioctl또는 fcntl을 가급적 사용하지 않고 프로그램을 짜야
포터블 하든 말씀에 전적으로 동감 합니다.

하지만 HP-UX 11.0의 동작에는 조금 이상한 부분이 있는 듯 합니다.
아래는 socket.h 파일과 Man page의 발췌 내용입니다.

HP-UX 11, /usr/include/sys/socket.h 내용 부분의 발췌입니다.

ifdef _XOPEN_SOURCE_EXTENDED
#define MSG_EOR 0x8 /* data completes record */
#define MSG_TRUNC 0x10 /* data discarded before delivery */
#define MSG_CTRUNC 0x20 /* control data lost before delivery */
#define MSG_WAITALL 0x40 /* wait for full request or error */

recv 함수의 Man 페이지 발췌입니다.
The flags parameter accepts a new value, MSG_WAITALL, which
requests that the function block until the full amount of data
requested can be returned. The function may return a smaller
amount of data if a signal is caught, the connection is terminated,
or an error is pending for the socket.

이렇게 header 파일이나 Man page에는 지원된다고 언급이 되는데
실제적으로 사용이 안됩니다.

혹시 제가 잘못 알고 있는건지요?

mach의 이미지

Quote:
이렇게 header 파일이나 Man page에는 지원된다고 언급이 되는데
실제적으로 사용이 안됩니다.

혹시 제가 잘못 알고 있는건지요?

음 먼저 제가 사과의 말씀을 드립니다. 몇년전에 해본 것을 가지고 안되는 것으로 말씀을 드렸습니다만, HP-UX에서도 그 기능은 수행이 되는군요. 단 몇가지 고려사항이 있습니다.
먼저 사실부터 말씀을 드리면, HP-UX의 X/Open socket에서만 지원되는 기능입니다.

1) HP-UX는 2가지 종류의 socket을 지원
(1) HP-UX socket :Release 10.01부터 지원, default 세팅임
(2) X/Open socket
2) X/Open socket 사용법
(1) Source나 Makefile에 아래 매크로를 문법에 맞게 기술하여 컴파일

Quote:
#define _XOPEN_SOURCE_EXTENDED 1

(2) link옵션으로 "-l xnet"을 추가("libxnet.sl" 과 링크를 위함)
3) X/Open socket과 HP-UX socket은 다소의 차이점이 있음(전체적인 고려필요)

2)를 수행하여 예제를 작성하시면 님이 원하시던 결과를 보실 수 있을것입니다.
3)을 고려해야하는대요, 각 소켓함수별로 다소간의 차이점이 있습니다. 보다 자세한 사항은 X/Open Socket에 관한 내용을 찾아보시면 좋으실듯하고요. X/Open socket은 저도 별로 아는바가 없습니다. (이제 공부하려 합니다 :))

제 개인적인 생각으로는 얻는것도 있지만, 노가다도 많아 보이네요.

전체적으로 님의 질문에 저도 하나의 깨우침을 얻었습니다.

Quote:
멈추면 퇴보한다.

님덕분에 다시 깨달음을 얻습니다. 감사합니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

lutanist의 이미지

제가 더 감사드립니다.

님의 답변을 듣고 제 소스를 다시 보니 _XOPEN_SOURCE_EXTENDED 를
define 할 때 삽질을 했더군요. :lol:
덕분에 이제 HP-UX에서도 동작을 합니다.

Linux, Solaris, HP-UX는 해결됐고 AIX 4.3으로 가서
또 삽질을 해 봐야겠네요. :lol:

감사합니다~~

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.