socket을 이용한 데이터 전송 문제

bosub의 이미지

소켓을 이용한 데이터 전송 문제 입니다.

클라이언트가 데이터를 보내고 읽기를 준비하는데요

서버에서 데이터를 받고 받는 구문을 마치지 못하고 계속 받을려고 하네요

무엇이 문제인가요?

클라이언트 소스

20 int main(int argc, char *argv[]) {
     21     struct sockaddr_in servaddr;
     22     int  s, nbyte;
     23
     24     char *buf1 = (char *)malloc(sizeof(char)*512);
     25     char *buf = (char *)malloc(sizeof(char)*1024);
     26
     27
     28     sprintf(buf1,
     29             "<?xml version=\"1.0\" ?>\n"
     30             "<transaction type =\"event\" txid=\"%s\">\n"
     31             "\t<to>homeserver</to>\n"
     32             "\t<from>door1</from>\n"
     33             "\t<command>visit</command>\n"
     34             "\t<arg>\n"
     35             "\t\t<name>authorized_id</name>\n"
     36             "\t\t<value>0001</value>\n"
     37             "\t</arg>\n"
     38             "</transaction>\n"
     39             ,"8091");
     40
     41     sprintf(buf,
     42             "POST /message.cgi HTTP/1.0"
     43             CRLF
     44             "Content-Type: application/x-isas-message"
     45             CRLF
     46             "Content-length:%d"
     47             CRLF
     48             CRLF
     49             "%s"
     50             ,strlen(buf1),buf1);
     51 
     53     if((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
     54         perror("socket fail");
     55         exit(0);
     56     }
     57     // 에코 서버의 소켓주소 구조체 작성
     58     bzero((char *)&servaddr, sizeof(servaddr));
     59     servaddr.sin_family = AF_INET;
     60     inet_pton(AF_INET,"127.0.0.1" , &servaddr.sin_addr);
     61     servaddr.sin_port = htons(8080);
     62
     63     printf("2\n");
     64     // 연결요청
     65     if(connect(s, (struct sockaddr *)&servaddr, sizeof(servaddr))
     66         < 0) {
     67         perror("connect fail");
     68             exit(0);
     69     }
     70
     71     nbyte = strlen(buf);
     72
     73     printf("send message is %d \n %s\n",nbyte,buf);
     74
     75     // 에코 서버로 메시지 송신
     76     if (write(s, buf, nbyte) < 0) {
     77         printf("write error\n");
     78         exit(0);
     79     }
     80
     81     printf("\nwrite end\n ");
     82     // 수신된 에코 데이터 화면출력
     83     printf("receive : ");
     84
     85     if( (nbyte=read(s, buf, MAXLINE)) <0) {
     86         perror("read fail");
     87         exit(0);
     88     }
     89     buf[nbyte]=0;
     90     printf("%s\n", buf);
     91
     92     close(s);
     93     free(buf);
     94     free(buf1);
     95     return 0;
     96 }

서버 쪽 프로그램 소스 입니다. 쓰레드로 돌아갑니다.

 42 /* network module*/
     43 void *network_module(void *data)
     44 {
     45     int sock;
     46     int state;
     47     int client_sockfd;
     48     struct sockaddr_in cliaddr,seraddr;
     49
     50     int addrlen = sizeof(cliaddr);
     51
     52     int pid;
     53
     54     /* create listen socket and listen */
     55     sock = socket(AF_INET,SOCK_STREAM,0);
     56
     57     if (sock == -1) {
     58         perror("don't create socket ");
     59         exit_isas();
     60     }
     61
     62     bzero((char *)&seraddr, sizeof(seraddr));
     63     seraddr.sin_family = AF_INET;
     64     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
     65     seraddr.sin_port = htons(port);
     66
     67     if (bind(sock,(struct sockaddr *)&seraddr, sizeof(seraddr)) < 0) {
     68         perror("bind fail");
     69         exit_isas();
     70        
     71
     72     state = listen(sock,10);
     73
     74     if (state == -1)
     75     {
     76         perror("listen error : ");
     77         exit_isas();
     78     }
     79
     80     signal(SIGCHLD,SIG_IGN);
     81
     82
     83     while (1)
     84     {
     85         client_sockfd = accept(sock,(struct sockaddr *)&cliaddr,&addrlen);     86
     87         pid = fork();
     88
     89         if (pid == 0)
     90         {
     91             int nbyte;
     92             char *buf = (char *)malloc(sizeof(char)*1024);
     93
     94             printf("[pid:%ld]child process \n",(long)getpid());
     95
     96             if (client_sockfd == -1)
     97             {
     98                 perror("Accept error : ");
     99                 exit(0);
    100             }
    101
    102             while (1)
    103             {
    104
    105                 memset(buf,0x00,1024);
    106
    107                 printf("\nread message \n");
    108
    109                 while ((nbyte = read(client_sockfd,buf,1024)) > 0)
    110                 {
    111                     printf("%s",buf);
    112                     fflush(stdout);
    113                     memset(buf,0x00,1024);
    114                     break;
    115                 }
    116
    117                 printf("\nreading end \n");
    118
    119                 write(client_sockfd,"OK",2);
    120
    121                 printf("\nwriting end \n");
    122
    123                 
    124             }
    125
    126
    127             sleep(10);
    128             free(buf);
    129             close(client_sockfd);
    130             break;
    131         }
    132
    133         if (pid > 0){
    134             printf("parent process \n");
    135             fflush(stdout);
    136         }
    137
    138         if (pid == -1)
    139         {
    140             perror("fork error : ");
    141         }
    142     }
    143
    144     close(sock);
    145
    146     printf("this is network module\n");
    147
    148     return 0;
    149 };

서버를 실행하고 클라이언틀를 실행하면
클라이언트에서 xml 메시지를 서버에게 보냅니다.
서버는 이 메지리르 제대로 받고 다른 메시지를 기다리고 있습니다.
그런데 클라이언트에서는 메시지를 보내고 다시 서버에서 보낸 메시지를 받기를 대기 하고 있습니다.

왜 서버에서 메시지가 끝났다는 것을 알지 못하는 걸까요?
종료문자를 같이 넣어서 보내줘야 하나요?
'\0' 넣어서 보내봤지만 결과는 똑같더라고요.

그리고 위 서버 프로그램에서 문제가 생길만 한게 머가 있을까요?

helloneo의 이미지

 while ((nbyte = read(client_sockfd,buf,1024)) > 0)

이렇게하면.. 1024 byte를 다 받을때까지 기다리지안나요..?

WHAT'S UP

alwaysN00b의 이미지

아직 내공이 부족하나..

unix network progrimming 에
데이터를 보낼때 얼마나 보낼건지 알려준다음 보낸다고 알고 있습니다.

그리고, 단순히 자식을 fork 하는 것입니다.
(쓰레드는 아닌것 같습니다)

언제나 시작

익명 사용자의 이미지

apnetwork wrote:
 while ((nbyte = read(client_sockfd,buf,1024)) > 0)

이렇게하면.. 1024 byte를 다 받을때까지 기다리지안나요..?

* 일단 아닙니다.

별다른 언급이 없으므로, 블록킹모드로 코딩되었다고 가정하고.

read()를 호출하면, 호출한 시점에 커널(운영체제)에 도착한 바이트만을 리턴합니다.
0<수신바이트수
인 결과를 리턴하게됩니다.(물론 에러나, 연결단절시에는 0 또는 -1등의 값이 리턴될 수 있으니 유의하시고) 여기서 수신바이트수는 위 코드기준으로 1024 보다 작거나 같은 값을 가지게 됩니다. 반드시 1024를 리턴하지 않습니다.!!!

따라서, 리턴값을 검토하고 나머지(다 받았다면 물론 오케이지만)를 전체중 나머지를 재수신하도록 프로그램해야합니다.
위 소스에서는 1024를 하드코딩하면 안되고,
수신하고자하는 총량-실제받은양(누적으로)
과 유사한 수식을 사용하여 받아야 합니다.
실제받은양은 리턴값으로(read의) 알 수 있으며, 받을 양은 프로그래머의 의도에 의해 프로토콜 설계에서 결정되게 됩니다.

또한, fork()를 사용했는데, child의 종료에 대해 고려가 없어 보입니다. (클라이언트를 빈번하게 연결하면 서버 프로세스가 무한히 증가될것으로 보인다는)
처음 접할때는 서버를 단일 클라이언트에 대해 테스트를 거치고(fork없이) 나중에 멀티프로세스이건 멀티쓰레드이건 확장하는게 도움이 될듯합니다.

전반적으로, 재검토 필요합니다.

bosub의 이미지

Anonymous wrote:
apnetwork wrote:
 while ((nbyte = read(client_sockfd,buf,1024)) > 0)

이렇게하면.. 1024 byte를 다 받을때까지 기다리지안나요..?

* 일단 아닙니다.

별다른 언급이 없으므로, 블록킹모드로 코딩되었다고 가정하고.

read()를 호출하면, 호출한 시점에 커널(운영체제)에 도착한 바이트만을 리턴합니다.
0<수신바이트수
인 결과를 리턴하게됩니다.(물론 에러나, 연결단절시에는 0 또는 -1등의 값이 리턴될 수 있으니 유의하시고) 여기서 수신바이트수는 위 코드기준으로 1024 보다 작거나 같은 값을 가지게 됩니다. 반드시 1024를 리턴하지 않습니다.!!!

따라서, 리턴값을 검토하고 나머지(다 받았다면 물론 오케이지만)를 전체중 나머지를 재수신하도록 프로그램해야합니다.
위 소스에서는 1024를 하드코딩하면 안되고,
수신하고자하는 총량-실제받은양(누적으로)
과 유사한 수식을 사용하여 받아야 합니다.
실제받은양은 리턴값으로(read의) 알 수 있으며, 받을 양은 프로그래머의 의도에 의해 프로토콜 설계에서 결정되게 됩니다.

또한, fork()를 사용했는데, child의 종료에 대해 고려가 없어 보입니다. (클라이언트를 빈번하게 연결하면 서버 프로세스가 무한히 증가될것으로 보인다는)
처음 접할때는 서버를 단일 클라이언트에 대해 테스트를 거치고(fork없이) 나중에 멀티프로세스이건 멀티쓰레드이건 확장하는게 도움이 될듯합니다.

전반적으로, 재검토 필요합니다.

만약에 클라이언트에서 보내는 메시지의 사이즈가 항상 틀리면
서버에서는 이 메시지를 어떻게 받아야 하나요?

5초 앞이라도 내다 볼 수 있다면..

익명 사용자의 이미지

bosub wrote:

...
만약에 클라이언트에서 보내는 메시지의 사이즈가 항상 틀리면
서버에서는 이 메시지를 어떻게 받아야 하나요?

보통 2가지 방법을 사용합니다.
1) 구분자를 사용해서 파악합니다.
- 메시지의 끝부분에 \r\n을 추가시켜보냅니다.
- 메시지의 끝부분에 "." (마침표) 추가시켜보냅니다.
* 이러한 경우, 실제 데이터내에도 구분자가 있으므로, 이에대한 처리 방법론이 필요합니다. bit단위 통신인경우 bit stuffing 이라하고, 바이트단위인 경우 byte stuffing이라고 하지요.
* 또는 데이터를 특정한 문자만 나오게 인코딩(예, base 64)하여 보내고, 메시지의 구분자를 \r\n등으로 표기하는 방법도 있습니다.
* 길이가 제한받지 않는 장점이 있으나, 수신측에서 당연히, 무한한 데이터를 받을 수 없을 것이므로 이에 대한 버퍼관리가 필요하게 됩니다.

2) 프로토콜 헤더에 받을 크기를 명시합니다.
- 헤더 + 데이터 형태로 응용수준의 프로토콜을 정의합니다.
- 헤더는 고정 크기로 정의하고, 헤더 내부에는 수신할 메시지의 길이를 명시하는 필드를 구성하고 이를 사용합니다.

3) 매 메시지를 보내고 연결을 종료합니다.
- 하나의 메시지를 보내고 연결을 종료(수신측에서 read()에는 0 이 리턴됨)
- 지속적으로 연결 및 종료가 필요하므로, 자원소모가 있고, 하나의 세션임을 보이기 위해 부가적인 처리등(http의 경우, 쿠키)이 필요하게 됩니다.
- 하나의 메시지가 아주 긴 경우에 대한 고려가 필요합니다.

** 결국 프로토콜을 정의해야 합니다.

댓글 달기

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