浅谈并发服务器---多进程并发---3

            

                   当父进程产生新的子进程后,父,子进程共享父进程在调用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;
  }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值