Linux之并发进程服务器

本文介绍了服务器按处理方式分为迭代服务器和并发服务器,并详细讲解了Linux系统下通过进程创建、终止等方式实现并发服务器的方法,包括fork函数的具体使用及示例。

服务器按处理方式可以分为迭代服务器和并发服务器两类。迭代服务器每次只能处理一个客户的请求,他实现简单但效率很低,通常这种服务器被称为迭代服务器

然而在实际应用中,不可能让一个服务器长时间地为一个客户服务,而需要器具有同时处理多个客户请求的能力,这种同时可以处理多个客户请求的服务器称为并发服务器,其效率很高却实现复杂

Linux系统主要提供3种方式支持并发: 进程,线程 以及 I/O多路复用。

1: 进程创建

#include <unistd.h>

pid_t  fork(void);

函数调用失败返回 -1 ,fork函数调用失败主要有两原因 :系统中已经有太多的进程和改实际用户的ID进程超过系统的限制

函数会返回两次 , 子进程ID和父进程ID。

fork调用后,父进程和子进程继续执行fork函数后的指令,是父进程还是子进程先执行是不确定的,这取决于系统内核所使用的调度算法。

fork有两个典型的应用:1 夫,子进程各自执行不同的程序段,这是非常典型的网络服务器。父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork函数产生子进程,又子进程对该请求进行处理,父进程则继续等待下一个客户的服务请求。在这种情况下,父,子进程需要关闭各自不使用描述符。

2  每个进程要执行不同的程序。在这种情况下,子进程在从fork函数发回后立即调用exec函数执行其他程序。

pid_t vfork(void);

和fork函数一样,不同vfork是完全共享创建,新老进程共享同样的资源,完全没有拷贝。当使用vfork函数创建新进程时,父进程将被暂时阻塞,而子进程则可以借用父进程的地址空间运行。这个奇特的状态持续到子进程推出或调用execve函数,至此父进程菜继续执行

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
	pid_t child_pid;
	int i=1;
	printf("the main program process ID is %d\n",(int)getpid() );
	child_pid = fork();
	if( 0 != child_pid  )
	{
		i= 0;
		printf("this is the parent process , with id %d and i= %d \n",(int)getpid(),i );
		printf("the child's process ID is %d\n",(int)child_pid );	
	}
	
	else
		printf("this is the child process , with ID= %d and i = %d\n",(int)getpid(), i);
	return 1;
}
2: 进程终止

#include<sys/wait.h>

pid_t wait(int *staloc);

wait()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用wait()时子进程已经结束,则wait()会立即返回子进程结束状态值。子进程的结束状态值会由参数status 返回,而子进程的进程识别码也会一起返回。如果不在意结束状态值,则参数status 可以设成NULL。子进程的结束状态值请参考下面的waitpid()。

#include <sys/wait.h>

  #include <sys/type.h>

 pid_t waitpid( pid_t pid, int *statloc,int option )

waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程

  结束。如果在调用 waitpid()时子进程已经结束,则 waitpid()会立即
  返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回,
  而子进程的进程识别码也会一起返回。如果不在意结束状态值,则
  参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码,
  其他数值意义如下:
  pid<-1 等待 进程组 识别码为 pid  绝对值 的任何子进程。
  pid=-1 等待任何子进程,相当于 wait()。
  pid=0 等待进程组识别码与目前进程相同的任何子进程。
  pid>0 等待任何子进程识别码为 pid 的子进程。

参数option让用户指定附加选项。最常用的选项是WHO_HANG,他通知内核在没有已终止子进程时不要阻塞。

 #include<stdlib.h>
  #include<unistd.h>
  #include<sys/types.h>
  #include<sys/wait.h>
  main()
  {
	  pid_t pid;
	  int status,i;
	  if(fork()= =0){
		  printf(“This is the child process .pid =%d\n”,getpid());
		  exit(5);
	  }else{
	  	   	sleep(1);
	           	printf(“This is the parent process ,wait for child...\n”);
		  	pid=wait(&status);
		  	i=WEXITSTATUS(status);
		  	printf(“child’s pid =%d .exit status=%d\n”,pid,i);
	  }
  }
下面是用多进程实现的服务器端实例:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000

void process(int connfd, struct sockaddr_in client);
int main()
{
	int listenfd,connfd;
	pid_t pid;
	struct sockaddr_in server;
	struct sockaddr_in client;
	if( (listenfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1 )
	{
		printf("socket fail \n ");
		_exit(1);
	}

	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,(const void *)SO_REUSEADDR,sizeof(SO_REUSEADDR) );
	bzero( (void *)&server,sizeof(server) );

	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htonl(INADDR_ANY);           //服务器可能有许多网卡,但是INADDR_ANY 设置默认网卡IP  也可设为172.16.1.166(本人pc机上的固定IP)

	if( bind( listenfd,(struct sockaddr *)&server ,sizeof(server) ) == -1 )
	{
		printf(" bind fail \n");
		_exit(1);
	}

	if( listen(listenfd, BACKLOG) < 0)
	{
		printf("listen fail  \n");
		_exit(1);
	}

	//-------------开始监听客户端-----------------------------------------------
	while(1)
	{
		//int len = sizeof(client);
		if( (connfd = accept(listenfd, (struct sockaddr *)&client, &(int){sizeof client}) ) == -1 )   //如果接受成功,那么client结构体将会存储远程主机的各种信息
		{
			printf("accept fail \n");
			_exit(1);
		}

		if( (pid = fork() ) < 0 )
		{
			printf("fork fail \n");
			_exit(1);
		}
		else if(pid == 0){
				close(connfd);      //必须关闭connfd,connfd再次用来接受下一个远程主机的信息
				process(connfd,client);       //子进程对请求进程处理,在process函数中处理,之前必须关闭套接字
			}
			else{
					close(connfd);    //如果是父进程 这关闭套接字。跳出if循环,继续监听下次请求
					continue;
				}
	}
}

void process(int connfd, struct sockaddr_in client)
{
	int num;
	char cli_name[MAXDATASIZE];
	char databuf[MAXDATASIZE];
	printf("get data from : %s", inet_ntoa( client.sin_addr) );
	
	if((num = recv(connfd, cli_name, MAXDATASIZE, 0) ) == 0 )
	{
		printf("client disconnected \n");
		return ;
	}
	cli_name[num-1] = '\0';
	printf("client name is %s \n", cli_name);

	while( num = recv(connfd, databuf, MAXDATASIZE, 0) )
	{
		databuf[num-1] = '\0';
		printf("get message from client : %s \n",databuf);
		send(connfd, databuf, strlen(databuf), 0);
	}
	return ;
	
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值