Linux socke server编程:父进程和子进程关系
测试过程中碰到的需要记录的地方
1、父进程accept创建新socket后(比如socket为connect_fd),需要在父进程中关闭该connect_fd(即close(connect_fd)
)(父进程负责accept等待创建连接,每次产生新的连接交给子进程处理的情况下)
因为
:子进程是父进程的完全拷贝,所以只有父子两个进程都关掉connect_fd,这个connect_fd才算关掉;如果父进程这里没有close(connect_fd),那就算子进程close(connect_fd),实际socket connect_fd并没有被关闭; 所以为了子进程完全控制connect_fd,父进程创建connect_fd后,直接close(connect_fd);
完整代码
下面展示 TCP server端代码
:
/* File Name: server.c */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<unistd.h>/*#包含<unistd.h> fork()等程序会用到*/
#include<arpa/inet.h>/**/
#include<sys/socket.h>
#include<netinet/in.h>
#define DEFAULT_PORT 40010
#define MAXLINE 4096
int main(int argc, char** argv)
{
int socket_fd, connect_fd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
//初始化Socket
if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//初始化
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
// printf("servaddr.sin_addr_cxf == %s\n",inet_ntoa((struct in_addr) servaddr.sin_addr));
servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT
//将本地地址绑定到所创建的套接字上
if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
//开始监听是否有客户端连接
if( listen(socket_fd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
printf("main PID == %d\n",getpid());
while(1){
//阻塞直到有客户端连接,不然多浪费CPU资源。
if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
if(!fork())
{
printf("son PID == %d\n",getpid());
printf("connect_fd == %d\n",connect_fd);
if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)
perror("send error");
while(1)
{
//接受客户端传过来的数据
if((n = recv(connect_fd, buff, MAXLINE, 0))== 0) //这里要判断recv接收到的是否为0,不然client主动断开后,会导致一直打印"recv msg from client: %s,PID:%d \n"
{
printf("closed from client,connect_fd:%d\n",connect_fd);
close(connect_fd);
exit(0);
}
buff[n] = '\0';
printf("connect_fd:%d \n", connect_fd);
printf("recv msg from client: %s,PID:%d \n", buff,getpid());
}
}
close(connect_fd); //这里是父进程的connect_fd,需要着重讲下:父子进程都有这个connect_fd标识符,只有父子两个进程都关掉connect_fd,这个connect_fd才算关掉;如果父进程这里没有close(connect_fd),那就算子进程close(connect_fd),实际socket connect_fd并没有被关闭; 所以为了子进程完全控制connect_fd,父进程创建connect_fd后,直接close(connect_fd)
}
close(socket_fd);
}
注意点
1、fork()之后,创建新进程(即子进程),通过sudo netstat -antup
可以查看原进程和新进程;
2、可以通过getpid()
获取本进程的进程ID(即PID);
3、如果子进程结束,但是子进程的某些资源没关掉的话,会导致进程被挂到父进程下面
比如子进程中没有close(connect_fd),但是子进程调用了exit(0)关闭自己的线程,然后从sudo netstat -antup来看,子线程还在,只不过PID变成了父进程的PID