pipe()를 사용하여 데이터를 전송하고자 할 때 질문입니다.

ksjsc의 이미지

안녕하세요.

아래에 제 아이디로 동일한 질문을 두번 했었는데요.
아직 해결되지 않아서 다시 질문합니다.

먼저, 제가 구현하고자 하는 내용은 pipe()를 사용하여 server process와 client process간의 데이터전송이 되겠습니
다.

아래 코드를 보시면 아시겠지만, 제가 정의한 구조체를 write()함수를 사용해서 client process로 전송하면, 이를 수신한 client process가 이에 대한 내용을 출력하는 심플한 프로그램입니다.

그런데, 여기서 문제되는 점은 seq변수의 경우 client에서 값을 정확히 읽어올 수 있지만 data변수는 포인터로 정의되어 있어서 write()함수를 사용하면 포인터가 가지고 있는 내용이 아니라 포인터의 주소값만이 client process에게로 전송되어서 client process에서는 data변수가 가리키는 값을 출력할 수 없다는 것입니다.

이에 대한 해결책으로 아래 질문에 답글을 달아주신 분들이 data변수를 포인터변수로 선언하지말고 array로 잡아야 한다는 의견을 주셨습니다. 그러나, 이렇게 하면 data의 size가 static하게 되는 문제점을 가지고 있습니다. 제가 구현해야 할 프로그램에서는 data의 크기에 따라서 변수의 크기가 variable해야 합니다. 그리고 seq변수와 data변수의 값을 따로 전송하면 안되고 한번의 write()함수를 사용해서 전송해야 합니다.

또한, 이에 대한 답글로 "flexable array"에 대한 정보를 알려주셨습니다. 제가 현재 개발환경이 FreeBSD 4.6 인데요. 이 환경에서 위의 기능을 사용하기 위해서 구조체의 data변수를 char data[]로 선언하게 되면 컴파일시에 "incomplete type"이라는 에러를 발생시킵니다.

어떻게 하면 제가 하고자하는 기능을 구현할 수 있을까요?

다시 한번 고수님들의 답글 부탁드립니다.

#include<stdio.h> 
#include<unistd.h> 
#include<stdlib.h> 

typedef struct { 
   unsigned short int seq; 
   char *data; 
}header; 

void DataLinkSend(int fd) 
{ 
    header *Header = (header *)malloc(sizeof(header)); 
   char *buffer = (char *)malloc(sizeof(char)*10); 

   memset(buffer, '*', 10); 
    Header->seq = 0x1234; 
   Header->data = buffer; 
   write(fd, Header, sizeof(header)); 

   free(buffer); 
} 

void DataLinkRecv(int fd) 
{ 
   header *temp = (header *)malloc(sizeof(header)) ; 
    
   read(fd, temp, sizeof(header)); 

   printf("received seq : %x\n", temp->seq); 
   printf("received data : %s\n", temp->data); 
} 

int main(int argc, char **argv) 
{ 
   int fd[2]; 
   pid_t pid; 

   pipe(fd); 
    
   pid = fork(); 

   if(pid == 0){ 
      DataLinkSend(fd[1]); 
   } 
   else{ 
      DataLinkRecv(fd[0]); 
   } 

   return 0; 
} 
pynoos의 이미지

typedef struct {
   int len; 
   unsigned short int seq; 
   char data[1024]; 
}header; 

이렇게 작성하시고,

write( & hdr, hdr.len );

으로 보내시면 됩니다. 가변적인 길이를 보낼 때는 항상 길이에 대한 값도 넘어가야 말끔한 프로토콜이 됩니다.

받을 때에는 두번 나눠서 len 부분을 먼저 받고 다음을 받으시면 됩니다.

ksjsc의 이미지

질문에...data의 크기를 가변적으로 잡고자 했는데요..

char data[1024];

요렇게 하면 static하게 되지 않나요?

그리고 write()를 이렇게 사용해도 되나요?

write( & hdr, hdr.len );

file descriptor도 없는데요..^^;;

마지막으로 받을 때에는 두번 나워서 len 부분을 먼저 받고 다음을 받으라는

게 무슨 말씀이신지..잘 이해가 되지 않네요...

괜찮으시다면 코드로 좀 보여 주실 수 없나요?

그럼, 다시 한번 답변 부탁드립니다.

안녕하세요?

pynoos의 이미지

data의 크기는 최대 크기로 잡으시는 것이구요.
write 예제는 fd 값이 빠졌군요. ㅋ~
보내기 위해 패킷을 만들 때, seq와 데이터 길이를 조합한 값을 len에 넣어 두면 가변 길이를 의미하게 됩니다.
어차피 프로토콜을 설계할 때 데이터의 최대 크기는 정하는 것이 맞고,
그렇다면, 그 크기가 수용될만한 공간을 어디엔가에는 잡아 둬야,
전송이나 수신할 때 사용할 수 있게됩니다.

받을 때는 상대가 얼마나 되는 크기를 보낼지 모르므로, 일단
4 byte 정도로 앞으로 올 길이를 먼저 수신해보고, 그 길이 만큼 스트림을
읽는 방식으로 생각하여 설계하시면 됩니다.

write, read 의 회수를 생각하시면 안되고, 보내는 양과 받는 양을 생각하셔서 설계하세요.
즉, 저쪽에서 몇번에 보냈는지를 이쪽에서 아는 설계를 하면 안된다는 것이고,
다른 말로 하면, 모든 데이터가 다닥다닥 붙어서 왔을경우에도 쪼갤 수 있는
패킷을 설계하라는 것입니다.

도움이 되시길..

happycat의 이미지

sequence, data의 길이, data를 각각 따로 write 하시고, 각각 따로 read 하시면 되겠네요. sizeof는 컴파일 타임에 계산되는 값이기 때문에 '절대' 가변 길이를 넘기는 데에는 사용될 수 없습니다.

예를 들어 위의 코드라면

typedef struct {
   size_t len;
   unsigned short int seq;
   char* data;
}header;

보내는 쪽에서는

   write(fd, Header->seq, sizeof(unsigned short int));
   write(fd, Header->len, sizeof(size_t)); //len에는 data의 길이만큼 들어가 있습니다.
   write(fd, Header->data, Header->len);

받는 쪽에서는

   header temp;
   read(fd, &(temp.seq), sizeof(unsigned short int)); 
   read(fd, &(temp.len), sizeof(size_t));

   temp.data = malloc(temp.len);
   read(fd, temp.data, temp.len);

여기서 포인트는 read할 때에도 malloc을 해 주시라는 겁니다. 읽을 바이트가 얼만큼인지 알 수 없기 때문에 malloc을 사용해 allocation해야 합니다. 다시 말씀 드리지만, sizeof는 컴파일 타임에 계산되는 값이고 가변 길이 어레이를 주고 받을 때는 사용하실 수 없습니다.

위의 코드를 실제 돌려보지 않아 오타가 있을 수도 있겠습니다만, 적당히 수정해 돌려 주시면 될 것입니다. :)

익명 사용자의 이미지

happycat님의 개략적인 알고리즘에 덧붙여,
...
예를 들면,
ret = write(fd, "abcdefg", 7);
ret <= 7 일 수 있다는 것을 유념하시고 코딩하세요.

마찬가지로,
ret = read(fd, buff, 7);
ret <= 7 임에 유의하시고요.

* 검사의 생활화~

amister의 이미지

흠. static array 를 크게 잡고 일부만 쓰는 방식과, flexible size array를 사용하는 방법 두 가지가 있습니다. (물론 그 외에 갖가지 방법이 다 있습니다. 구현하기 나름.)

대충 코드가 어떤 식이 되는지 예만 보여드리겠습니다.

1. static array를 크게 사용하는 경우

#define MAX_DATA_LEN 4096

typdef struct {
    int seq;
    size_t len;
    char data[MAX_DATA_LEN];
} Packet;


int Send(int sock, int seq, char* data, size_t datalen)
{
    Packet p;

    if (datalen > MAX_DATA_LEN) return -1;

    p.seq = seq;
    p.len = datalen;
    // 사실 이 예에서는 memcpy가 필요없지만, 그냥 data 처리하는 코드라고 생각해주세요. 겔. ;;
    memcpy(p.data, data, datalen);

    return write(sock, &p, sizeo(int) + sizeof(size_t) + datalen);
}


int Read(int sock)
{
   Packet p;

   read(sock, &p, sizeof(int) + sizeof(size_t));
   read(sock, p.data, p.len);

   // ...

   return 0;
}

2. Flexible Array를 사용하느 경우

typedef struct {
    int seq;
    size_t len;
    char data[0];
} Packet;

#define sizeof_packet(len) (sizeof(Packet) + len)


int Send(int sock, int seq, char* data, size_t datalen)
{
    Paket *p = malloc(sizeof_packet(datalen));
    int e;

    p->seq = seq;
    p->len = datalen;
    memcpy(p->data, data, datalen);

    e = write(sock, p, sizeof_packet(datalen));
    free(p);
    return e;
}


int Read(int sock)
{
    Pakcet tmp;
    Packet* p;

    read(sock, &tmp, sizeof(tmp));
    p = malloc(sizeof_packet(tmp.len));
    *p = tmp;
    read(sock, p->data, p->len);

    // ...

    free(p);
    return 0;
}

컴파일해보지는 않았지만 대충 저런 식이 됩니다.

그리고 flexible array를 사용할 경우, 보시다시피 패킷 헤더랑 바디랑 나누어져있지 않으면 처리하는데 약간 귀찮게 됩니다. 그래서 많은 프로토콜들이 패킷 헤더로 패킷 사이즈와 타입 등을 명시해서 보냄으로써 패킷 받는 쪽에서 유연하게 대처할 수 있도록 하죠. 참고하시기 바랍니다.

doldori의 이미지

amister wrote:
2. Flexible Array를 사용하느 경우
typedef struct {
    int seq;
    size_t len;
    char data[0];
} Packet;


이것은 표준의 관점에서는 문제가 있군요. 배열 크기는 0보다 커야 합니다. 의도는
아마 C99에 추가된 flexible array member일 것으로 짐작이 됩니다만.
amister의 이미지

Quote:

amister 씀:
2. Flexible Array를 사용하느 경우
코드:

typedef struct {
int seq;
size_t len;
char data[0];
} Packet;

이것은 표준의 관점에서는 문제가 있군요. 배열 크기는 0보다 커야 합니다. 의도는
아마 C99에 추가된 flexible array member일 것으로 짐작이 됩니다만.

예. 맞습니다. 정확히 C99에 따르면 data[0]가 아니라 data[] 가 되어야겠죠.

원래는 C99가 아니더라도 가능하도록 data[1] 로 정의하고 size 계산할 때 그만큼 보정하는 예를 들려고 했다 그렇게 하면 또 padding 얘기까지 나오게 될 것 같아서 그냥 gcc에서 사용가능한 문법으로 쉽게 표현해봤습니다.

BarracuTa의 이미지

시스템 네트웍 프로그래머지망생입니다....
초보인지라 따라해보았습니다.....
감사합니다.

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <limits.h>

const int MAXLEN=31;

typedef struct {
  int len;
  unsigned short int seq;
  char data[MAXLEN];
}header;

void alrm_action(int signo)
{
  printf("ding ding ding\n");
  exit(1);
}
void DataLinkSend(int fd)
{
  printf("Send Start\n");
  header *Header = (header *)malloc(sizeof(header));
  char *buffer = (char *)malloc(sizeof(char)*41);

  memset(buffer, '*', 10);//10
  memset(buffer+10, '@',10);//20
  memset(buffer+10+10,'$',10);//30
  memset(buffer+10+10+10,'!',10);//40
  memset(buffer+40,'\0',1);//41

  int bufsize=strlen(buffer)+1;
  printf("bufsize %d\n",bufsize);
  int writesize=0;
  int count=0;
  //close(fda[0]);
  while(bufsize>0)
    {
      if(bufsize<MAXLEN)
        writesize=bufsize;
      else
        writesize=MAXLEN;
      printf("%d\n",writesize);
      Header->len=writesize;
      Header->seq=count;
      strncpy(Header->data,buffer+count,Header->len);
      printf("HD %s\n",Header->data);
      write(fd,&(Header->len),sizeof(int));
      write(fd,&(Header->seq),sizeof(unsigned short int));
      
      write(fd,Header->data,writesize);
      bufsize-=writesize;
      count+=writesize;
    }


  free(buffer);
  printf("Send end\n");
}

void DataLinkRecv(int fd)
{
  static struct sigaction act;
  act.sa_handler=alrm_action;
  sigfillset(&(act.sa_mask));
  sigaction(SIGALRM,&act,NULL);

  printf("Rec Start\n");
  header *temp = (header *)malloc(sizeof(header)) ;
  ssize_t nread;
  int i=0;
  char S[32];
  //close(fda[1]);
   while(1)
    {
      alarm(5);
      
      if((nread=read(fd,&(temp->len),sizeof(int))) == 0)break;
      read(fd, &(temp->seq), sizeof(unsigned short int) );
      read(fd,temp->data,temp->len);
      printf("S %s %d \n",temp->data,strlen(temp->data));
      
      alarm(0);
    }

  printf("Rev end\n");
}

int main(int argc, char **argv)
{
  int fda[2];
  pid_t pid;

  pipe(fda);

  pid = fork();

  if(pid == 0){
    printf("Child Start\n");
    close(fda[0]);
    DataLinkSend(fda[1]);
    printf("Child end\n");
  }
  else{
    printf("Parent Start\n");
    wait(NULL);
    close(fda[1]);
    DataLinkRecv(fda[0]);
    printf("Parrent end\n");
  }

  return 0;
}

저희 업소를 방문해 주셔서 감사합니다.

BarracuTa의 이미지

happycat 님 글보고 한번해보았습니다...

happycat wrote:
sequence, data의 길이, data를 각각 따로 write 하시고, 각각 따로 read 하시면 되겠네요. sizeof는 컴파일 타임에 계산되는 값이기 때문에 '절대' 가변 길이를 넘기는 데에는 사용될 수 없습니다.

예를 들어 위의 코드라면

typedef struct {
   size_t len;
   unsigned short int seq;
   char* data;
}header;

보내는 쪽에서는

   write(fd, Header->seq, sizeof(unsigned short int));
   write(fd, Header->len, sizeof(size_t)); //len에는 data의 길이만큼 들어가 있습니다.
   write(fd, Header->data, Header->len);

받는 쪽에서는

   header temp;
   read(fd, &(temp.seq), sizeof(unsigned short int)); 
   read(fd, &(temp.len), sizeof(size_t));

   temp.data = malloc(temp.len);
   read(fd, temp.data, temp.len);

여기서 포인트는 read할 때에도 malloc을 해 주시라는 겁니다. 읽을 바이트가 얼만큼인지 알 수 없기 때문에 malloc을 사용해 allocation해야 합니다. 다시 말씀 드리지만, sizeof는 컴파일 타임에 계산되는 값이고 가변 길이 어레이를 주고 받을 때는 사용하실 수 없습니다.

위의 코드를 실제 돌려보지 않아 오타가 있을 수도 있겠습니다만, 적당히 수정해 돌려 주시면 될 것입니다. :)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

struct header
{
  int len;
  int n;
  char * data;
};

int main()
{
  pid_t pid;
  int p[2];

  if(pipe(p)==-1){perror("pipe");exit(1);}
  if((pid=fork())==-1){perror("fork");exit(1);}

  if(pid==0)//child
    {
      close(p[0]);
      header* H=(header*)malloc(sizeof(header));
      char* buf=(char* )malloc(sizeof(char)*10);
      memset(buf,'%',10);
      H->len=10;
      H->n=2;
      H->data=buf;

      write(p[1],&H->len,sizeof(int));
      write(p[1],&H->n,sizeof(int));
      write(p[1],H->data,H->len);

    }
  else
    {
      close(p[1]);
      header *T=(header*)malloc(sizeof(header));
      read(p[0],&T->len,sizeof(int));
      read(p[0],&T->n,sizeof(int));
      T->data=(char*)malloc(T->len);
      read(p[0],T->data,T->len);

      printf("len %d\n",T->len);
      printf("n   %d\n",T->n);
      printf("data%s\n",T->data);
    }

  exit(0);
}

저희 업소를 방문해 주셔서 감사합니다.

댓글 달기

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