C/S系统实现两数求和
任务要求:
实现配置文件
实现日志滚动
设置非阻塞套接字,EPOLL实现
检测客户端的连接,设置心跳检测
主线程 + 心跳检测线程 + EPOLL的ET模式处理事务线程
注意事项:设置volatile类型跳出死循环
作品简介:
本次实验分为bin,config,include,log,src五个文件夹以及一个makefile文件,下面是里面的具体内容以及功能详解:
bin: 存放二进制文件,生成的可执行文件,执行本系统只需在超级终端打开即可。
config: 存放配置文件,客户可以在里面设置所要连接的服务器的ip以及端口,以及所要发送的数据。
include: 存放本系统的头文件。
log:存放日志文件,当一个文件大于10M的时候会新建一个文件存放名是log+数字,后缀名是txt。
src:存放本系统的源代码,里面两个文件夹,client存放客户机的源代码,server存放服务器的源代码。
makefile:本系统的编译文件,在终端打开主目录,输入命令make即可。
具体实现:
客户机:
从ini配置文件中得到连接服务器的ip以及端口,向服务端发送数据包,以及心跳检测发送心跳包。
客户端是以阻塞的方式向服务端发送数据(由于公司业务主要在服务端,所以在这里不用过多要求),主线程分出一个发送心跳线程每隔3秒向用户发送一个数据包;数据包格式:
头部是一个短整型,存放数据包的格式;
0代表发送的是心跳包,4代表发送的是不完整包,8代表发送的包完整。
然后是一个长度为16的字符数组,存放客户的名字,这里用客户的ip来代替。
接下来是两个整型代表发送的两个数字,也就是本任务发送的主要数据。
客户端每隔3秒发送一个心跳包,由服务器检测心跳,从配置文件读取发送的数据向客户端发送数据,由服务器检测数据完整性,ip和端口存放在配置文件的IP项目下,数据在TWONUMBER项目下。
接收从服务端发来的数据,用data_type判断当前数据是否完整,完整就接受输出从服务器得到的数据;
服务器:
服务器实现检测连接,epoll处理事物,两种工作模式(et, lt)
主线程里面包含两个线程,心跳检测线程以及et/lt事物处理线程:
心跳检测线程:
检测用户是否掉线,每隔3秒计数器加1,当et接收到客户传来的数据包时,重置计数器,当计数器达到5的时候断开连接。用户用链表连接。
et事物处理线程:
接受客户端发来的连接,处理客户端发来的数据包,完整性检测。
从waitepoll中得到事物后,判断是连接请求还是发送请求,如果是连接请求就接受连接,如果是发送请求就接收数据。
建立连接后要在客户链表中加入客户信息。
由于是非阻塞模式所以要循环接收数据,接收到数据包后进行心跳处理,初始化心跳计数器,写入日志文件,什么时间接收了什么数据,或者发送了什么数据。。。
lt事物处理线程:
由于本次任务是et模式下,所以lt只是实现了接收数据,如果想要查看效果则需要在server.c中把et换为lt即可,lt工作模式比较简单,由于lt在接收数据时,一次没接收完,下次还会发送事物请求,所以不必要一次接收完,只需要判断连接请求建立连接,发送请求接收数据即可,不必要循环接收。
实现代码:
- 客户端代码:
- client.c:客户端主代码 View Code
#include "handsomecui.h" #include "config.h" #include "heart_client.h" #define BUFFER_SIZE 40 int main(int argc, char *argv[]) { int client_sockfd; int len; struct sockaddr_in remote_addr; memset(&remote_addr,0,sizeof(remote_addr)); remote_addr.sin_family=AF_INET; char s_ip[20]; GetConfigFileStringValue("IPANDPORT", "IP", "127.0.0.1", s_ip, sizeof(s_ip), "Config.ini"); printf("IP : %s ", s_ip); remote_addr.sin_addr.s_addr=inet_addr(s_ip); uint16_t port = GetConfigFileIntValue("IPANDPORT", "PORT", 8866, "Config.ini"); printf("port : %d ", (int)port); if (port == -1) { printf("Get port failed! "); return -1; } remote_addr.sin_port=htons(port); client_sockfd=socket(PF_INET,SOCK_STREAM,0); if(client_sockfd<0) { perror("client socket creation failed"); exit(EXIT_FAILURE); } if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<0) { perror("connect to server failed"); exit(EXIT_FAILURE); } pthread_t pth; int err; int *client_sockfd_1 = (int *)malloc(sizeof(int)); *client_sockfd_1 = client_sockfd; if((err = pthread_create(&pth, NULL, send_heart, (void *)client_sockfd_1)) != 0){ fprintf(stderr, "pthread_create: %s ", strerror(err)); exit(1); } sleep(5); pd = (DATA_PACK *)malloc(sizeof(DATA_PACK)); char *buf = (char *)malloc(sizeof(*pd)); pd->data_type = 8; strcpy(pd->name, "127.0.0.1"); pd->num1= (int )GetConfigFileIntValue("TWONUMBER", "NUMBER1", 0xfffffff, "Config.ini"); pd->num2 = (int )GetConfigFileIntValue("TWONUMBER", "NUMBER2", 0xfffffff, "Config.ini"); len = sizeof(*pd); memcpy(buf, pd, len); send(client_sockfd,buf,len,0); free(pd); len=recv(client_sockfd,pd,BUFFER_SIZE,0); if(pd->data_type == 8) printf("receive from server %s: %d ",pd->name, pd->num1); else printf("receive from server %s: 发送数字个数不正确 ", pd->name); if(len<0) { perror("receive from server failed"); exit(EXIT_FAILURE); } close(client_sockfd); return 0; }
config.c:配置文件
#include "config.h" void GetCompletePath(UINT8 *pszConfigFileName, UINT8 *pszWholePath) { UINT8 *pszHomePath = NULL; UINT8 szWholePath[256] = {0}; if (pszConfigFileName == NULL || pszWholePath == NULL) { printf("GetCompletePath: input parameter(s) is NULL! "); return; } pszHomePath = (UINT8 *)getenv("HOME"); if (pszHomePath == NULL) { printf("GetCompletePath: Can't find home path! "); return; } snprintf(szWholePath, sizeof(szWholePath)-1, "/home/handsome/work/socket/Cuijunyong_3/config/%s", pszConfigFileName); strncpy(pszWholePath, szWholePath, strlen(szWholePath)); } void GetStringContentValue(FILE *fp, UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pszOutput, UINT32 iOutputLen) { UINT8 szSectionName[100] = {0}; UINT8 szKeyName[100] = {0}; UINT8 szContentLine[256] = {0}; UINT8 szContentLineBak[256] = {0}; UINT32 iContentLineLen = 0; UINT32 iPositionFlag = 0; if (fp == NULL || pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL) { printf("GetStringContentValue: input parameter(s) is NULL! "); return; } sprintf(szSectionName, "[%s]", pszSectionName); strcpy(szKeyName, pszKeyName); while (feof(fp) == 0) { memset(szContentLine, 0x00, sizeof(szContentLine)); fgets(szContentLine, sizeof(szContentLine), fp); if (szContentLine[0] == ';' || szContentLine[0] == ' ' || szContentLine[0] == ' ' || szContentLine[0] == '