TCP实现标准客户/服务模式(停-等模式)

相关网络编程函数:http://blog.csdn.net/somehow1002/article/details/72648743

服务端流程:

1.初始化套结字
2.bind
3.listen
4.阻塞于accept,等待客户端连接

5.有客户端连接到达,父进程通过fork创建子进程对其处理,父进程关闭连接,继续监听

程序:

int main(int argc,char **argv)
{
    int listenfd,connfd;
    pid_t childpid;
    socklen_t clilen;
    struct  sockaddr_in cliaddr,servaddr;

    listenfd=socket(AF_INET,SOCK_STREAM,0);

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servaddr.sin_port=htons(SERV_PORT);

    bind(listenfd,(SA *)&servaddr,sizeof(servaddr));
    listen(listenfd,LISTENQ);

    for(;;)
    {
        clilen=sizeof(cliaddr);
        connfd=accept(listenfd,(SA *)&cliaddr,&clilen);
      
        if((childpid=fork())==0)
        {
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
      close(connfd);
    }
}
void str_echo(int sockfd)
{
    ssize_t n;
    char buf[MAXLINE];
    for(;;)
    {
        bzero(buf,MAXLINE);
        if((n=read(sockfd,buf,MAXLINE))==0)
            break;
        printf("got message from client!\nlength:%d   content:%s\n",n-1,buf);
        writen(sockfd,buf,n);
    }
    printf("No client to serve! I am exiting!");
}
如果客户端关闭连接,那么接受到客户的FIN将导致服务器子进程的read返回0,这又导致str_echo函数返回,从而中止子进程。


客户端流程:
1.初始化套结字
2.通过connect建立与服务器的连接
3.str_cli完成处理工作

程序:

int main(int argc,char **argv)
{
    int sockfd;
    struct sockaddr_in servaddr;
    if(argc!=2)
    {
        printf("useage:tcpcli <IPaddress>");
        exit(1);
    }
    sockfd=socket(AF_INET,SOCK_STREAM,0);

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_port=htons(SERV_PORT);
    inet_pton(AF_INET,argv[1],&servaddr.sin_addr);

    connect(sockfd,(SA *)&servaddr,sizeof(servaddr));
    str_cli(stdin,sockfd);

    exit(0);
}
void str_cli(FILE *fp,int sockfd)
{
    char sendline[MAXLINE],recvline[MAXLINE];

    while(fgets(sendline,MAXLINE,fp)!=NULL)
    {
        Writen(sockfd,sendline,strlen(sendline));
        bzero(recvline,MAXLINE);
        if(read(sockfd,recvline,MAXLINE)==0)
        {
            printf("str_cli: server terminated prematurely");
            close(sockfd);
            exit(-1);
        }
        printf("got message from server:%s\n\n",recvline);
    }
    close(sockfd);
}

当遇到文件结束符或错误时,fgets将返回一个空指针,于是客户处理循环中止。

问题:
当服务器子进程终止时,给父进程发送了一个SIGCHLD信号,但是我们并没有在代码中捕获该信号,该信号的默认处理是被忽略。因为父进程未加处理,子进程于是进入僵死状态。
于是,我们在服务端程序中加入信号处理函数

void sig_cld(int signo)
{
    pid_t pid;
    int stat;

    while((pid = waitpid(-1, &stat, 0)) > 0)
        printf("Child %d terminated\n",pid);
    return;
}
有了信号处理函数,我们在服务端的main函数中再添加上相应的调用即可。


问题:
但是当父进程阻塞于accpet时,子进程可能会终止,sig_chld函数执行。因为该信号是父进程阻塞于慢系统调用时由父进程捕获的,内核可能会使accept返回一个EINTR错误(被中断的系统调用),而父进程不处理该错误,于是父进程终止,服务端程序结束。
为了程序的稳定性和可移植性,对accept函数进行失败情况的处理。
最终的TCP服务器程序main函数如下:

int main(int argc,char **argv)
{
    int listenfd,connfd;
    pid_t childpid;
    socklen_t clilen;
    struct  sockaddr_in cliaddr,servaddr;
    void sig_chld(int);//声明信号处理函数

    listenfd=socket(AF_INET,SOCK_STREAM,0);

    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family=AF_INET;
    servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    servaddr.sin_port=htons(SERV_PORT);

    bind(listenfd,(SA *)&servaddr,sizeof(servaddr));
    listen(listenfd,LISTENQ);
    signal(SIGCHLD,sig_chld);//绑定信号信号名与信号处理函数

    for(;;)
    {
        clilen=sizeof(cliaddr);
        //accept失败情况处理
        if((connfd=accept(listenfd,(SA *)&cliaddr,&clilen))<0)
        {
            if(errno==EINTR)
                continue;//back to for()
            else
            {
                printf("accept error");
                exit(0);
            }
        }
        if((childpid=fork())==0)
        {
            close(listenfd);
            str_echo(connfd);
            exit(0);
        }
        close(connfd);
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hober.z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值