网络编程(28)—— socket的close函数以及shutdown函数在多进程服务器中的不同表现(一)

        这几天遇到了一个很奇怪的问题,编写了一个读写IO分离的多进程socket客户端,主要功能是在成功connect服务端后,fork一个子进程,在子进程中不断的提示用户输入字符串(输入Q或者q会退出程序),然后发给服务端。然后再父进程中不断的读,读取从客户端发送过来的数据。代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<signal.h>
#include<string.h>
#define BUF_SIZE 1024
 
void error_handling(char* message);
void read_handling(int sock);
void write_handling(int sock);
 
int main(int argc,char* argv[])
{
    int sock;
    struct sockaddr_in addr;
    pid_t pid;
 
    if(argc!=3)
    {
        printf("Usage %s<address> <port>\n",argv[0]);
        exit(1);
    }
 
   sock=socket(AF_INET,SOCK_STREAM,0);
    if(sock==-1)
       error_handling("socket() error");
 
   memset(&addr,0,sizeof(addr));
    addr.sin_family=AF_INET;
   addr.sin_addr.s_addr=inet_addr(argv[1]);
   addr.sin_port=htons(atoi(argv[2]));
   
    if(connect(sock,(structsockaddr*)&addr,sizeof(addr))==-1)
       error_handling("connect() error");
 
    pid=fork();
    if(pid==0)
    {
        write_handling(sock);
    }
    else
    {
        read_handling(sock);
    }
    close(sock);
    return 0;
}
 
//写进程
void write_handling(int sock)
{
    char buf[BUF_SIZE];
    while(1)
    {  
       memset(buf,0,BUF_SIZE);
        fputs("Input:",stdout);
       fgets(buf,BUF_SIZE,stdin);
       if(!strcmp(buf,"q\n")||
          !strcmp(buf,"Q\n"))
        {
            //shutdown(sock,SHUT_WR);
            return;
        }
        write(sock,buf,strlen(buf));
    }
}
 
//读进程
void read_handling(int sock)
{
    int str_len;
    char buf[BUF_SIZE];
    while(1)
    {
       str_len=read(sock,buf,BUF_SIZE);
        if(str_len<=0)
            return;
        buf[str_len]=0;
        printf("themessage from server:%s\n",buf);
    }
}
 
 
void error_handling(char* message)
{
    fputs(message,stderr);
    fputc('\n',stderr);
    exit(1);
}     

正常编译上述代码,成功生成可执行文件,然后运行客户端,输入q却无法退出程序:

[Hyman@Hyman-PC multiProcess]$ ./clnt127.0.0.1 9190
Input:q

        按照我的设想,在标准输入q后,子进程(write_handling)会结束死循环,然后回到主程序代码执行  close(sock)代码,然后客户端发送一个EOF结束符给服务端表明要断开连接,服务端接收到EOF后,也会close客户端的socket,并发送一个EOF结束符,而父进程(read_handling)读到EOF后也会结束死循环,返回主函数执行close(sock)代码,OK,程序完美结束。

        但是现实时,无论我输入q还是Q,都无法结束程序。为了查看子进程是否正常结束,我输入ps au查看当前系统中的所有进程:


       请注意红色方框圈出的部分,上面的为主进程,STAT为S+表示正常运行,而下面的红色圈出进程为子进程,STAT为Z+,很明显改进程已经结束,但是由于父进程没有结束而成了僵尸进程。但是这说明我输入q之后成功结束了子进程的死循环,然后成功结束了进程。

       但问题是:结束了子进程说明执行了close(sock)代码,但是为什么没有close成功?

        后面参考一些资料,发现了其中的原因:

        我们平时在客户端或者服务端操作的其实是socket描述符,它相当于一个指针,指向真正的socket,是指针就可以复制,它可以存在多个socket描述符指向同一个socket,在上面的客户端中,子进程会复制一个socket文件描述符,此时的状态是两个socket文件描述同时指向一个socket,如下图所示:


        如果单纯close一个socket描述,不会真正的关闭改socket,只有所有的socket描述符,才会真正的关闭socket,因此在上述情景中,我们在子进程中执行close(sock)并不会真正的关闭改socket,因此此时父进程仍然有一个socket描述符指向改socket。

       那么怎么解决这个问题?

       答案是用shutdown()函数,用shutdown函数关闭socket描述符时,不管还有没有别描述符指向改socket,客户端都会发送一个EOF表示输出关闭。把write_handling函数中shutdown前面的注释去掉,发现程序就能正常关闭。

[Hyman@Hyman-PC multiProcess]$ ./clnt 127.0.0.1 9190
Input:q
[Hyman@Hyman-PC multiProcess]$


Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
Git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL28

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值