学习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;
}