网络编程:服务器模型-并发服务器-多进程

并发服务器概念:

并发服务器同一时刻可以处理多个客户机的请求

设计思路:

并发服务器是在循环服务器基础上优化过来的

(1)每连接一个客户机,服务器立马创建子进程或者子线程来跟新的客户机通信 (accept之后的),服务器不会与客户端进行通信!!!

(2)IO多路复用技术

1、多进程实现并发服务器

思想:

主进程专门用于连接多个客户端的请求,若有一路客户端连接进来,主进程就创建一个子进程,用该子进程来处理该客户端的业务数据。

回顾:创建进程

 #include <sys/types.h>

 #include <unistd.h>

    pid_t fork(void);
    功能:创建一个子进程
    参数:无
    返回值:pid_t就是int类型的别名
        返回值大于0,代表此时是父进程,该值的含义为创建成功的子进程的ID号
        返回值等于0,代表此时是子进程
        返回值小于0,创建失败可以perror

源代码:

tcp_server.c 

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>//sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>   // 包含 inet_addr 函数的声明 
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 20

int main(int argc, const char *argv[])
{
	//1.socket
	int iServer = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == iServer){
		puts("----------1、create socket error!");
		return -1;
	}
	printf("----------1、create socket ok! iServer:%d", iServer);//0,1,2标准输入输出出错,iServer为3

	//2.bind
	struct sockaddr_in stServer;
	stServer.sin_family = AF_INET;//第一个成员
	stServer.sin_port = htons(8888);//第二个成员
	stServer.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分十进制ip地址转换为32位无符号整数
	int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr));
	if(-1 == ret){
		puts("----------2、bind error!");
		return -1;
	}
	puts("----------2、bind ok!");

	//3.listen
	ret = listen(iServer, 5);
	if(-1 == ret){
		puts("----------3、listen error!");
		return -1;
	}
	puts("----------3、listen ok!");

	//4.accept
	struct sockaddr_in stClient;//存放对方的主机信息
	socklen_t len = sizeof(struct sockaddr_in);
	char buf[BUF_SIZE] = {0};
	fd_set stFdr;//文件描述符集合表,大小1024
	FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零
	while(1){
		FD_SET(iServer, &stFdr);
		FD_SET(0, &stFdr);
		//select
		ret = select(iServer + 1, &stFdr, NULL, NULL, NULL);

		if(ret <= 0){
			continue;
		}

		printf("select ok, ret = %d\r\n", ret);

		//FD_ISSET
		if(FD_ISSET(0, &stFdr)){
			memset(buf, 0, BUF_SIZE);
			fgets(buf, BUF_SIZE, stdin);
			printf("fgets ok, data = %s\r\n", buf);
		}

		if(FD_ISSET(iServer, &stFdr)){
			int iClient = accept(iServer, (struct sockaddr *)&stClient, &len);
			if(-1 == iClient){
				continue;//当前客户端出错转向下一个客户端	
			}
			printf("----------4、accept ok! iClient = %d\r\n",iClient );//标准输入输出出错,所以下一个打开的文件一定是3
			//5.recv/send
			ret = recv(iClient, buf, BUF_SIZE, 0);
			if(ret <= 0){
				close(iClient);
				continue;
			}
			printf("----------recv data ok! buf = %s\r\n",buf);
			//send
			ret = send(iClient, buf, BUF_SIZE, 0);
			if(ret <= 0){
				close(iClient);
				continue;
			}
			printf("----------send data ok! %s\r\n",buf);
			//close(iClient);//断开当前客户端

		}


	}

	return 0;
}


tcp_client.c

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




#define BUF_SIZE 20
//main函数参数,如果需要键入IP则给定即可
int main(int argc, const char *argv[])
{
	//1、socket
	int iClient = 	socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == iClient){
		puts("----------1、create socket error!");
		return -1;
	}
	puts("----------1、create socket ok!");

	//2、connect
	struct sockaddr_in stServer;
	stServer.sin_family = AF_INET;
	stServer.sin_port = htons(8888);
	//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");
	stServer.sin_addr.s_addr = inet_addr("127.0.0.1");
	int ret = connect(iClient, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
	if(-1 == ret){
		puts("----------2、connect error!");
		return -1;
	}
	puts("----------2、connect ok!");

	char buf[BUF_SIZE] = {0};
	while(1){
		//gets();
		//char *fgets(char *s, int size, FILE *stream);
		fgets(buf, BUF_SIZE, stdin);//更安全,边界检查

		//3、send recv
		ret = send(iClient, buf, BUF_SIZE, 0);
		if(-1 == ret){
			puts("----------3、send data error!");
		}
		printf("----------3、send data ok! buf = %s\r\n",buf);

		//recv
		//函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);
		memset(buf, 0, BUF_SIZE);
		ret = recv(iClient, buf, BUF_SIZE, 0);
		if(-1 == ret){
			puts("----------4、recv error!");
			return -1;
		}
		printf("----------4、recv data ok! buf = %s\r\n",buf);
	}
	close(iClient);

	return 0;
}

思考:

多进程并发服务器的缺点:每连接一个客户端,就为其创建子进程,客户端数量比较大时,服务器的运 行效率就会变低。

注:

以上代码只能实现:

        ①客户端连接到服务器端,只能发送一条数据,之后发送不成功

        ②服务器端可以检测标准输入给自己

测试结果如下图:

2、多进程实现并发服务器-优化版本

tcp_server.c 

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>//sockaddr_in
#include <unistd.h>
#include <arpa/inet.h>   // 包含 inet_addr 函数的声明 
#include <sys/select.h>
#include <sys/time.h>
#define BUF_SIZE 20

int main(int argc, const char *argv[])
{
	//1.socket
	int iServer = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == iServer){
		puts("----------1、create socket error!");
		return -1;
	}
	printf("----------1、create socket ok! iServer:%d", iServer);//0,1,2标准输入输出出错,iServer为3

	//2.bind
	struct sockaddr_in stServer;
	stServer.sin_family = AF_INET;//第一个成员
	stServer.sin_port = htons(9999);//第二个成员
	stServer.sin_addr.s_addr = inet_addr("127.0.0.1");//将点分十进制ip地址转换为32位无符号整数
	int ret = bind(iServer, (struct sockaddr *)&stServer, sizeof(struct sockaddr));
	if(-1 == ret){
		puts("----------2、bind error!");
		return -1;
	}
	puts("----------2、bind ok!");

	//3.listen
	ret = listen(iServer, 5);
	if(-1 == ret){
		puts("----------3、listen error!");
		return -1;
	}
	puts("----------3、listen ok!");

	//4.accept
	struct sockaddr_in stClient;//存放对方的主机信息
	socklen_t len = sizeof(struct sockaddr_in);
	char buf[BUF_SIZE] = {0};
	fd_set stFdr;//文件描述符集合表,大小1024
	FD_ZERO(&stFdr);//将文件描述符集合表中所有内容清零
	FD_SET(iServer, &stFdr);//iServer添加到原文件描述符集合表中
	int max = iServer;
	while(1)
	{
		//select
		fd_set stFdrTmp = stFdr;	//定义临时文件描述符集合表
		ret = select(max + 1, &stFdrTmp, NULL, NULL, NULL);

		if(ret <= 0)
		{
			printf("select error!\r\n");
			continue;
		}

		printf("select ok, ret = %d\r\n", ret);

		int i = 0;
		for(i = 0; i < max + 1; i++)
		{
			if(FD_ISSET(i, &stFdrTmp))
			{	 // 循环判断哪个文件描述符被置位
				//操作
				if(i == iServer)
				{ 
					// i == 3, 操作
					int iClient = accept(iServer, (struct sockaddr *)&stClient, &len);
					if(-1 == iClient)
					{
						continue;//当前客户端出错转向下一个客户端	
					}
					printf("----------4、accept ok! iClient = %d\r\n",iClient );//标准输入输出出错,所以下一个打开的文件一定是4
					FD_SET(iClient, &stFdr);
					//更新max
					if(max < iClient)
					{
						max = iClient;
					}
				}
				else
				{ // 与多个客户端保持连接
					//recv/send
					ret = recv(i, buf, BUF_SIZE, 0);

					if(ret > 0)
					{
						printf("recv:%s\r\n", buf);
						send(i, buf, BUF_SIZE, 0);
					}
					else
					{
						close(i);
						FD_CLR(i, &stFdr);
					}

				}
			}

		}
	}
	return 0;
}

tcp_client.c

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




#define BUF_SIZE 20
//main函数参数,如果需要键入IP则给定即可
int main(int argc, const char *argv[])
{
	//1、socket
	int iClient = 	socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == iClient){
		puts("----------1、create socket error!");
		return -1;
	}
	puts("----------1、create socket ok!");

	//2、connect
	struct sockaddr_in stServer;
	stServer.sin_family = AF_INET;
	stServer.sin_port = htons(9999);
	//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");
	//stServer.sin_addr.s_addr = inet_addr("192.168.15.71");
	stServer.sin_addr.s_addr = inet_addr("127.0.0.1");
	int ret = connect(iClient, (struct sockaddr *)&stServer, sizeof(struct sockaddr_in));
	if(-1 == ret){
		puts("----------2、connect error!");
		return -1;
	}
	puts("----------2、connect ok!");

	char buf[BUF_SIZE] = {0};
	while(1){
		//gets();
		//char *fgets(char *s, int size, FILE *stream);
		fgets(buf, BUF_SIZE, stdin);//更安全,边界检查

		//3、send recv
		ret = send(iClient, buf, BUF_SIZE, 0);
		if(-1 == ret){
			puts("----------3、send data error!");
		}
		printf("----------3、send data ok! buf = %s\r\n",buf);

		//recv
		//函数原型:ssize_t recv(int sockfd, void *buf, size_t len, int flags);
		memset(buf, 0, BUF_SIZE);
		ret = recv(iClient, buf, BUF_SIZE, 0);
		if(-1 == ret){
			puts("----------4、recv error!");
			return -1;
		}
		printf("----------4、recv data ok! buf = %s\r\n",buf);
	}
	close(iClient);

	return 0;
}

注:

以上代码可以实现:

        ①多个客户端与服务器连接 并 发送&回显数据

测试结果如下图:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值