赞
踩
在linux c/c++编程时,父进程fork出子进程后,子进程经常第一件事就是close掉所有的文件描述符;为何需要这样做,本文用一个例子进行简单说明。
考虑到一种情况,父进程创建了tcp服务端套接字,并且listen,此时fork出子进程,若子进程里面不close此监听套接字,则子进程里面是否能接收到客户端的连接。
因为一旦子进程里面也能够接收到客户端的连接,无疑会造成混乱。
答案是可以的,现在构造一个服务端程序,listen后,fork出子进程,然后父进程进入sleep,子进程调用accept,在accept成功后,打印对应的信息。
服务端代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #define PORT 8888 //侦听端口地址:8888 #define BACKLOG 20 //侦听队列长度:2 extern void process_conn_server(int s); //服务器对客户端的处理:读取数据并发送响应字符 int main(int argc,char *argv[]) { int ss = 0; //ss = server socket = 服务器socket描述符 int cs = 0; //cs = client socket = 客户端socket描述符 struct sockaddr_in server_addr; //服务器地址结构 struct sockaddr_in client_addr; //客户端地址结构 int ret = 0; //返回值 pid_t pid; //进程ID char buffer[1024]; ssize_t size = 0; /** * Step 2 : 建立套接字 */ ss = socket(AF_INET,SOCK_STREAM,0); //创建一个AF_INET族的流类型socket if(ss < 0) //检查是否正常创建socket { perror("socket error\n"); exit(EXIT_FAILURE); } /** * Step 3 : 设置服务器地址 * Note: * htonl():将主机数转换成无符号长整型的网络字节顺序 * htons():将整型变量从主机字节顺序转变成网络字节顺序 */ bzero(&server_addr,sizeof(server_addr)); //清零 server_addr.sin_family = AF_INET; //设置地址族为AF_INET server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址 server_addr.sin_port = htons(PORT); //设置端口号 /** * Step 4 : 绑定地址结构到套接字描述符 */ ret = bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr)); if (ret < 0) //出错 { perror("bind error\n"); exit(EXIT_FAILURE); } /** * Step 5 : 设置侦听,侦听队列长度为2,可同时处理两个客户端连接请求 */ ret = listen(ss,BACKLOG); if (ret < 0) //出错 { perror("bind error\n"); exit(EXIT_FAILURE); } /** * Step 6 : 主循环过程 */ int pid2 = fork(); if(pid2 > 0) { sleep (60000); } else for(;;) { /* 接收客户端连接 */ int addrlen = sizeof(struct sockaddr); cs = accept(ss,(struct sockaddr*)&client_addr,&addrlen); if(cs < 0) //出错 { continue; //结束本次循环 } printf("accept one client in child process\n"); //sleep(30); /* for(;;) { size = read(cs,buffer,1024); //从套接字中读取数据放到缓冲区buffer中 if(size == 0) //没有数据 { return; } printf("buffer is %s\n", buffer); } */ }
客户端代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #define PORT 8888 //端口地址:8888 #define SERVER_IP "10.0.0.64" extern void process_conn_client(int s); int main(int argc,char *argv[]) { int s = 0; //socket描述符 struct sockaddr_in server_addr; //服务器地址结构 int ret = 0; //返回值 char buf[1024] = {0}; int i = 0; strcpy(buf, "hello world"); /** * Step 2 : 建立套接字 */ s = socket(AF_INET,SOCK_STREAM,0); //创建一个AF_INET族的流类型socket if(s < 0) //检查是否正常创建socket { perror("socket error\n"); exit(EXIT_FAILURE); } /** * Step 3 : 设置服务器地址 */ memset(&server_addr, 0, sizeof(server_addr)); //清零 server_addr.sin_family = AF_INET; //设置地址族为AF_INET inet_pton(AF_INET, SERVER_IP, &(server_addr.sin_addr)); server_addr.sin_port = htons(PORT); //设置端口号 /** * Step 4 : 将用户输入的字符串类型的IP地址转为整型 */ //inet_pton(AF_INET,argv[1],&server_addr.sin_addr); /** * Step 5 : 连接服务器 */ ret = connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr)); if(ret < 0) { perror("connect error\n"); printf("errno is %d\n", errno); exit(EXIT_FAILURE); } printf("connect succeed\n"); //for(;;) { //sleep(10000000); sprintf(buf, "hello world, %d", i); i++; ret = write(s,buf,strlen(buf)+1); if(ret < 0) { printf("write failed, errno is %d\n", errno); } ret = read(s, buf, strlen(buf)+1); if(ret < 0) { printf("read failed, errno is %d\n", errno); } } while(1)sleep(10); close(s); //关闭连接 }
启动服务端(ip:10.0.0.64),然后客户端(10.0.0.60)连接,结果打印的信息如下:
可以看到监听套接字所在的进程20811是父进程,建立连接的套接字位于子进程20812。
所以后续父进程fork出子进程后,子进程需要关闭对应的文件描述符。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。