在做网络服务的时候并发服务端程序的编写必不可少。前端客户端应用程序是否稳定一部分取决于客户端自身,而更多的取决于服务器是否相应时间够迅速,够稳定.

    常见的linux并发服务器模型;


    • 多进程并发服务器

    • 多线程并发服务器

    • select多路I/O转接服务器

    • poll多路I/O转接服务器

    • epool多路I/O转接服务器.



    本次主要讨论多进程并发服务器模型:

    wKioL1dlV3ORUbUGAAISrviO-QM731.png-wh_50


    使用多进程并发服务器时要考虑以下几点:


    • 父进程最大文件描述个数(父进程中需要close关闭accpet返回的新文件描述符)

    • 系统内创建进程个数(与内存大小相关)

    • 进程创建过多是否降低整体服务性能(进程调度)


            


    .server代码[实际开发中要特别注意函数调用返回值判断]

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>

/*
 * 多进程并发服务器
 *	author: sea time 2016/06/18
 */

#define SERV_PORT 9096			//服务器端口号
#define SERV_ADDR "10.10.101.102"	//服务器ip地址

void wait_process(int num){
	//回收子进程
	while(0 < waitpid(0, NULL, WNOHANG)){
		;
	}
}

int main(int argc, char* argv[]){
	pid_t 			pid;		//创建进程使用
	int 			lfd;		//监听套接字
	int 			cfd;		//客户端套接字
	struct sockaddr_in 	serv_addr;	//服务器地址族结构体, 绑定使用
	struct sockaddr_in 	clie_addr;	//客户端地址族结构体,接收连接使用
	socklen_t 		clie_addr_len;	//客户端地址族大小,接收连接使用
	char			buf[BUFSIZ];
	int n, i;

	//注册进程结束信号,回收结束的子进程
	signal(SIGCHLD, wait_process);

	//创建监听套接字
	//AF_INET:	指定ipv4
	//SOCK_STREAM:	指定tcp
	//0:		函数会根据指定的类型来自动指定协议
	lfd = socket(AF_INET, SOCK_STREAM, 0);
	
	//全部初始化为0
	bzero(&serv_addr, sizeof(serv_addr));
	//指定族:ipv4
	serv_addr.sin_family = AF_INET;	
	//指定ip地址并转换为网络字节序
	inet_pton(AF_INET, SERV_ADDR, &serv_addr.sin_addr.s_addr);
	//指定端口号并转换为网络字节序
	serv_addr.sin_port = htons(SERV_PORT);
	//绑定到监听套接字
	bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

	//指定监听套接字并设置同时连接上限
	//SOMAXCONN: 系统默认为128
	listen(lfd, SOMAXCONN);

	//循环获取连接
	while(1){
		clie_addr_len = sizeof(clie_addr);
		//阻塞获取连接
		cfd = accept(lfd, (struct sockaddr*)&clie_addr, &clie_addr_len);
		//打屏提示连接成功
		printf("%s:%d connected successfully!\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, buf, sizeof(buf)), ntohs(clie_addr.sin_port));
		//创建进程
		pid = fork();
		//子进程
		if(0 == pid){	
			//子进程关闭监听套接字
			close(lfd);
			break;
		}
		//父进程关闭连接套接字
		close(cfd);
	}

	//子进程与客户端进程通信 
	while(0 == pid){
		bzero(buf, sizeof(buf));
		//读取客户端发来的消息 
		n = read(cfd, buf, sizeof(buf));
		//客户端关闭的情况下
		if(0 == n){
			close(cfd);
			printf("%s:%d disconnect successfully!\n", inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, buf, sizeof(buf)), ntohs(clie_addr.sin_port));
			exit(1);
		}
		//转换为大小
		for(i = 0; i < n; i++){
			buf[i] = toupper(buf[i]);
		}
		//回写给客户端
		write(cfd, buf, n);
	}

	return 0;
}

.client

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

/*
 * socket客户端连接服务器
 */

#define SERV_PORT 9096			//服务器port
#define SERV_ADDR "10.10.101.102"	//服务器ip

int main(int argc, char* agrv[]){
	int sfd;	
	struct sockaddr_in serv_addr;
	char buf[BUFSIZ];
	int n;

	//创建网络套接字
	//AF_INET:	指定ipv4
	//SOCK_STREAM:	指定tcp
	//0:		函数会根据指定的类型来自动指定协议
	sfd = socket(AF_INET, SOCK_STREAM, 0);

	//初始化
	bzero(&serv_addr, sizeof(serv_addr));
	//指定族
	serv_addr.sin_family = AF_INET;
	//指定ip地址并转换为网络字节序
	inet_pton(AF_INET, SERV_ADDR, &serv_addr.sin_addr.s_addr);
	//指定端口并转换为网络字节序
	serv_addr.sin_port = htons(SERV_PORT);
	//连接服务器
	connect(sfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	
	//读取键盘输入
	while(fgets(buf, sizeof(buf), stdin) != NULL){
		//写服务器发送数据
		write(sfd, buf, strlen(buf));
		bzero(buf, sizeof(buf));
		//读取服务器发来数据
		n = read(sfd, buf, sizeof(buf));
		//服务器关闭
		if(0 == n){
			close(sfd);
			break;
		}
		//向屏幕输出
		write(STDOUT_FILENO, buf, n);
		bzero(buf, sizeof(buf));
	}
}