当父进程产生新的子进程后,父,子进程共享父进程在调用fork之前的所有描述符,一般情况下,接下来父进程只负责
接收客户请求,而子进程只负责处理客户请求。关闭不需要的描述符既可以节省系统资源,又可以防止父,子进程同时对共享描
述符进程操作,产生不可预计的后果。
此外,由于当fork()函数返回后,与监听和已连接描述符相关联的文件表项的访问计数值均加一,当父进程调用close关
闭已连接描述符时,只是将访问计数值减1,而描述符只在访问计数为0时才真正关闭,所以为了正确地关闭链接,当调用fork()函数
后父进程将不需要的已连接描述符(accept函数返回的描述符)关闭,而子进程关闭不需要的监听描述符(socket函数返回的描述符)。
多进程并发服务器实例:
服务器端的程序步骤如下:
/*
* =====================================================================================
*
* Filename: server.c
*
* Description: 多进程并发服务器的server端程序
*
* Version: 1.0
* Created: 2014年07月17日 09时45分30秒
* Revision: none
* Compiler: gcc
* CopyRight: open , free , share
* Author: yexingkong(zhangbaoqing)
* Email: abqyexingkong@gmail.com
* Company: Xi'an University of post and Telecommunications
*
* =====================================================================================
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERV_PORT 4507 //服务端的端口
#define LISTENQ 12 //链接请求队列的最大长度
void my_err(const char *err_string, int err_line)
{
fprintf(stderr,"line: %d",err_line);
perror(err_string);
exit(1);
}
int main()
{
int sock_fd,conn_fd;
int optval;
int ret;
int name_num;
pid_t pid;
socklen_t cli_len;
struct sockaddr_in cli_addr,serv_addr;
//创建一个TCPT套接字
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
{
if(sock_fd < 0)
{
my_err("socket", __LINE__);
}
}
//设置该套接字使之可以重新绑定端口
optval = 1;
if(setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)) < 0)
{
my_err("setsockopt", __LINE__);
}
//初始化服务端地址结构
memset(&serv_addr, 0 , sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htons(INADDR_ANY);
//将套接字绑定到本地端口
if(bind(sock_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)) < 0)
{
my_err("bind", __LINE__);
}
//将套接字转化为监听套接字
if(listen(sock_fd, LISTENQ))
{
my_err("listen", __LINE__);
}
cli_len = sizeof(struct sockaddr_in);
while(1)
{
//通过accept接收客户端的链接请求,并返回链接套接字
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)
{
close(sock_fd); //子进程关闭监听描述符
//child handle with something
//...............
exit(0); //结束子进程
}else if(pid > 0) //父进程关闭刚刚接收的链接请求,执行accept等待其他链接请求
{
close(conn_fd); //父进程关闭已连接描述符
continue;
}
else {
printf("fork() error\n");
exit(1);
}
}
close(sock_fd);
return 0;
}
客户端程序如下 :
/*
* =====================================================================================
*
* Filename: client.c
*
* Description: 多进程并发服务器的client端程序
*
* Version: 1.0
* Created: 2014年07月17日 10时18分16秒
* Revision: none
* Compiler: gcc
* CopyRight: open , free , share
* Author: yexingkong(zhangbaoqing)
* Email: abqyexingkong@gmail.com
* Company: Xi'an University of post and Telecommunications
*
* =====================================================================================
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<errno.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#define SERV_PORT 4507 //服务器端的端口
void my_err(const char * err_string, int err_line)
{
fprintf(stderr,"%d",err_line);
perror(err_string);
exit(1);
}
int main(int argc, char **argv)
{
int i;
int ret;
int sock_fd;
struct sockaddr_in serv_addr;
struct hostent *he;
//检查参数的个数
if (argc != 2)
{
printf("Usage: %s [serv_address]\n",argv[0]);
exit(1);
}
if ((he=gethostbyname(argv[1])) == NULL)
{
printf("gethostbyname() error\n");
exit(1);
}
//创建一个TCP套接字
sock_fd = socket (AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
my_err("socket", __LINE__);
}
//初始化服务器端的端口与地址
memset(&serv_addr, 0, sizeof (struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr = *((struct in_addr *)he->h_addr);
//向服务器端发送链接请求
if (connect(sock_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0)
{
my_err("connect", __LINE__);
}
//函数调用
//................
close(sock_fd);
return 0;
}