您的位置:首页 > 理论基础 > 计算机网络

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

2016-12-06 20:35 381 查看
        这几天遇到了一个很奇怪的问题,编写了一个读写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

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐