学习C高级(二十六)


服务端管理连接请求:

  1. 通过两个链式队列来管理连接请求
    a. 已完成的连接请求队列
    accept在该队列为空时阻塞,不空则取队头的已完成的连接
    b. 未完成的连接请求队列
    协助实现三路握手的过程
  2. listen函数的第二参数决定了已完成的连接请求队列的最大长度
    如果大于/proc/sys/net/core/somaxconn 则为somaxconn
  3. /proc/sys/net/ipv4/tcp_max_syn_backlog来决定未完成的连接请求队列的最大长度

  设计多进程版服务器和多线程版服务器:客户端接收用户输入的整数n,并产生n个随机数(1000以内)发送给服务器,服务器求出n个随机数的平均值发送回给客户端。

多进程版服务器

运行结果:
  先打开服务器。在这里插入图片描述
  打开一个客户端,服务器与客户端连接后,客户端输入整数count,并产生n个随机数(1000以内)发送给服务器,服务器求出count个随机数的平均值发送回给客户端。
在这里插入图片描述
  再打开另外一个客户端,此时服务器也可以为这个客户端提供服务。 在这里插入图片描述
  刚才打开了一个服务器和两个客户端,现在查看进程,可以看到为客户端提供服务的其实是孙子进程。这个能生成多个孙子进程为多个客户端提供服务的就是多进程版服务器。在这里插入图片描述

多线程版服务器

  把上面的多进程版服务器改为多线程版服务器。需要改动的只有server.c中的main()和HandleClient()两个函数,再在头文件加上#include<pthread.h>。
  运行结果:
  先打开服务器。打开一个客户端,再打开另外一个客户端,此时服务器也可以为这个客户端提供服务。现在查看服务器线程,可以看到为客户端提供服务的其实是服务器的线程。这个能生成多个线程为多个客户端提供服务的就是多线程版服务器。
  在这里插入图片描述
  与多进程方案的比较:

  1. 线程间通讯更加方便
  2. 线程效率更高
  3. 但会受描述符数量的限制

多进程版服务器和多线程版服务器代码

(randpdu.h, server.c服务器, client.c客户端):

//randpdu.h
#ifndef RAND_PDU_H
#define RAND_PDU_H

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>//多线程版要用到的头文件
#include <time.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct RandPDU
{
	int count;
	char buf[1];
};

#endif
//server.c
#include "randpdu.h"

int CreateServerSocket(char *ip,unsigned short port);
struct RandPDU *ReceiveRandPDU(int datafd);
float GetAve(int *pi,int count);

/***************多进程版*************/
/*
int HandleClient(int datafd);
int main(int argc,char *argv[])
{
	int port = 0;
	int fd = -1;
	int datafd = -1;
	pid_t spid;
	pid_t gpid;
	
	if(argc < 3)
	{
		printf("argument too few\n");
		return 1;
	}
	sscanf(argv[2],"%d",&port);

	if(port <= 0 || port > 65535)
	{
		printf("port is invalid\n");
		return 2;
	}

	fd = CreateServerSocket(argv[1],(unsigned short)port);
	if(fd < 0)
	{
		return 3;
	}

	while(1)
	{
		datafd = accept(fd,NULL,NULL);
		if(datafd < 0)	
		{
			if(errno == EINTR)
			{
				continue;
			}
			else
			{
				perror("accept failed");
				break;
			}
		}
		spid = fork();
		if(spid < 0)
		{
			perror("fork son-process failed");
			close(datafd);
			datafd = -1;
			continue;
		}	
		if(spid == 0)
		{//son-process
			close(fd);//子进程和孙子进程无需使用处理连接用的描述符
			fd = -1;
			gpid = fork();
			if(gpid < 0)
			{
				perror("fork grandson-process error");
				close(datafd);
				datafd = -1;
				return 1;	
			}
			if(gpid == 0)
			{//孙子进程为对应客户端提供服务	
				HandleClient(datafd);
			}
			else
			{
				close(datafd);
				datafd = -1;
			}
			return 0;
		}
		else
		{//father-process:只做监控客户端连接的工作,不做数据交互
			close(datafd);
			datafd = -1;
			wait(NULL);
		}
	}

	close(fd);
	fd = -1;
	return 0;
}

int HandleClient(int datafd)
{
	struct RandPDU *pstPDU = NULL;
	float ave = 0.0f;
	int ret = 0;

	while(1)
	{
		pstPDU = ReceiveRandPDU(datafd);
		if(NULL == pstPDU)
		{
			break;
		}
		ave = GetAve((int *)pstPDU->buf,pstPDU->count);
		free(pstPDU);
		pstPDU = NULL;
		ret = write(datafd,&ave,sizeof(ave));
		if(ret != sizeof(ave))
		{
			perror("Send Ave Error");
			break;
		}
	}

	close(datafd);
	datafd = -1;
	return 0;
}
*/
/*********************************/


/***********多线程版***************/
/*
void* HandleClient(void* datafd);//线程的入口函数
int main(int argc,char *argv[])
{
	int port = 0;
	int fd = -1;
	int datafd = -1;
    pthread_t tid;//存放新线程ID
    int ret = 0;
	
	if(argc < 3)
	{
		printf("argument too few\n");
		return 1;
	}
	sscanf(argv[2],"%d",&port);

	if(port <= 0 || port > 65535)
	{
		printf("port is invalid\n");
		return 2;
	}


	fd = CreateServerSocket(argv[1],(unsigned short)port);
	if(fd < 0)
	{
		return 3;
	}

	while(1)
	{
		datafd = accept(fd,NULL,NULL);//系统执行入口函数时用的实际参数
		if(datafd < 0)	
		{
			if(errno == EINTR)
			{
				continue;
			}
			else
			{
				perror("accept failed");
				break;
			}
		}
        ret = pthread_create(&tid,NULL,HandleClient,(void*)(long)datafd);//创建新线程
        if(ret != 0)
        {
            perror("pthread_create failed");
            break;
        }
	}

	close(fd);
	fd = -1;
	return 0;
}

void* HandleClient(void* fd)
{
	struct RandPDU *pstPDU = NULL;
	float ave = 0.0f;
	int ret = 0;
    int datafd = (int)(long)fd;
    //线程自己将自己设置成分离的
    pthread_detach(pthread_self());//线程自己将自己设置成分离的

	while(1)
	{
		pstPDU = ReceiveRandPDU(datafd);
		if(NULL == pstPDU)
		{
			break;
		}
		ave = GetAve((int *)pstPDU->buf,pstPDU->count);
		free(pstPDU);
		pstPDU = NULL;
		ret = write(datafd,&ave,sizeof(ave));
		if(ret != sizeof(ave))
		{
			perror("Send Ave Error");
			break;
		}
	}

	close(datafd);
	datafd = -1;
	return NULL;
}
*/
/**************************/

float GetAve(int *pi,int count)
{
	float sum = 0.0;
	int i = 0;

	for(i = 0;i < count;i++)
	{
		sum += *(pi + i);
	}

	return sum / count;
}

struct RandPDU *ReceiveRandPDU(int datafd)
{
	int count = 0;
	int ret = 0;
	struct RandPDU *pstPDU = NULL;

	ret = read(datafd,&count,sizeof(int));
	if(ret != sizeof(int))
	{
		perror("read rand count failed");
		return NULL;
	}
	if(count <= 0)
	{
		printf("Rand Count is invalid\n");
		return NULL;
	}

	pstPDU = (struct RandPDU *)malloc((count+1) * sizeof(int));
	if(NULL == pstPDU)
	{
		perror("Malloc Failed");
		return NULL;
	}
	memset(pstPDU,0,(count + 1) * sizeof(int));

	pstPDU->count = count;
	
	ret = read(datafd,pstPDU->buf,pstPDU->count * sizeof(int));
	if(ret != pstPDU->count * sizeof(int))
	{
		perror("Receive Rand Data Failed");
		free(pstPDU);
		pstPDU = NULL;
		return NULL;
	}
	
	return pstPDU;
}

int CreateServerSocket(char *ip,unsigned short port)
{
	int fd = -1;
	int ret = 0;
	struct sockaddr_in servaddr;

	fd = socket(AF_INET,SOCK_STREAM,0);

	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	inet_aton(ip,&servaddr.sin_addr);

	ret = bind(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	ret += listen(fd,6);
	if(ret < 0)
	{
		close(fd);
		fd = -1;
		perror("bind or listen error");
		return -1;
	}
	
	return fd;
}
//client.c
#include "randpdu.h"

int CreateClientSocket(char *ip,unsigned short port);
int InputNumber();
int main(int argc,char *argv[])
{
	int port = 0;
	int fd = -1;
	int count = 0;
	struct RandPDU *pstPDU = NULL;
	int i = 0;
	float ave = 0.0f;
	int ret = 0;
	
	if(argc < 3)
	{
		printf("argument too few\n");
		return 1;
	}
	sscanf(argv[2],"%d",&port);

	if(port <= 0 || port > 65535)
	{
		printf("port is invalid\n");
		return 2;
	}

	srand(time(NULL));

	fd = CreateClientSocket(argv[1],(unsigned short)port);
	if(fd < 0)
	{
		return 3;
	}

	do
	{
		count = InputNumber();
		if(count <= 0)
		{
			printf("Your input error\n");
			break;
		}

		pstPDU = (struct RandPDU *)malloc((count+1) * sizeof(int));
		if(NULL == pstPDU)
		{
			perror("Malloc Failed");
			continue;
		}
		memset(pstPDU,0,(count + 1) * sizeof(int));

		pstPDU->count = count;
		for(i = 0;i < count;i++)
		{
			*((int *)pstPDU->buf + i) = rand() % 1000;
		}

		ret = write(fd,pstPDU,(pstPDU->count + 1) * sizeof(int));
		free(pstPDU);
		pstPDU = NULL;
		if(ret != (count + 1) * sizeof(int))
		{
			perror("Send Rand Failed");
			break;
		}

		ret = read(fd,&ave,sizeof(float));
		if(ret != sizeof(float))
		{
			perror("Receive Ave Failed\n");
			break;
		}
		printf("The avrange is %.2f\n",ave);
	}while(1);

	close(fd);
	fd = -1;
	return 0;
}

int InputNumber()
{
	int num = 0;
	
	printf("Please input a count:\n");
	scanf("%d",&num);

	while(getchar() != '\n')
	{
	}

	return num;
}

int CreateClientSocket(char *ip,unsigned short port)
{
	int fd = -1;
	int ret = 0;
	struct sockaddr_in servaddr;

	fd = socket(AF_INET,SOCK_STREAM,0);

	bzero(&servaddr,sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	inet_aton(ip,&servaddr.sin_addr);

	ret = connect(fd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	if(ret < 0)
	{
		close(fd);
		fd = -1;
		perror("connect failed");
		return -1;
	}
	
	return fd;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值