소켓으로짜는 프로그램 (포크) 버젼 1-1......

cccc2002의 이미지

서버와 클라이어트가 같이 있는 B부분입니다.

아직 A로부터 받은 아이피를 가지고 C로 접속을 했을때 실패한 경우 다시 A에게 답을 해줘야 하는데 그것을 아직은 구현 못했습니다.
두번째로는 fork()수를 제한 하는 문제가 남아 있지만 오늘중으로 해결하려합니다.

여기서 한가지 묻고싶습니다.
클라이언트가 비정상적으로 죽어버리면 서버가 죽어버리는데 이것을 해결하기위한 방법은어떤것이 있을까요.....
예를 들면 Ctrl +C 를 받아서 클라이언트가 죽는경우 그리고 전원이 나가서 클라이언트가 죽는경우등등 ..............

#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 60007  // random port number, we need something
#define SPORTNUM 60003 //server port number
#define MAXLINE 1024
#define MAXHOSTIP 200

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

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

struct in_addr host_ip[MAXHOSTIP];

int command_nflag,command_nsize,option_nflag,option_nsize,send_size;
int main()
{
        int s_fd,c_fd,ip_count,i;

        if ((s_fd= establish(PORTNUM)) < 0)             // socket() bind() listen()
        {
                perror("establish");
                exit(1);
        }

        signal(SIGCHLD,fireman);                        //  this eliminates zombies

        while(1)
        {
                if ((c_fd= get_connection(s_fd)) < 0)  // accept()
                {
                        if (errno == EINTR)
                                continue;
                        perror("accept");
                        exit(1);
                }

                ip_count=read_save(c_fd);

                for(i=0; i<ip_count;i++)
                {
                        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);
                                        close(c_fd);
                                        do_something(i);
                                        exit(0);
                                default :                    //  we're the parent so look for
                                        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 port number

        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);
}

// catches falling children.

void fireman(int sig)
{
        while (waitpid(-1, NULL, WNOHANG) > 0);
        return;
}


int get_connection(int s_fd)
{
        int t,clilen;                         // socket of connection
        struct sockaddr_in server_addr;
        clilen=sizeof(server_addr);
        if ((t = accept(s_fd,(struct sockaddr *)&server_addr,&clilen))<0)   // accept connection if there is one
                return(-1);
        return(t);
}


void do_something(int i)
{
        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=host_ip[i].s_addr;
        name_addr.sin_port=htons(SPORTNUM);

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

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

int read_save(int t_fd)
{
        int host_ip_count,i;
        if((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(&host_ip_count,rline + send_size,4);
        
        for(i=0;i<host_ip_count; i++)
        {
                memcpy(&host_ip[i].s_addr,rline + send_size + sizeof(int) +sizeof(int)*i ,4);
        }
        
        return(host_ip_count);
}

pynoos의 이미지

Client가 죽는다고 해서 서버가 죽는 것은 프로그램을 잘못작성해서입니다. (우째 그런일이!)

linux 이죠?

디버깅 툴을 이용해보세요.

strace 등을 통해서 어떤일이 일어나고 있는지 알 수 있지요.
gdb를 통해서 일어나는 동안 변수 값을 확인 할 수 있습니다.

http://bbs.kldp.org/viewtopic.php?t=1045

혹 사용하지 않으신다면, gdb는 맨 위 검색에서도 많이 나오니까.. 찾아보시기 바랍니다.

madkoala의 이미지

소켓이 비정상적으로 끊기게 되었을 때 발생하는 시그날에 대한
핸들링을 잘못 해서 그렇습니다.

발생하는 시그날은 SIGPIPE 이며
아래와 같이 처리하시면 간단합니다.

signal(SIGPIPE, SIG_IGN);

cccc2002의 이미지

다만
A에서 send로 넘겨주는 데이터 중에서 B서버의 똑 같은ip 와 port 번호를 넘겨주면 B서버의 클라이언트 부분에서 새로운 socket을 만들때 무한이 많은 fork를 생성해버립니다.
소켓생성시 자신의 포트하고 아이피가 중복이 되서 그런거 같은데 그러면 connect에서 에러가 발생하니까 멈줘야 하는데 그러지 않네요.....
그래서 쪼금 많이 포크 하고 나면 나중에서 시스템에서 알아서 처리해줍니다.

A -->send(data+ 192.168.0.60+60000 192.168.0.61+60001)
B의 아이피와 포트번호는 192.168.0.60 60000 입니다.

이경우 A로부터의 데이터가B의 아이피랑 포트번호가 같기때문에
B의 클라이언트 기능중에서 소켓을 다시 생성할때 충돌이 일어나는거 같습니다.
그래서 에러처리를 했서 잡기는 했는데....조금 이상하네요.

댓글 달기

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