소켓으로 짜는 프로그램인데요...포크로..

cccc2002의 이미지

아래의 기능을 포크를 이용해서 다시 만들고 있습니다.
처음 접속되고 나서 데이터가 한번은 잘 넘어갑니다.
그런데 연결된 상태에서 다시 한번 데이터를 넘기면

A에는 ******Broken pipe*****
B에는 *****recv error: Interrupted system call******

에러가 나서 두컴퓨터가 죽어버립니다.
어디가 문제인가요? 다시 한번 고수님들의 의견을 묻고 싶습니다.

Quote:

A클라이언트로 부터 (데이타+특정 아이피)를 받아서 B서버는 특정아이피로 받은 데이터를 넘기는 프로그램을 짜고 있습니다.
여기서 A에서는 send로 반복해서 (다른 특정아이피하고 데이터)를 넘기면 B서버는 다시 받은 또 다른 특정아이피에게 데이터를 넘겨야 합니다.
그래서 B에 서버와 클라이언트를 같이 프로그램밍했는데 고수님들의 의견을 듣고싶습니다.
그럼....
A computer-->send(data+ip1) send(data+ip2) send(data+ip3).......;
b computer-->recv(data+ip1)해서 send(data)ip1에게
recv(data+ip2)해서 send(data)ip 2에게
recv(data+ip3)해서 send(data)ip3에게

#include <errno.h>       /* obligatory includes */
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORTNUM 60001 /* random port number, we need something */
#define MAXHOSTNAME 20
#define MAXLINE 1024


int establish(unsigned short);
void fireman(void);
int get_connection(int s);
void do_something(int);
int read_save(int);

char command[256],option[256];
char rline[MAXLINE],my_msg[MAXLINE];

char client_ip[16]="";
int command_nflag,command_nsize,option_nflag,option_nsize,send_size;
int main()
{
        int s_fd,c_fd,clilen;
        struct sockaddr_in server_addr,client_addr;
        if ((s_fd= establish(PORTNUM)) < 0)     // socket() bind() listen()
        {
                perror("establish");
                exit(1);
        }
        clilen=sizeof(server_addr);
        sigset(SIGCHLD,fireman);                // this eliminates zombies
        if ((c_fd = accept(s_fd,(struct sockaddr *)&server_addr,&clilen)) < 0)
        {
                perror("accept");
                exit(1);
        }
        while(send_size=read_save(c_fd))                 // read and save
        {

                switch(fork()) {
                        case -1 :                               // fork error
                                perror("fork");
                                close(s_fd);
                                close(c_fd);
                                exit(1);
                        case 0 :                    // we're the child, do something
                                close(s_fd);
                                do_something(c_fd);
                                exit(0);
                        default :                     //  we're the parent so look for
                                close(c_fd);
                                continue;
                }
        }
}

int establish(unsigned short portnum)
{
        int    server_fd,client_fd;
        struct sockaddr_in server_addr;

        if ((server_fd= socket(AF_INET, SOCK_STREAM, 0)) < 0)           // create socket
        {
                perror("socket");
                exit(1);
        }

        bzero((char *)&server_addr,sizeof(server_addr));
        server_addr.sin_family= AF_INET;
        server_addr.sin_addr.s_addr=htonl(INADDR_ANY);           // server ip address
        server_addr.sin_port= htons(portnum);// this is portnumber

        if (bind(server_fd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr_in)) < 0)
        {
                close(server_fd);
                return(-1);                            // bind address to socket
        }

        if(listen(server_fd, 3)<0)              // listen
        {
                perror("listen");
                exit(1);
        }
        return(server_fd);
}

// as children die we should get catch their returns or else we get
// zombies, A Bad Thing.  fireman() catches falling children.
//
void fireman(void)
{
        while (waitpid(-1, NULL, WNOHANG) > 0);
}



// this is the function that plays with the socket.  it will be called
// after getting a connection.
//
void do_something(int s)
{
        int ser_cli_fd;
        struct sockaddr_in name_addr;
        if((ser_cli_fd=socket(AF_INET,SOCK_STREAM,0))<0)
        {
                perror("socket");
                exit(1);
        }

        bzero((char *)&name_addr,sizeof(name_addr));
        name_addr.sin_family=AF_INET;
        name_addr.sin_addr.s_addr=inet_addr(client_ip);
        name_addr.sin_port=htons(atoi("60003"));           

        if(connect(ser_cli_fd,(struct sockaddr *)&name_addr,sizeof(struct sockaddr_in))<0)
        {
                printf("Connect failed server_ip: %s\n",client_ip);
                exit(1);
        }

        if(send(ser_cli_fd,rline,send_size,0)<0)
        {
                perror("send error");
                exit(1);
        }
}

int read_save(int t_fd)
{
        int recv_size,ip_nflag,ip_nsize;
        if((recv_size=recv((int)t_fd,rline,MAXLINE,0)) <= 0)
        {
                perror("recv error");
                exit(1);
        }
        memcpy(&command_nflag,rline,4);
        memcpy(&command_nsize,rline+4,4);
        memcpy(command,rline+8,command_nsize);
        memcpy(&option_nflag,rline+sizeof(int)*2 + command_nsize,4);
        memcpy(&option_nsize,rline+sizeof(int)*3 + command_nsize,4);
        memcpy(option,rline+sizeof(int)*4 + command_nsize,option_nsize);
        send_size=sizeof(int)*4 + command_nsize + option_nsize;

        memcpy(&ip_nflag,rline,4);
        memcpy(&ip_nsize,rline + send_size ,4);
        memcpy(client_ip,rline + send_size + sizeof(int),ip_nsize);
        printf("destination server_ip: %s\n",client_ip);        

        return(send_size);
}


pynoos의 이미지

cccc2002 wrote:
int main() 
{ 
        int s_fd,c_fd,clilen; 
        struct sockaddr_in server_addr,client_addr; 
        if ((s_fd= establish(PORTNUM)) < 0)     // socket() bind() listen() 
        { 
                perror("establish"); 
                exit(1); 
        } 
        clilen=sizeof(server_addr); 
        sigset(SIGCHLD,fireman);                // this eliminates zombies 
        if ((c_fd = accept(s_fd,(struct sockaddr *)&server_addr,&clilen)) < 0) 
        { 
                perror("accept"); 
                exit(1); 
        } 
        while(send_size=read_save(c_fd))                 // read and save 
        { 

                switch(fork()) { 
                        case -1 :                               // fork error 
                                perror("fork"); 
                                close(s_fd); 
                                close(c_fd); 
                                exit(1); 
                        case 0 :                    // we're the child, do something 
                                close(s_fd); 
                                do_something(c_fd); 
                                exit(0); 
                        default :                     //  we're the parent so look for 
                                close(c_fd); 
                                continue; 
                } 
        } 
} 

main 함수를 보면, accept 는 딱 한번만 불려지고, while을 돌면서 close 해버린 socket을 recv 하는 군요.

그것과는 상관없이. 위 에러메시지는

close 된 socket에 send 하면서 에러난것이고 (A)
accept나 recv 대기중에 child process 가 죽어서나는 에러입니다. 이경우 errno==EINTR 인경우인지 확인하여 아무일 없다는 듯 다시 recv 나 accept를 해야합니다.

cccc2002의 이미지

일단 A클라이언트로 부터 받은 (데이터+아이피 갯수+ip+ip+ip.......)를 가지고 각해당하는 ip피로 데이터를 넘기려고 합니다. 지금은 그냥 데이터+ip 이기때문에 그냥 아이피 부분을 잘라서 해당 아이피로 처리하였습니다만 아이피가 여러게 날라 올 경우 각각을 fork로 생성한 자식에서 처리 하려고 합니다.
만약 자식이 해당 아이피로 접속을 요청 할때 접속이 되지 않으면 자식은 죽으면서 해당 ip에 해당하는 값을 A클라이언트로 넘기려고 합니다.
이것이 첫번째이고
두번째는 포크의 갯수를 5개로 한정하려고합니다.

두가지가 오늘 해결해야할 문제입니다.
많은 의견바랍니다.
답변해주신분께 감사드립니다.

아래 부분중에

Quote:
sigset(SIGCHLD,fireman);
인용 부분이 잘못되어있어서 일단은 주석 처리했습니다.
그리고
아래부분을 이렇게 처리하였습니다.
Quote:
case 0 : // we're the child, do something
close(s_fd);
close(c_fd);
do_something();
exit(0);
default : // we're the parent so look for
// close(c_fd);
continue;


이렇게 하였더니 일단은 정상적으로 계속 데이터가 넘어갑니다.
pynoos의 이미지

cccc2002 wrote:
int read_save(int t_fd) 
{ 
        int recv_size,ip_nflag,ip_nsize; 
        if((recv_size=recv((int)t_fd,rline,MAXLINE,0)) <= 0) 
        { 
                perror("recv error"); 
                exit(1); 
        } 
        memcpy(&command_nflag,rline,4); 
        memcpy(&command_nsize,rline+4,4); 
        memcpy(command,rline+8,command_nsize); 
        memcpy(&option_nflag,rline+sizeof(int)*2 + command_nsize,4); 
        memcpy(&option_nsize,rline+sizeof(int)*3 + command_nsize,4); 
        memcpy(option,rline+sizeof(int)*4 + command_nsize,option_nsize); 
        send_size=sizeof(int)*4 + command_nsize + option_nsize; 

        memcpy(&ip_nflag,rline,4); 
        memcpy(&ip_nsize,rline + send_size ,4); 
        memcpy(client_ip,rline + send_size + sizeof(int),ip_nsize); 
        printf("destination server_ip: %s\n",client_ip);        

        return(send_size); 
} 

이 함수를 쪼개는게 좋겠군요. 현재 packet을 완성시키는 부분과 완성된 패킷내에서 원하는 정보를 취하는 일이 동시에 일어나고 있습니다.

물론 설명을 들어보니 하나의 패킷이 그다지 크지 않을 것 같고, 한 번의 전송에 원하는 내용이 다 전달 될 것 같지만, tcp의 경우 전송량이 일치하는 것이지 전송 회수가 일치하는 것이 아니라서, 양이 많아 지면, 두번 send가 한 번의 recv에 읽힐 수 있습니다.
따라서, low level 은 하나의 패킷이 완성되었는지 확인하는 부분으로 만들고, 그 상위 level은 완성된 패킷을 구조체등에 받아서, 하나씩 처리하는 식으로 만들어야합니다.
그리고 child 개수를 조정하려면(예로 max 5라면), 전체 IP 개수를 5로 나누어 하나의 process마다 동일한 IP 수를 넣고 loop하는 방법으로 해야겠군요.

accept는 한번만 하시는 것은 아니죠? 한번만 하는 것이라면, 안전하게 설계하려는 의도가 있는 것인데, 아직 위 코드는 그런 생각이 반영되지 않은 상태이군요.

고생하시겠네요.. 오늘 내로 하시려면..

cccc2002의 이미지

답변감사합니다.
패킷은 그다지 크지 않아서 보내는 쪽(A)에서 1024크기로 고정해서 보내려고합니다.
그리고 보내는 쪽에서

Quote:
A computer-->send(data+ip1) send(data+ip2) send(data+ip3).......;

이런식으로 보내는것이 아니고
Quote:
[A computer-->send(data+ip1+ip2+ip3).......; Quote]
이러한 형태로 바꿀 생각입니다.
그래서 받은쪽(B)에서는 그 값을 읽어서 각각해당하는 아이피로 data를 넘기려고합니다.
Quote:

그리고 child 개수를 조정하려면(예로 max 5라면), 전체 IP 개수를 5로 나누어 하나의 process마다 동일한 IP 수를 넣고 loop하는 방법으로 해야겠군요.

위 부분에서 동일한 IP가 아니고 아이피가 각각 틀리기 때문에 각각 해당 아이피로 데이터를 넘기려고 합니다.
Quote:

accept는 한번만 하시는 것은 아니죠? 한번만 하는 것이라면, 안전하게 설계하려는 의도가 있는 것인데, 아직 위 코드는 그런 생각이 반영되지 않은 상태이군요.

accept를 한번만 하려고 했는데 안전하게 설계하려면 달리 취해야 일이 뭐가 있는지 지금은 내공이 부족해서.....조금만 알려주시면 감사하겠습니다.

그리고 sigset();부분을 signal로 고쳐서 컴파일 할때

Quote:

warning: passing arg 2 of `signal' from incompatible pointer type

이런 에러가 납니다. 인자 값이 뭐가 잘못된건지........
그래서 sigset로 해서 컴파일 하면 에러는 안나느데 실행하면 이런 에러가 납니다. recv쪽에서 이런
Quote:
error: Interrupted system call
에러가 나는데

오늘도 해결 해야 할 일이 너무 많네요...

pynoos의 이미지

cccc2002 wrote:

Quote:

그리고 child 개수를 조정하려면(예로 max 5라면), 전체 IP 개수를 5로 나누어 하나의 process마다 동일한 IP 수를 넣고 loop하는 방법으로 해야겠군요.

위 부분에서 동일한 IP가 아니고 아이피가 각각 틀리기 때문에 각각 해당 아이피로 데이터를 넘기려고 합니다.

각각 다르므로 loop을 돌면서 하나씩 connect 해야겠지요.

cccc2002 wrote:

Quote:

accept는 한번만 하시는 것은 아니죠? 한번만 하는 것이라면, 안전하게 설계하려는 의도가 있는 것인데, 아직 위 코드는 그런 생각이 반영되지 않은 상태이군요.

accept를 한번만 하려고 했는데 안전하게 설계하려면 달리 취해야 일이 뭐가 있는지 지금은 내공이 부족해서.....조금만 알려주시면 감사하겠습니다.

현재 코드는 한번 만하면, 두번째 connection은 못받는 것이 될텐데, 괜찮은 건가요?

cccc2002 wrote:
그리고 sigset();부분을 signal로 고쳐서 컴파일 할때
Quote:

warning: passing arg 2 of `signal' from incompatible pointer type

이런 에러가 납니다. 인자 값이 뭐가 잘못된건지........
그래서 sigset로 해서 컴파일 하면 에러는 안나느데 실행하면 이런 에러가 납니다.

fireman 함수 인자를 int 로 바꾸세요. prototype이 달라서 그렇습니다.

cccc2002 wrote:
recv쪽에서 이런
Quote:
error: Interrupted system call
에러가 나는데

오늘도 해결 해야 할 일이 너무 많네요...

원래 signal과 recv를 동시에 처리하는 소스라면, 제가 위에서 답했듯이 errno 값을 확인하여 EINTR 인경우 다시 recv 하도록 해야하는 것입니다. 찾아보세요. man recv

cccc2002의 이미지

아래인용부분은 accept를 한번만 하는거하고 여러번 하는거를 다 구현할 생각입니다.

Quote:

accept는 한번만 하시는 것은 아니죠? 한번만 하는 것이라면, 안전하게 설계하려는 의도가 있는 것인데, 아직 위 코드는 그런 생각이 반영되지 않은 상태이군요.

accept를 한번만 하려고 했는데 안전하게 설계하려면 달리 취해야 일이 뭐가 있는지 지금은 내공이 부족해서.....조금만 알려주시면 감사하겠습니다.

현재 코드는 한번 만하면, 두번째 connection은 못받는 것이 될텐데, 괜찮은 건가요?

댓글 달기

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