소켓으로 짜는 프로그램인데요...포크로..
글쓴이: cccc2002 / 작성시간: 목, 2003/09/04 - 6:31오후
아래의 기능을 포크를 이용해서 다시 만들고 있습니다.
처음 접속되고 나서 데이터가 한번은 잘 넘어갑니다.
그런데 연결된 상태에서 다시 한번 데이터를 넘기면
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); }
Forums:
Re: 소켓으로 짜는 프로그램인데요...포크로..
main 함수를 보면, accept 는 딱 한번만 불려지고, while을 돌면서 close 해버린 socket을 recv 하는 군요.
그것과는 상관없이. 위 에러메시지는
close 된 socket에 send 하면서 에러난것이고 (A)
accept나 recv 대기중에 child process 가 죽어서나는 에러입니다. 이경우 errno==EINTR 인경우인지 확인하여 아무일 없다는 듯 다시 recv 나 accept를 해야합니다.
---
http://coolengineer.com
두가지가 오늘 해결해야할 문제입니다
일단 A클라이언트로 부터 받은 (데이터+아이피 갯수+ip+ip+ip.......)를 가지고 각해당하는 ip피로 데이터를 넘기려고 합니다. 지금은 그냥 데이터+ip 이기때문에 그냥 아이피 부분을 잘라서 해당 아이피로 처리하였습니다만 아이피가 여러게 날라 올 경우 각각을 fork로 생성한 자식에서 처리 하려고 합니다.
만약 자식이 해당 아이피로 접속을 요청 할때 접속이 되지 않으면 자식은 죽으면서 해당 ip에 해당하는 값을 A클라이언트로 넘기려고 합니다.
이것이 첫번째이고
두번째는 포크의 갯수를 5개로 한정하려고합니다.
두가지가 오늘 해결해야할 문제입니다.
많은 의견바랍니다.
답변해주신분께 감사드립니다.
아래 부분중에
그리고
아래부분을 이렇게 처리하였습니다.
이렇게 하였더니 일단은 정상적으로 계속 데이터가 넘어갑니다.
Re: 소켓으로 짜는 프로그램인데요...포크로..
이 함수를 쪼개는게 좋겠군요. 현재 packet을 완성시키는 부분과 완성된 패킷내에서 원하는 정보를 취하는 일이 동시에 일어나고 있습니다.
물론 설명을 들어보니 하나의 패킷이 그다지 크지 않을 것 같고, 한 번의 전송에 원하는 내용이 다 전달 될 것 같지만, tcp의 경우 전송량이 일치하는 것이지 전송 회수가 일치하는 것이 아니라서, 양이 많아 지면, 두번 send가 한 번의 recv에 읽힐 수 있습니다.
따라서, low level 은 하나의 패킷이 완성되었는지 확인하는 부분으로 만들고, 그 상위 level은 완성된 패킷을 구조체등에 받아서, 하나씩 처리하는 식으로 만들어야합니다.
그리고 child 개수를 조정하려면(예로 max 5라면), 전체 IP 개수를 5로 나누어 하나의 process마다 동일한 IP 수를 넣고 loop하는 방법으로 해야겠군요.
accept는 한번만 하시는 것은 아니죠? 한번만 하는 것이라면, 안전하게 설계하려는 의도가 있는 것인데, 아직 위 코드는 그런 생각이 반영되지 않은 상태이군요.
고생하시겠네요.. 오늘 내로 하시려면..
---
http://coolengineer.com
답변감사합니다.패킷은 그다지 크지 않아서 보내는 쪽(A)에서 1024
답변감사합니다.
패킷은 그다지 크지 않아서 보내는 쪽(A)에서 1024크기로 고정해서 보내려고합니다.
그리고 보내는 쪽에서
이런식으로 보내는것이 아니고
[quote="cccc2002"][quote]그리고 child 개
각각 다르므로 loop을 돌면서 하나씩 connect 해야겠지요.
현재 코드는 한번 만하면, 두번째 connection은 못받는 것이 될텐데, 괜찮은 건가요?
fireman 함수 인자를 int 로 바꾸세요. prototype이 달라서 그렇습니다.
원래 signal과 recv를 동시에 처리하는 소스라면, 제가 위에서 답했듯이 errno 값을 확인하여 EINTR 인경우 다시 recv 하도록 해야하는 것입니다. 찾아보세요. man recv
---
http://coolengineer.com
아래인용부분은 accept를 한번만 하는거하고 여러번 하는거를 다 구
아래인용부분은 accept를 한번만 하는거하고 여러번 하는거를 다 구현할 생각입니다.
댓글 달기