네트웍 프로그래밍 중 send()에 대해서 질문합니다.

lovejin0309의 이미지

서버와 connect가 된 상태에서는 아무런 문제가 없지만, connect가 실패할 경우(이더넷이 빠졌거나, 서버가 꺼졌거나, 서버의 IP가 변경이 될경우)에 send()부분에서 프로그램이 종료 되어 버립니다.

send() 부분의 소스입니다.

int sendMsg(char *msg){
     printf(" Message Size = %d\n", strlen(msg));

     if (send(sockfd, msg, strlen(msg), 0) == -1) {
              printf("SendMsg failed.\n");
              return -1;
      }

      printf("sendMsg Success.\n");
      return 0;
}

소스는 위와 같습니다.

만약 connect가 실패한 상태에서 위 함수를 실행하면, if(....) 에서 프로그램이 종료되어 버립니다.

printf("SendMsg failed.\n") 부분도 출력하지 않습니다.

 Message Size = 257

root@hacker > 

이런식이지요.

원인이 무엇일까요?

dreamer의 이미지

connect가 안됬는데, send를 한다는게 일단 이상하구요..
connect가 안되면 sockfd가 정상적인 값을 안 가질 것입니다.
그래서 send가 안될 거라는건 당연 하구요...
그냥 상황이 알고 싶은 거면
send의 return value 체크를 -1이 아닐 경우에대해서 프린트 해보시고, strace나 gdb를 쓰면 돌아 가는 상황을 자세히 알 수 있을 겁니다.

lovejin0309의 이미지

이런 경우가 있기 때문입니다.

처음에는 connect()를 성공했지만, 프로그램이 돌아가고 있는 도중에 서버쪽에 문제가 생겨서 네트웍이 끊겼다고 가정합니다.

이럴 경우, send()를 했을 때, 네트웍이 끊긴 상태이기 때문에, 에러가 발생할 것이고, 화면에 "네트웍에 문제가 생겼습니다." 와 같은 메세지를 날려 주어야 할 것입니다.

그런데 지금 제가 처한 상황은 그런 에러 처리를 하기 전에 send()에서 죽어 버린다는 게 문제입니다.

lovejin0309의 이미지

자답입니다.

네트웍이 끊겨서 send()를 실패하면 SIGPIPE 시그널이 발생합니다.

달걀이 먼저인지 닭이 먼저인지 잘 모르겠지만, 어쨌든, SIGPIPE가 발생함으로써 응용 어플이 죽어 버리더군요.

그래서 다음과 같이 해결했습니다.

     if (send(sockfd, msg, strlen(msg), 0) == -1) {

를 다음과 같이 고쳤습니다.

     if (send(sockfd, msg, strlen(msg), MSG_NOSIGNAL) == -1) {

지금은 잘 돌아갑니다.

MSG_NOSIGNAL

    다른 한쪽의 소켓의 연결이 끊겼을때, 소켓으로부터 발생하는 스트림상의 SIGPIPE를 보내지 않도록 요구한다. 그러나 EPIPE에러는 여전히 반환된다

입니다.

즐거운 하루 보내세요.

jynis2937의 이미지

저도 소켓이 유효하지 않을 때 send 함수를 사용해서 프로그램이 죽어버렸었는데, 이거 보고 고쳤습니다 .감사합니다.

rasungboy의 이미지

왜 접속 끊긴 소켓에 Send 를 할려고 하죠?

sig_pipe 를 막는것도 하나의 방법이지만 그전에 그런

상황이 안 발생하게 해놓는것도 중요할것 같습니다..

lovejin0309의 이미지

connect()가 성공적으로 수행된 다음에 정상적으로 어플이 돌다가, 상대방에 문제가 생기던가 아니면 라인에 문제가 생기던가 하는 경우에, 그냥 send()를 사용하면 어플 자체가 죽어 버립니다.

네트웍에 문제가 생겨도 어플이 안 죽고 send()에서 에러 메세지를 내고 실패만 하면 그 때 다시 connect()를 시도해 보겠는데, 그런것이 아니라, 그냥 죽어 버립니다.

dreamer님께서 send()의 에러 코드를 조사해서 조치를 취하는게 좋다고 하셨지만, 그 에러 코드를 볼 수가 없는 상황이지요. 에러 코드 반환 전에 죽어 버리니까요.

프로그램이라는게, 네트웍만 돌게 하기 위해서 짜는게 아닌이상, 사용 도중 네트웍에 문제가 생겨도 어플 자체는 살아 있어야 하는데, 그게 안 되기 때문에 위와 같은 방식을 사용한 것입니다.

전 네트웍 프로그래밍을 전문으로 하는 사람이 아니라, 아직 별다른 해결 방안을 찾지 못하고 있습니다.

MSG_NOSIGNAL을 사용안하고, 처리할 수 있는 방법이 있으면 알려 주셨으면 합니다.

hey의 이미지

lovejin0309 wrote:
connect()가 성공적으로 수행된 다음에 정상적으로 어플이 돌다가, 상대방에 문제가 생기던가 아니면 라인에 문제가 생기던가 하는 경우에, 그냥 send()를 사용하면 어플 자체가 죽어 버립니다.

네트웍에 문제가 생겨도 어플이 안 죽고 send()에서 에러 메세지를 내고 실패만 하면 그 때 다시 connect()를 시도해 보겠는데, 그런것이 아니라, 그냥 죽어 버립니다.

dreamer님께서 send()의 에러 코드를 조사해서 조치를 취하는게 좋다고 하셨지만, 그 에러 코드를 볼 수가 없는 상황이지요. 에러 코드 반환 전에 죽어 버리니까요.

프로그램이라는게, 네트웍만 돌게 하기 위해서 짜는게 아닌이상, 사용 도중 네트웍에 문제가 생겨도 어플 자체는 살아 있어야 하는데, 그게 안 되기 때문에 위와 같은 방식을 사용한 것입니다.

전 네트웍 프로그래밍을 전문으로 하는 사람이 아니라, 아직 별다른 해결 방안을 찾지 못하고 있습니다.

MSG_NOSIGNAL을 사용안하고, 처리할 수 있는 방법이 있으면 알려 주셨으면 합니다.


시그널 함수로 SIGPIPE를 무시하시면 죽지 않을겁니다. 그리고 반환값을 받으세요.


----------------------------
May the F/OSS be with you..


rasungboy의 이미지

lovejin0309 wrote:

connect()가 성공적으로 수행된 다음에 정상적으로 어플이 돌다가, 상대방에 문제가 생기던가 아니면 라인에 문제가 생기던가 하는 경우에, 그냥 send()를 사용하면 어플 자체가 죽어 버립니다.

대충 이런 시나리오 같네요..

클라이언트로부터 데이타를 받은 서버 받은 데이타를 처리중이다.

클라이언트는 데이타를 보낸후 접속을 끊었다.

받은 데이타를 다 처리한 서버는 클라이언트한테 응답을 보내기

위해 센드를 하였다. 하지만 클라이언트는 접속을 끊었기 때문에

(서버에서는 아직 접속감지를 하지 못한상황) SIGPIPE 발생으

로 프로그램 다운.

제가 비슷한 경우가 있었는데 서버프로그램을 디버깅 하던중이

였습니다. 브레이크 포인터를 걸어놓고 한단계씩 내려가던중

이였는데 클라이언트는 서버로부터 HEART BEAT 패킷을

받지 못하면 접속을 끊는 구조라 오랫동안 서버 디버깅을 잡고

있던 저로서는 그 부분을 생각하지 못하고 있었죠.

클라이언트한테 응답할려고 SEND 부분으로 가는순간 SIGPIPE

로 서버 프로그램이 죽었습니다.

사실 클라이언트가 접속을 끊었으면 접속끊김을 먼저 감지하고

SEND 자체가 아예 실행 안되는 구조여서 문제가 없을줄 알았는데

사실 제 구조적 버그였습니다. 우연히 발견해서 고쳤네요..

만약 이와같은 시나리오라면 select 를 이용한 멀티플랙싱 io

를 통해 어느정도 해결할수 있지 않을까 합니다.

루프 {
   셀렉트();
   어셉트처리();
   read 및 접속종료 처리();
   write 처리();
   exception 처리();
   패컷처리();
};

소켓처리를 위와같이하여 각 처리부분을 모아서 처리하는겁
니다. 이렇게 처리하면 위와같은 상황을 타파할수 있습니다.

상황. 클라이언트가 패킷을 보내고 접속을 끊었습니다.

셀렉트() 에서 read 이벤트를 감지합니다.
어셉트처리() 스킵입니다.
read 및 접속종료 처리() - 온 패킷을 recv 를 합니다.
write 처리() - 스킵합니다.
.......
패킷처리() - 온 패킷을 처리합니다. 그리고 응답합니다.
즉 바로 send 를 호출해서 응답하는게 아니라
임시버퍼에 응답패킷을 셋팅하죠. 그리고 다음
번 루프때 write 처리() 할때 패킷을 보냅니다.

다음번 루프로 옵니다.
셀렉트() - 접속끊김을 감지합니다.
어셉트처리() - 스킵
read 및 접속종료처리() - 접속 끊김을 감지합니다.
해당 소켓을 닫고 관련 리소스를 해제
합니다.
write 처리() - 이전 루프에서 임시버퍼에 응답패킷을 셋팅했
지만 이미 이전단계에서 소켓을 닫고 관련 리소스
를 모두 해제한 상태이기 때문에 사실상 이부분
은 처리가 되지 않습니다...
.....

이런식이면 대충 유요하지 않은 소켓 discrptor 에 대한 send 시
sigpipe 로 인한 프로그램 크래쉬는 일어나지 않을거라 생각
됩니다.

단순히 네트윅 불안정이라던지에 대한 상황으로 인해 sigpipe 가

발생한다면 sigpipe 를 무시할수 밖에 없겠지만 전 아직 이런상황

을 겪어보지는 않았습니다. 사실 그런상황이 있을까 기달리는 중

입니다. 그래서 일부러 sigpipe 를 무시하지 않고 말이죠..

만약 발생한다면 결국 저도 sigpipe 를 무시할수 밖에 없겠지만..

익명 사용자의 이미지

SIGPIPE를 프로세스차원에서 막는것 같읍니다..
1thread에서 소켓처리하는거라면 소켓이 유효한지를 체크할수있겠지만..
2thread에서 한소켓에 recv,send를 하는 경우라면
소켓유효성체크는 힘듭니다.. 불가능한건 아니지만..
그렇게한다면 2thread에서 소켓처리하는 이점이 없어지므로..

질문하신 분께서는 SIGPIPE를 막는것이 편법이라고 생각하시는거 같은데요.. 대부분 그렇게 처리합니다..

댓글 달기

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