Server
内存映射,未考虑互斥
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>
#define VOTE_REQ 0xA9 /* 投票请求 */
#define INQY_REQ 0xAA /* 投票查询 */
#define INQY_RES 0xA8 /* 查询应答 */
#define HSIZE sizeof (struct vote) /* 投票协议头部大小 */
#define IDSIZE 1 /* 球队ID大小 */
#define BUFSIZE 128
#define VISIZE sizeof(struct voteinfo)
#define MAXTEAM 32 /* 球队数目 */
static unsigned char buf[BUFSIZE]; /* 协议单元 */
struct vote
{
unsigned char type : 2, /* 协议类型 */
prec : 6; /* 前导符101010 */
unsigned short len; /* 协议数据长度 */
//unsigned char content[0];
}__attribute__((packed));
struct voteinfo
{
unsigned char id; /* 球队ID */
unsigned int num; /* 球队得票数 */
}__attribute__((packed));//按 1 进行字节对齐
/*
*投票,查询请求消息
*id 待查询球队ID列表
*vote 为真代表投票请求,
* 为假代表查询请求
*/
int make_vote(unsigned char *id, int vote);
/*
*查询应答消息
*voteinfo,球队投票信息列表
*num,待查询球队ID的个数
*/
int reply_vote(struct voteinfo *votelist, int num);
/*
*输入: buf 来客户端的协议 【前导符(6位) - 协议类型(2位)】 【长度(2Bytes)】 内容(...)
*格式 0:[101010]-[00] [个数x5 + 3] [ID+Counts 1+4 Bytes]...
*格式 1:[101010]-[01] [恒为4] [球队ID,1Byte]
*格式 2:[101010]-[10] [个数+3] [ID 1 Bytes]...
*voteArr 服务器存储的球队投票信息
*输出:
*votelist 存储查询到的球队投票信息
*/
int analyze(const unsigned char *buf, struct voteinfo *voteArr, struct voteinfo *votelist);
/* 初始化函数 */
void Init_vote(struct voteinfo *voteArr);
static void sigchld_handler(int signo)
{
pid_t pid;
int status;
char msg[] = "SIGCHLD caught\n";
write(STDOUT_FILENO, msg, sizeof(msg));
/* 等待已退出的所有子进程 */
do {
pid = waitpid(-1, &status, WNOHANG);
} while (pid > 0);
}
int main(int argc, char *argv[])
{
/* 解析协议信息缓存数组 */
struct voteinfo *votelist= (struct voteinfo *)malloc(MAXTEAM * sizeof(struct voteinfo));
memset(votelist, 0, sizeof(votelist));
/*储存球队信息数组 -->内存消息映射*/
struct voteinfo *voteArr = (struct voteinfo *)mmap(NULL, sizeof(struct voteinfo) *32,
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
memset(voteArr, 0, sizeof(voteArr));
//初始化投票信息数组
Init_vote(voteArr);
int sockfd; /* 服务器套接字 */
int new_fd; /* 服务器连接套接字 */
struct sockaddr_in server_addr; /* 服务器监听套接字 */
struct sockaddr_in client_addr; /* 客户端IP地址 */
socklen_t size;
int portnumber;
struct sigaction child_action;
pid_t pid;
int z;
memset(&child_action, 0, sizeof(child_action));
child_action.sa_flags |= SA_RESTART;
child_action.sa_handler = sigchld_handler;
if (sigaction(SIGCHLD, &child_action, NULL) == -1)
perror("Failed to ignore SIGCHLD");
if (argc != 2)
{
fprintf(stderr, "Usage: %s portnumber\a\n", argv[0]);
exit(1);
}
if ((portnumber = atoi(argv[1])) < 0)
{
fprintf(stderr, "Usage: %s portnumber\a\n", argv[0]);
exit(1);
}
/* 创建服务器监听套接字 */
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "Socket error: %s\a\n", strerror(errno));
exit(1);
}
/* 为监听套接字准备IP地址和端口 */
memset(&server_addr, 0, sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(portnumber);
/* 绑定套接字到指定地址和端口 */
if ((bind(sockfd, (struct sockaddr *)(&server_addr), sizeof server_addr)) == -1)
{
fprintf(stderr, "Bind error: %s\a\n", strerror(errno));
exit(1);
}
/* 监听 */
if (listen(sockfd, 128) == -1)
{
fprintf(stderr, "Listen error: %s\n\a", strerror(errno));
exit(1);
}
printf("sockfd = %d\n", sockfd);
printf("waiting for the client's request...\n");
char *str = "Vote successfully!";
while (1)
{
size = sizeof(struct sockaddr_in);
int bufsize = 0;//cun chu buf size
/* 接收一个客户端连接并创建服务器连接套接字 */
if ((new_fd = accept(sockfd, (struct sockaddr *)(&client_addr), &size)) == -1)
{
fprintf(stderr, "Accept error: %s\a\n", strerror(errno));
exit(1);
}
fprintf(stdout, "Server got connection from %s\n",
inet_ntoa(client_addr.sin_addr));
pid = fork();
switch (pid)
{
case -1:
perror("fork failed");
exit(1);
case 0:
puts("Entering the child\n");
for (;;)
{
z = read(new_fd, buf, sizeof buf);
if (z <= 0)
{
break;
}
printf("buf : 0x%x--0x%x\n", *buf, *(buf + 1));
if(*buf == 0xa9)
{
z = analyze(buf, voteArr, votelist);
if(z)
write(new_fd, str, strlen(str));
}
else if(*buf == 0xaa)
{
printf("----->read succesfully!\n");
int reNum = analyze(buf, voteArr, votelist);
//printf("qiu dui Num = %d\n", reNum);
bufsize = reply_vote(votelist, reNum);
/* 将buf发送给客户端 */
z = write(new_fd, buf, bufsize);
//printf("bufinfo: 0x%x--0x%x--0x%x\n", *buf, *(buf + 1), *(buf + 4));
int index = 0;
while(z <= 0)
{
index++;
printf("----->write failed!\n");
z = write(new_fd, buf, strlen(buf));
if(index >= 10)
break;
}
if(index < 10)
{
printf("------>send success!\n");
memset(buf, 0, sizeof(buf));
}
}
}
printf("Child process: %d exits.\n", getpid());
close(new_fd);
exit(0);
}
}
/* 解除内存映射关系 */
munmap(voteArr, sizeof(struct voteinfo) * 32);
if(votelist != NULL)
{
free(votelist);
votelist == NULL;
printf("----->free success!\n");
}
return 0;
}
int reply_vote(struct voteinfo *votelist, int num)
{
struct vote v;
unsigned char type = INQY_RES;
memset(buf, 0, BUFSIZE);
*(unsigned char *)&v = type; /* 设置前导符和协议类型 */
v.len = HSIZE + VISIZE * num;
memcpy(buf, &v, HSIZE); /* 构造协议头部 */
/* 设置投票球队得票信息 */
memcpy(buf + HSIZE, votelist, VISIZE * num);
return v.len;
}
int analyze(const unsigned char *buf, struct voteinfo *voteArr, struct voteinfo *votelist)
{
if(NULL == voteArr || NULL == votelist)
{
return -1;
}
if (buf == NULL)
{
return -1;
}
if (*buf == 0xa9)
{
int i = 0;
for (; i < MAXTEAM; ++i)
{
//printf("%c ", *(buf + 3));
if (*(buf + 3) == voteArr[i].id)
{
voteArr[i].num++;
printf("voteArr[%d].num = %d\n", i, voteArr[i].num);
break;
}
}
if( i < MAXTEAM)
{
printf("%d队获得一票 \n", (unsigned int)voteArr[i].id - 48);
return 1;
}
else
{
printf("no such ID\n");
}
}
else if (*buf == 0xaa)
{
int i = 0;
unsigned short idNum = *(buf + 1) - 3;
buf += 3;
memset(votelist, 0, sizeof(votelist));
for (; i < idNum; ++i)
{
votelist[i].id = *buf;
votelist[i].num = voteArr[votelist[i].id - 49].num;
buf++;
}
return i;
}
}
void Init_vote(struct voteinfo *voteArr)
{
int i = 0;
for (; i < MAXTEAM; ++i)
{
voteArr[i].id = (unsigned char)(i + 49);
voteArr[i].num = 0;
}
}
Client
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#define VOTE_REQ 0xA9 /* 投票请求 */
#define INQY_REQ 0xAA /* 投票查询 */
#define INQY_RES 0xA8 /* 查询应答 */
#define HSIZE sizeof(struct vote) /* 投票协议头部大小 */
#define IDSIZE 1 /* 球队ID大小 */
#define BUFSIZE 128
#define VISIZE sizeof(struct voteinfo)
#define MAXTEAM 32 /* 球队数目 */
static unsigned char buf[BUFSIZE]; /* 协议单元 */
struct vote
{
unsigned char type : 2, /* 协议类型 */
prec : 6; /* 前导符101010 */
unsigned short len; /* 协议数据长度 */
unsigned char content[0];
}__attribute__((packed));
struct voteinfo
{
unsigned char id; /* 球队ID */
unsigned int num; /* 球队得票数 */
}__attribute__((packed));
/*投票,查询请求消息
*id,待查询球队ID列表
*vote, 为真代表投票请求,
*为假代表查询请求*/
int make_vote(unsigned char *id, int vote);
/*解析协议*/
int analyze(const unsigned char *buf, struct voteinfo *votelist);
int main(int argc, char *argv[])
{
/* 球队投票信息数组 */
struct voteinfo *votelist = (struct voteinfo*)malloc(sizeof(struct voteinfo) * MAXTEAM);
memset(votelist, 0, sizeof votelist);
int sockfd; /* 客户端套接字 */
struct sockaddr_in server_addr; /* 服务器IP地址 */
struct hostent *host; //主机信息结构体
int portnumber;
int nbytes;
int z;
if (argc != 3)
{
fprintf(stderr, "Usage: %s hostname portnumber\a\n", argv[0]);
exit(1);
}
if ((host = gethostbyname(argv[1])) == NULL)
{
fprintf(stderr, "Gethostname error\n");
exit(1);
}
if ((portnumber = atoi(argv[2])) < 0)
{
fprintf(stderr, "Usage: %s hostname portnumber\a\n", argv[0]);
exit(1);
}
/* 创建客户端套接字 */
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
{
fprintf(stderr, "Socket Error: %s\a\n", strerror(errno));
exit(1);
}
/* 创建服务器地址 */
memset(&server_addr, 0, sizeof server_addr);
server_addr.sin_family = PF_INET;
server_addr.sin_port = htons(portnumber);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
printf("sockfd = %d\n", sockfd);
/* 连接服务器 */
if (connect(sockfd, (struct sockaddr *)(&server_addr), sizeof server_addr) == -1)
{
fprintf(stderr, "Connect Error: %s\a\n", strerror(errno));
exit(1);
}
printf("connected to server %s\n", inet_ntoa(server_addr.sin_addr));
for (;;)
{
int index = -1;
unsigned int num[MAXTEAM] = {0};
unsigned char arr[MAXTEAM] = {0};
int reNum = 0;
printf("请输入信息(1:投票 0:查询票数情况)(输入其他则退出):\n");
scanf("%d", &index);
getchar(); // 获取回车键 '10'
if (index == 0)
{
printf("请输入你想要查询的球队ID(1-32)(按0退出):\n");
for(; index < MAXTEAM; ++index)
{
scanf("%d", &num[index]);
getchar();
if(num[index] == 0 || num[index]<1 || num[index] > 32)
{
if(num[index] == 0)
{
index = 0;
break;
}
else
{
printf("请输入1-32之间的队号\n");
index--;
continue;
}
}
arr[index] = (unsigned char)(num[index] + 48);
}
}
else if(index == 1)
{
for(;;)
{
printf("请输入你想要投票的球队ID(1-32):\n");
scanf("%d", &num[0]);
getchar();
if(num[0] >= 1 && num[0] <= 32)
{
arr[0] = (unsigned char)(num[0] + 48);
break;
}
else
{
printf("请正确输入!\n");
continue;
}
}
}
else
{
break;
}
if ((reNum = make_vote(arr, index)) <= 0)
{
fprintf(stderr, "make_vote failed: %s\n", strerror(errno));
printf("make_vote failed\n");
exit(1);
}
z = write(sockfd, buf, reNum);
while(z <= 0)
{
printf("----->write failed!\n");
z = write(sockfd, buf, reNum);
}
printf("----->send success!\n");
printf("send--->bufInfo : 0x%x--0x%x\n", *buf, *(buf + 1));
memset(buf, 0, sizeof buf);
/*
* 从客户端套接字中读取服务器发回的应答
*/
if ((nbytes = read(sockfd, buf, sizeof buf)) == -1)
{
fprintf(stderr, "Read Error: %s\n", strerror(errno));
exit(1);
}
if (nbytes == 0) /* 遇到EOF */
{
printf("server has closed the socket.\n");
break;
}
if(*buf == 0xa8)
{
printf("recv-->bufInfo : 0x%x--0x%x\n", *buf, *(buf + 1));
int renum = analyze(buf, votelist);
int i = 0;
for(i; i < renum; ++i)
{
printf("ID: %d, vote:%d\n", votelist[i].id - 48, votelist[i].num);
}
}
else
printf("Server : %s\n", buf);
}
close(sockfd);
return 0;
}
/*id: 0 - 255*/
int make_vote(unsigned char *id, int vote)
{
struct vote v;
unsigned char type;
int num;
num = strlen(id); /* 获得球队个数 */
if (vote)
type = VOTE_REQ;
else
type = INQY_REQ; /* 投票查询 */
memset(buf, 0, BUFSIZE);
*(unsigned char *)&v = type; /* 设置前导符和协议类型 */
v.len = HSIZE + IDSIZE * num;
memcpy(buf, &v, HSIZE); /* 构造协议头部 */
if (num)
memcpy(buf + HSIZE, id, IDSIZE * num); /* 球队ID */
return v.len;
}
/*
输入: buf 来自服务器 前导符(6位) - 协议类型(2位) 长度(2Bytes) 内容(...)
格式 0:[101010]-[00] [个数x5 + 3] [ID+Counts 1+4 Bytes]...
格式 1:[101010]-[01] [恒为4] [球队ID,1Byte]
格式 2:[101010]-[10] [个数+3] [ID 1 Bytes]...
输出:
votelist 更新
*/
int analyze(const unsigned char *buf, struct voteinfo *votelist)
{
//main 已判断buf不为空
int i = 0;
int idnum = *(buf + 1) / 5;
buf += 3;
for( i; i < idnum; ++i)
{
votelist[i].id = *(buf++);
votelist[i].num = (unsigned int)*buf;
buf += 4;
}
return idnum;
}