学习C高级(二十七)

学习C高级(二十七)

多进程服务器设计案例

客户端选择要进行的操作:
  1.求长方形的周长和面积(输入长宽)
  2.求椭圆的周长和面积(输入长轴半径与短轴半径)
  3.求三角形的周长和面积(输入三条边)
  0.退出
服务器完成各种形状周长和面积的计算,并返回给客户端由客户端显示结果
要求:
  1) 服务端多进程并发实现可以同时为多个客户端提供循环服务
  2) 客户端可重复选择操作
  3) 使用如下变长结构体表示协议数据单元(PDU)
    struct ShapePDU
    {
      int tlen;//总字节数
      int type;//1 表示后续数据为长方形的长和宽 8
          //2 表示后续数据为椭圆的长轴半径和短轴半径 8
          //3 表示后续数据为三角形的三条边长度 12
          //4 表示服务器发给客户端的正常计算结果 8
          //5 表示服务器发给客户端的计算错误号 4
      char buf[1];
    };
  4) 所有长度数据、周长、面积使用float类型

运行结果:
 先打开服务器。
在这里插入图片描述
 打开客户端
在这里插入图片描述
  再打开一个客户端,这个客户端同样可以得到服务器的服务。再打开多个客户端也可得到服务器的服务。
在这里插入图片描述
  最后查看服务器进程,可以看到,客户端一旦和服务器连接成功,服务器就生成一个孙子进程为客户端提供服务。
在这里插入图片描述
  多进程版服务器和客户端的代码如下:

//shapepdu.h
#ifndef SHAPE_PDU_H
#define SHAPE_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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define PI 3.14

enum ShapeType
{
	RECT_WH = 1,//1 表示后续数据为长方形的长和宽 8      
	ELLI_LS,//2 表示后续数据为椭圆的长轴半径和短轴半径 8
	TRI_SIDE,//3 表示后续数据为三角形的三条边长度 12
	SRV_RET,//4 表示服务器发给客户端的正常计算结果 8
	SRV_ERROR//5 表示服务器发给客户端的计算错误号 4
};

struct ShapePDU//变长结构体表示协议数据单元(PDU)
{
	int tlen;
	int type;
	char buf[1];
};

struct ShapePDU *RecvShapePDU(int fd);
struct ShapePDU *CreateShapePDU(int type,void *pval);
int DestroyShapePDU(struct ShapePDU *pstPDU);

#endif
//shapepdu.c
#include "shapepdu.h"
/*接收PDU*/
struct ShapePDU *RecvShapePDU(int fd)
{
	int tlen = 0;
	int ret = 0;
	struct ShapePDU *pstPDU = NULL;

	ret = read(fd,&tlen,sizeof(int));
	if(ret != sizeof(int))
	{
		perror("read tlen failed");
		return NULL;
	}

	if(tlen <= 0)
	{
		printf("tlen is invalid\n");
		return NULL;
	}
	pstPDU = (struct ShapePDU *)malloc(tlen);
	if(NULL == pstPDU)
	{
		perror("malloc failed");
		return NULL;
	}
	memset(pstPDU,0,tlen);
	
	pstPDU->tlen = tlen;

	ret = read(fd,&pstPDU->type,tlen-sizeof(int));
	if(ret != tlen - sizeof(int))
	{
		perror("read pdu data error");
		free(pstPDU);
		pstPDU = NULL;
		return NULL;
	}

	return pstPDU;
}
/*创建PDU*/
struct ShapePDU *CreateShapePDU(int type,void *pval)
{
	struct ShapePDU *pstPDU = NULL;
	int tlen = sizeof(int) * 2;
    /*检查数据正确性,数据不正确不创建PDU*/
	if(type < RECT_WH || type > SRV_ERROR || NULL == pval)	
	{
		printf("input param is invalid\n");
		return NULL;
	}
    /*根据type决定tlen*/
	switch(type)
	{
		case RECT_WH:
		case ELLI_LS:
		case SRV_RET:
			tlen += sizeof(float) * 2;
			break;
		case TRI_SIDE:
			tlen += sizeof(float) * 3;
			break;
		case SRV_ERROR:
			tlen += sizeof(int);
			break;
		default:
			break;
	}
    /*根据tlen申请PDU的空间大小*/
	pstPDU = (struct ShapePDU *)malloc(tlen);
	if(NULL == pstPDU)
	{
		perror("malloc failed");
		return NULL;
	}
	memset(pstPDU,0,tlen);
	/*存放数据进PDU*/
	pstPDU->tlen = tlen;
	pstPDU->type = type;
	switch(type)
	{
		case RECT_WH:
		case ELLI_LS:
		case SRV_RET:
			memcpy(pstPDU->buf,pval,sizeof(float) * 2);
			break;
		case TRI_SIDE:
			memcpy(pstPDU->buf,pval,sizeof(float) * 3);
			break;
		case SRV_ERROR:
			memcpy(pstPDU->buf,pval,sizeof(int));
			break;
		default:
			break;
	}

	return pstPDU;
}
/*销毁PDU*/
int DestroyShapePDU(struct ShapePDU *pstPDU)
{
	if(pstPDU != NULL)
	{
		free(pstPDU);
		pstPDU = NULL;
	}
	
	return 0;
}
//server.c
#include "shapepdu.h"

int CreateServerSocket(const char *ip,unsigned short port);
int MainLoop(int servfd);
int IsTriangle(float s1,float s2,float s3);
float GetTriangleArea(float s1,float s2,float s3);
float GetEllipseCircleLen(float r1,float r2);
int HandleClient(int fd);
int main(int argc,char *argv[])
{
	int port = 0;
	int servfd = -1;

	if(argc < 3)
	{
		printf("argument too few\n");
		return 1;
	}

	sscanf(argv[2],"%d",&port);
	/*创建服务器socket*/
	servfd = CreateServerSocket(argv[1],(unsigned short)port);
	if(servfd < 0)
	{
		return 2;
	}

	/*主循环*/
	MainLoop(servfd);

	/*关闭服务器socket*/
	close(servfd);
	servfd = -1;
	return 0;
}

int CreateServerSocket(const 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,5);
	if(ret < 0)
	{
		perror("bind or listen failed");
		close(fd);
		fd = -1;
	}
	return fd;
}

int MainLoop(int servfd)
{
	int datafd = -1;
	pid_t spid = 0;
	pid_t gpid = 0;

	while(1)
	{   //父进程
		datafd = accept(servfd,NULL,NULL);
		spid = fork();
		if(spid < 0)
		{
			perror("fork son-process failed");
			close(datafd);
			datafd = -1;
			continue;
		}
		if(spid == 0)
		{//子进程
			close(servfd);
			servfd = -1;
			
			gpid = fork();
			if(gpid < 0)
			{
				perror("fork son-process failed");
				close(datafd);
				datafd = -1;
				return 1;
			}
			if(gpid == 0)
			{//提供服务的孙子进程
				HandleClient(datafd);
				return 0;
			}
			else
			{//子进程
				close(datafd);//
				datafd = -1;
			}
			return 0;
		}
		else
		{//父进程
			close(datafd);
			datafd = -1;
			waitpid(spid,NULL,0);
		}
	}//end while(1)

	return 0;
}

int HandleClient(int fd)
{
	struct ShapePDU *pstPDU = NULL;
	int exitflag = 0;
	int hasRet = 0;
	float ret[2] = {0.0f};

	while(1)
	{
		pstPDU = RecvShapePDU(fd);
		if(NULL == pstPDU)
		{
			break;
		}//根据type计算结果
		switch(pstPDU->type)
		{
			case RECT_WH:
				{
					float w = *(float *)pstPDU->buf;
					float h = *((float *)pstPDU->buf+1);
					if(w <= 0 || h <= 0)
					{
						hasRet = 0;
					}
					else
					{
						hasRet = 1;
						ret[0] = 2 * w + 2 * h;
						ret[1] = w * h;
					}
				}
				break;
			case ELLI_LS:
				{
					float lr = *(float *)pstPDU->buf;
					float sr = *((float *)pstPDU->buf+1);
					if(lr <= 0 || sr <= 0)
					{
						hasRet = 0;
					}
					else
					{
						hasRet = 1;
						ret[0] = GetEllipseCircleLen(lr,sr);
						ret[1] = PI * lr * sr;
					}
				}
				break;
			case TRI_SIDE:
				{
					float s1 = *(float *)pstPDU->buf;
					float s2 = *((float *)pstPDU->buf+1);
					float s3 = *((float *)pstPDU->buf+2);
					if(!IsTriangle(s1,s2,s3))
					{
						hasRet = 0;
					}
					else
					{
						hasRet = 1;
						ret[0] = s1+s2+s3;
						ret[1] = GetTriangleArea(s1,s2,s3);
					}
				}
				break;
			default:
				exitflag = 1;
				break;
		}//end switch
		DestroyShapePDU(pstPDU);
		if(exitflag)
		{
			break;
		}
		if(hasRet)
		{//有结果
			int len = 0;
			pstPDU = CreateShapePDU(SRV_RET,ret);
			if(NULL == pstPDU)
			{
				break;
			}
			len = write(fd,pstPDU,pstPDU->tlen);
			if(len != pstPDU->tlen)
			{
				perror("Send Result Failed");
				break;
			}
			DestroyShapePDU(pstPDU);
		}
		else
		{//无结果
			int v = 100;
			int len = 0;
			pstPDU = CreateShapePDU(SRV_ERROR,&v);
			if(NULL == pstPDU)
			{
				break;
			}
			len = write(fd,pstPDU,pstPDU->tlen);
			if(len != pstPDU->tlen)
			{
				perror("Send Result Failed");
				break;
			}
			DestroyShapePDU(pstPDU);
		}
	}//end while(1)

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

float GetEllipseCircleLen(float r1,float r2)
{
	float lr = r1 > r2 ? r1 : r2;
	float sr = r1 < r2 ? r1 : r2;

	return 2 * PI * sr + 4 * (lr - sr);
}

float GetTriangleArea(float s1,float s2,float s3)
{
	float p = (s1+s2+s3)/2;

	return sqrt(p * (p-s1) * (p-s2) * (p-s3));
}

int IsTriangle(float s1,float s2,float s3)
{
	if(s1 <= 0 || s2 <= 0 || s3 <= 0)
	{
		return 0;
	}

	if(s1+s2 > s3 && s2+s3 > s1 && s1+s3 > s2)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//client.c
#include "shapepdu.h"

int DisplayUI();
int MainLoop(int fd);
float InputLength(const char *pstr);
int CreateClientSocket(const char *ip,unsigned short port);
int main(int argc,char *argv[])
{
	int fd = -1;
	int port = 0;
	
	if(argc < 3)
	{
		printf("argument too few\n");
		return 1;
	}
	sscanf(argv[2],"%d",&port);

	/*创建客户端socket并连接服务器*/
	fd = CreateClientSocket(argv[1],(unsigned short)port);	

	MainLoop(fd);	
	/*关闭描述符*/
	close(fd);
	fd = -1;
	return 0;
}

/*主循环*/
int MainLoop(int fd)
{
	int exitflag = 1;
	int op = -1;
	float arr[3] = {0.0f};
	struct ShapePDU *pstPDU = NULL;
	int ret = 0;

	while(exitflag)
	{
		op = DisplayUI();//显示界面
		switch(op)
		{
			case 1://长方形
				arr[0] = InputLength("Please input wide of rectangle");	
				arr[1] = InputLength("Please input high of rectangle");
                /*创建PDU,发送PDU,销毁PDU*/
				pstPDU = CreateShapePDU(RECT_WH,arr);
				ret = write(fd,pstPDU,pstPDU->tlen);
				if(ret != pstPDU->tlen)
				{
					DestroyShapePDU(pstPDU);
					exitflag = 0;
					break;
				}
				DestroyShapePDU(pstPDU);
                /*接收服务器发来的PDU*/
				pstPDU = RecvShapePDU(fd);
				if(NULL == pstPDU)
				{
					exitflag = 0;
					break;
				}
				/*打印结果*/
				if(pstPDU->type == SRV_RET)
				{
					printf("The Circle-Len is %.2f\n",*(float *)pstPDU->buf);
					printf("The Area is %.2f\n",*((float *)pstPDU->buf + 1));
				}
				else
				{
					printf("No Result\n");
				}
                /*销毁服务器发来的PDU*/
				DestroyShapePDU(pstPDU);
				
				break;
			case 2://椭圆
				arr[0] = InputLength("Please input long-r");	
				arr[1] = InputLength("Please input short-r");

				pstPDU = CreateShapePDU(ELLI_LS,arr);
				ret = write(fd,pstPDU,pstPDU->tlen);
				if(ret != pstPDU->tlen)
				{
					DestroyShapePDU(pstPDU);
					exitflag = 0;
					break;
				}
				DestroyShapePDU(pstPDU);

				pstPDU = RecvShapePDU(fd);
				if(NULL == pstPDU)
				{
					exitflag = 0;
					break;
				}
				
				if(pstPDU->type == SRV_RET)
				{
					printf("The Circle-Len is %.2f\n",*(float *)pstPDU->buf);
					printf("The Area is %.2f\n",*((float *)pstPDU->buf + 1));
				}
				else
				{
					printf("No Result\n");
				}
				DestroyShapePDU(pstPDU);
				
				break;
			case 3://三角形
				arr[0] = InputLength("Please input side1");	
				arr[1] = InputLength("Please input side2");
				arr[2] = InputLength("Please input side3");

				pstPDU = CreateShapePDU(TRI_SIDE,arr);
				ret = write(fd,pstPDU,pstPDU->tlen);
				if(ret != pstPDU->tlen)
				{
					DestroyShapePDU(pstPDU);
					exitflag = 0;
					break;
				}
				DestroyShapePDU(pstPDU);

				pstPDU = RecvShapePDU(fd);
				if(NULL == pstPDU)
				{
					exitflag = 0;
					break;
				}
				
				if(pstPDU->type == SRV_RET)
				{
					printf("The Circle-Len is %.2f\n",*(float *)pstPDU->buf);
					printf("The Area is %.2f\n",*((float *)pstPDU->buf + 1));
				}
				else
				{
					printf("No Result\n");
				}
				DestroyShapePDU(pstPDU);
				break;
			case 0:
				exitflag = 0;
				break;
			default:
				break;
		}//end switch
	}//end while(1)
	return 0;
}

float InputLength(const char *pstr)
{
	float v = 0.0f;

	printf("%s\n",pstr);
	scanf("%f",&v);

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

	return v;
}

int DisplayUI()
{
	int cmd = 0;

	printf("Please select your operation:\n");
	printf("1. Get Rectangle Circle_Len and Area\n");
	printf("2. Get Ellipse Circle_Len and Area\n");
	printf("3. Get Triangle Circle_Len and Area\n");
	printf("0. Exit\n");
	
	scanf("%d",&cmd);
	
	while(getchar() != '\n')
	{
	}

	return cmd;
}

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

	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)
	{
		perror("connect failed");
		close(fd);
		fd = -1;
		return -1;
	}

	return fd;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值