SIGPIPE가 생기는 이유가 뭔지 궁금합니다.

MackTheKnife의 이미지

스티븐슨 아저씨의 책에 실린 예제로 테스트중인데
클라이언트에서 서버로 최초한번은 데이터가 전송/수신이 되지만 그다음부터는
데이터 전송/수신이 되지 않습니다.

SIGPIPE시그널이 발생하더군여..
원인이 몰까여?
클라이언트를 쓰레드로 돌려서 데이터전송을 계속 시켜도 마찬가지더군여.

socketoption은 변경하지 않았고 클라이언트 블럭킹소켓,서버는 non blocking을 사용합니다.

클라이언트 소스
....
if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) < 0)

while(1)
{

write(client_sockfd, buf_in, 255);

read(client_sockfd, buf_get, 255);
}
close(client_sockfd);


}

서버 소스
parent process(....)
{
listen()
bind()

prefork child process.(...)

while(1)
select(...)
accept(...)

write_fd(..child process에 연결된 fd전송)
close(연결된 fd)
}

child Process(.........)\
{
...
close(listenfd)
read_fd(parent에서 fd전송받음) <-- 전송받으면 항상 listenfd가 전송됨.??

read()
write()

close(연결된fd)
...
}

codebank의 이미지

서버소스가 저렇게 되어있나요?
SIGPIPE는 파이프라인이 손상되었을때 발생하는거라고 알고 있었는데...
서버소스가 저렇게되버리면...
음...
select를 한후에 accept를 부를 필요가 있나요?
select해서 이벤트가 도착한 것을 감지하면 그 fd번호를 이용해서 read, write를
하는걸로 알고 있었는데 그것을 또 accept해버리면...
모르겠군요...
select는 프로그램중에 턱(기다리는 부분)을 없애기 위해서 사용하는 걸로 알고
있는데 select이후에 accept라... 좀 부적절한 코드같기도하군요.(제가 아는게 별로
없어서... :oops: )

------------------------------
좋은 하루 되세요.

stoneshim의 이미지

서버 소스를 잘 보시면 한번 accept하여 client에게 데이터를 보낸 다음 곧바로 소켓을 close 합니다.

이에 반해 클라이언트측은 한번 connection을 맺은 후에 계속해서 write/read를 수행합니다.

결국 서버측이 한번의 데이터를 보낸 후에 소켓을 close 하므로 클라이언트 입장에서는 한번의 데이터 수신만이 이루어지고, 그 후 두번째의 write 행위에서 sigpipe가 발생할 겁니다.

서버를 하나의 connection에 대해서 여러번의 request를 처리해 주도록 변경하시면 되겠습니다.

sigpipe가 나서 클라이언트가 종료되지 않도록 변경하시려면 sigpipe를 ignore 하세요.

우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자

MackTheKnife의 이미지

stoneshim wrote:
서버 소스를 잘 보시면 한번 accept하여 client에게 데이터를 보낸 다음 곧바로 소켓을 close 합니다.

이에 반해 클라이언트측은 한번 connection을 맺은 후에 계속해서 write/read를 수행합니다.

결국 서버측이 한번의 데이터를 보낸 후에 소켓을 close 하므로 클라이언트 입장에서는 한번의 데이터 수신만이 이루어지고, 그 후 두번째의 write 행위에서 sigpipe가 발생할 겁니다.

서버를 하나의 connection에 대해서 여러번의 request를 처리해 주도록 변경하시면 되겠습니다.

sigpipe가 나서 클라이언트가 종료되지 않도록 변경하시려면 sigpipe를 ignore 하세요.

말씀하신대로 서버에서 하나의 connection에 대하여 여러번 request를 처리할려면 어떻게 해야되져?

서버쪽에서 accept 된 fd를 close하는 부분을 삭제하고 돌리니까
클라이언트에서 SIGPIPE는 일어나지 않으나
서버의 select에서 hang되어있네여
왜 서버쪽에서 select에 걸리지 않느걸까여?

서버소스...

// set file descriptor
FD_ZERO(&masterset);
FD_SET(listenfd,&masterset);

maxfd=listenfd;

nchildren=MAX_CLIENTS;
navail=nchildren;

// create Child Array
childInfo=(Child*)calloc(nchildren,sizeof(Child));

//prefork child process
for(i=0;i<nchildren;i++)
{
MakeChild(i,listenfd); // if parent return else child never return
FD_SET(childInfo[i].child_pipefd,&masterset);
maxfd=max(maxfd,childInfo[i].child_pipefd);
}

// set Signal Handler
signal(SIGINT,SigIntHandler);

for(;;)
{
rset=masterset;
if(navail<=0)
{
FD_CLR(listenfd,&rset);
}

nsel=select(maxfd,&rset,NULL,NULL,NULL);

if(FD_ISSET(listenfd,&rset))
{
connfd=accept(listenfd,&clientaddr,&clientLen);

for(i=0;i<nchildren;i++)
{
if (childInfo[i].child_status==0)
break;
}

if(i==nchildren)
{
printf("too man children\n");
exit(0);
}

// set childInfo busy
childInfo[i].child_status=1;
childInfo[i].child_count++;
navail--;

n=Write_FD(childInfo[i].child_pipefd,"",1,connfd);
// close(connfd);// close appceted fd

//test
printChildInfo();

if(--nsel==0) // select event count=1
continue;
}

for(i=0;i<nchildren;i++)
{
if(FD_ISSET(childInfo[i].child_pipefd,&rset))
{
if((n=read(childInfo[i].child_pipefd,&rc,1))==0)
{
printf("child %d terminated unexpectedly\n");
exit(0);
}
// set chlidInfo idle
childInfo[i].child_status=0;
navail++;

if(--nsel==0)
break;
}
}
}


}

stoneshim의 이미지

n=Write_FD(childInfo[i].child_pipefd,"",1,connfd);

이 부분에서 unix domain socket으로 fd를 전송하도록 되어있는게 맞지요?

아시겠지만 서버의 구조를 살펴보면,

부모 프로세스가 루핑으로 계속하여 클라이언트의 connection을 accept 하고, accept 한 fd를 unix socket으로 자식 프로세스에게 전송한 후에, accept fd를 close 합니다.
자식 프로세스는 unix socket으로 전송받은 fd에서 request를 읽고, 이에 대한 response를 처리하는 방식일 겁니다.

서버를 하나의 connection에 대해서 여러번의 request를 처리할 수 있도록 변경하시려면, 부모 프로세스에서의 accepted fd를 close하는 부분을 막는것이 아니라, 자식 프로세스에서 read_fd(parent에서 fd전송받음)로 부모 프로세스로부터 받은 fd에 대해 클라이언트가 소켓을 close 할때까지 계속해서 서비스를 해주어야 합니다.

질문 처음에 보여주신 소스의 구조로 간단하게 변경해보면...

child Process(.........)\ 
{ 
... 
   close(listenfd) // 이건 fork 후 child_process 를 호출하기 전에 해주시는게 좋지 않을까요?
   read_fd(parent에서 fd전송받음)

   while( 1 ) {
      // 이 부분에 select를 넣을 수도 있겠죠?
      ...
      if( read() == 0 ) { // peer socket closed
         break;
      }
      if( write() < 0 && errno == EPIPE ) { // peer socket closed
         break;
      }
      ... 
   }
   close(연결된fd) 
... 
}

이런정도가 되지 않을까 싶습니다.

아시겠지만 TCP connection에 대해서 peer의 socket이 close 된 상태라면, 이후의 read/recv 등의 system call은 0을 리턴하며, write/send 류의 system call은 -1 을 return하고, errno가 EPIPE 입니다.
물론 이전에 SIGPIPE를 ignore 하셔야 프로세스가 종료되는것을 막을 수 있습니다.

왜 이 모델을 사용하시는지는 잘 모르겠지만, 일반적인 preforked 모델은 이런식으로 부모가 accept하여 unix domain socket으로 자식 프로세스에게 fd를 전달하는 방식이 아닙니다.

unix socket으로 process간의 fd 전달을 공부하시려는 목적이 아니라면 일반적인 preforked server 모델을 사용하시는것이 좋지 않을까요?

우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자

purewell의 이미지

닫혀 있는 소켓이 send 하면 SIGPIPE가 납니다.
(물론 그외의 경우도 있지만요...)

서버 측에서 열심히 send하고 있는데 클라이언트 측에서
(혹은 반대로...)

close(fd) 해버리면 RH7.x, 8에서 SIGPIPE가 발생하며,

어플리캐이션이 종료됩니다.

그래서 ㅡ0-) 전 main 함수에서 SIGPIPE 무시합니다.
(무식하려나...)

_____________________________
언제나 맑고픈 샘이가...
http://purewell.biz

댓글 달기

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