你的位置:首页 > 操作系统

[操作系统]linuxC编程实战 my_server.c例子问题总结


今天看linux C 编程实战的my_server例子时,敲到这段代码,对其父子进程关闭socket 进行close调用产生疑问

如图中标注的三个close socket,思考子进程通信结束 关闭自己的通信socket:conn_fd可以理解,但将sock_fd,服务器的监听socket也关闭就不知怎么回事了。而且父进程执行close(conn_fd)将子进程的通信socket关闭,这样不久让子进程无法和客户端soket通信了吗? 但是程序正常运行。重新查资料发现是自己对子进程资源的复制和close函数,还有文件描述符的理解不到位

 

1. 首先,socket是一种特殊的数据传输IO,也是一种文件描述符。

2. fork创建进程时,子进程共享父进程打开的文件描述符,但父进程对文件描述符的改变不会影响子进程中的文件描述符。

3. close 一个套接字的默认行为是把套接字标记为已关闭,然后立即返回到调用进程,该套接字描述符不能再由调用进程使用,也就是说它不能再作为read或write的第一个参数,然而TCP将尝试发送已排队等待发送到对端的任何数据,发送完毕后发生的是正常的TCP连接终止序列。

  在多进程并发服务器中,父子进程共享着套接字,套接字描述符引用计数记录着共享着的进程个数,当父进程或某一子进程close掉套接字时,描述符引用计数会相应的减一,当引用计数仍大于零时,这个close调用就不会引发TCP的四路握手断连过程。

 

所以:父进程close(conn_fd)不会对子进程通信造成影响。相反,如果不这么做,在父进程中conn_fd占用着可用的文件描述符,会影响父进程accept链接的个数,因为conn_fd是有用户级限制的(内核为了不让某一个进程消耗掉所有的文件资源,其也会对单个进程最大打开文件数做默认值处理,默认值一般是1024,使用ulimit -n命令可以查看),再accept一个新的连接时,前一个conn_fd被覆盖,就找不到了,但相应的描述符值还是被标记为“占用”,当达到用户级限制时,不可再accept新连接,成为瓶颈。

在Web服务器中,通过更改系统默认值文件描述符的最大值来优化服务器是最常见的方式之一。具体优化方式请查看http://blog.csdn.net/kumu_linux/article/details/7877770 其中介绍的很详细

经本人亲测,如果去除了父进程的close(conn_fd),可以accept的连接数大大减少。而且通过修改系统文件描述符最大值,可以大大提高accept连接数量。

 

初学Linux 对其中很多细节还不清楚,后续会针对修改!

文件描述符讲解很好的博客:http://blog.csdn.net/cywosp/article/details/38965239

 

while(1) {    conn_fd = accept(sock_fd, (struct sockaddr *)&cli_addr, &cli_len);    if (conn_fd < 0) {      my_err("accept", __LINE__);    }    printf("accept a new client, ip:%s\n", inet_ntoa(cli_addr.sin_addr));    if ((pid = fork()) == 0) {      while(1) {        if ((ret = recv(conn_fd, recv_buf, sizeof(recv_buf), 0)) < 0) {          perror("recv");          exit(1);        }        recv_buf[ret-1] = '\0';        if (flag_recv == USERNAME) {          name_num = find_name(recv_buf);          switch(name_num) {            case -1:              send_data(conn_fd, "n\n");              break;            case -2:              exit(1);              break;            default:              send_data(conn_fd, "y\n");              flag_recv = PASSWORD;              break;          }        }        else if (flag_recv == PASSWORD) {          if (strcmp(users[name_num].password, recv_buf) == 0) {            send_data(conn_fd, "y\n");            send_data(conn_fd, "welcome login my tcp server\n");            printf("%s login\n", users[name_num].username);            flag_recv = MESSAGE;          }          else {            send_data(conn_fd, "n\n");          }        }        else if (flag_recv == MESSAGE) {          if (strcmp("quit", recv_buf) == 0) {            break;          }          printf("message from %s: %s\n", users[name_num].username, recv_buf);        }      }      close(sock_fd);      close(conn_fd);      printf("child exit!\n");      exit(0);    }else {       close(conn_fd); }