C/S系统实现两数求和(非阻塞+epoll+心跳包检测用户在线状况+滚动日志+配置文件.)

C/S系统实现两数求和

  • 任务要求:

  1. 实现配置文件

  2. 实现日志滚动

  3. 设置非阻塞套接字,EPOLL实现

  4. 检测客户端的连接,设置心跳检测

  5. 主线程 + 心跳检测线程 + EPOLL的ET模式处理事务线程

    注意事项:设置volatile类型跳出死循环

  • 作品简介:

本次实验分为bin,config,include,log,src五个文件夹以及一个makefile文件,下面是里面的具体内容以及功能详解:

      1. bin: 存放二进制文件,生成的可执行文件,执行本系统只需在超级终端打开即可。

      2. config: 存放配置文件,客户可以在里面设置所要连接的服务器的ip以及端口,以及所要发送的数据。

      3. include: 存放本系统的头文件。

      4. log:存放日志文件,当一个文件大于10M的时候会新建一个文件存放名是log+数字,后缀名是txt。

      5. src:存放本系统的源代码,里面两个文件夹,client存放客户机的源代码,server存放服务器的源代码。

      6. 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事物处理线程:

          接受客户端发来的连接,处理客户端发来的数据包,完整性检测。

            1. 从waitepoll中得到事物后,判断是连接请求还是发送请求,如果是连接请求就接受连接,如果是发送请求就接收数据。

            2. 建立连接后要在客户链表中加入客户信息。

            3. 由于是非阻塞模式所以要循环接收数据,接收到数据包后进行心跳处理,初始化心跳计数器,写入日志文件,什么时间接收了什么数据,或者发送了什么数据。。。

        • lt事物处理线程:

          由于本次任务是et模式下,所以lt只是实现了接收数据,如果想要查看效果则需要在server.c中把et换为lt即可,lt工作模式比较简单,由于lt在接收数据时,一次没接收完,下次还会发送事物请求,所以不必要一次接收完,只需要判断连接请求建立连接,发送请求接收数据即可,不必要循环接收。

      实现代码:

      • 客户端代码:
      • client.c:客户端主代码
        #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,,sizeof(remote_addr));
        remote_addr.sin_family=AF_INET;
        char s_ip[];
        GetConfigFileStringValue("IPANDPORT", "IP", "127.0.0.1", s_ip, sizeof(s_ip), "Config.ini");
        printf("IP : %s\n", s_ip);
        remote_addr.sin_addr.s_addr=inet_addr(s_ip); uint16_t port = GetConfigFileIntValue("IPANDPORT", "PORT", , "Config.ini");
        printf("port : %d\n", (int)port);
        if (port == -)
        {
        printf("Get port failed!\n");
        return -;
        } remote_addr.sin_port=htons(port); client_sockfd=socket(PF_INET,SOCK_STREAM,); if(client_sockfd<)
        {
        perror("client socket creation failed");
        exit(EXIT_FAILURE);
        } if(connect(client_sockfd,(struct sockaddr *)&remote_addr,sizeof(struct sockaddr))<)
        {
        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)) != ){
        fprintf(stderr, "pthread_create: %s\n", strerror(err));
        exit();
        }
        sleep(); pd = (DATA_PACK *)malloc(sizeof(DATA_PACK));
        char *buf = (char *)malloc(sizeof(*pd));
        pd->data_type = ;
        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,);
        free(pd); len=recv(client_sockfd,pd,BUFFER_SIZE,);
        if(pd->data_type == )
        printf("receive from server %s: %d\n",pd->name, pd->num1);
        else
        printf("receive from server %s: 发送数字个数不正确\n", pd->name);
        if(len<)
        {
        perror("receive from server failed");
        exit(EXIT_FAILURE);
        }
        close(client_sockfd);
        return ;
        }

        config.c:配置文件

        #include "config.h"
        void GetCompletePath(UINT8 *pszConfigFileName, UINT8 *pszWholePath)
        {
        UINT8 *pszHomePath = NULL;
        UINT8 szWholePath[] = {}; if (pszConfigFileName == NULL || pszWholePath == NULL)
        {
        printf("GetCompletePath: input parameter(s) is NULL!\n");
        return;
        } pszHomePath = (UINT8 *)getenv("HOME");
        if (pszHomePath == NULL)
        {
        printf("GetCompletePath: Can't find home path!\n");
        return;
        } snprintf(szWholePath, sizeof(szWholePath)-, "/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[] = {};
        UINT8 szKeyName[] = {};
        UINT8 szContentLine[] = {};
        UINT8 szContentLineBak[] = {};
        UINT32 iContentLineLen = ;
        UINT32 iPositionFlag = ; if (fp == NULL || pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL)
        {
        printf("GetStringContentValue: input parameter(s) is NULL!\n");
        return;
        } sprintf(szSectionName, "[%s]", pszSectionName);
        strcpy(szKeyName, pszKeyName); while (feof(fp) == )
        {
        memset(szContentLine, 0x00, sizeof(szContentLine));
        fgets(szContentLine, sizeof(szContentLine), fp); if (szContentLine[] == ';' || szContentLine[] == '\r' || szContentLine[] == '\n' || szContentLine[] == '\0')
        {
        continue;
        } if (strncasecmp(szSectionName, szContentLine, strlen(szSectionName)) == )
        {
        while (feof(fp) == )
        {
        memset(szContentLine, 0x00, sizeof(szContentLine));
        memset(szContentLineBak, 0x00, sizeof(szContentLineBak));
        fgets(szContentLine, sizeof(szContentLine), fp); if (szContentLine[] == ';')
        {
        continue;
        } memcpy(szContentLineBak, szContentLine, strlen(szContentLine)); if (strncasecmp(szKeyName, szContentLineBak, strlen(szKeyName)) == )
        {
        iContentLineLen = strlen(szContentLine);
        for (iPositionFlag = strlen(szKeyName); iPositionFlag <= iContentLineLen; iPositionFlag ++)
        {
        if (szContentLine[iPositionFlag] == ' ')
        {
        continue;
        }
        if (szContentLine[iPositionFlag] == '=')
        {
        break;
        } iPositionFlag = iContentLineLen + ;
        break;
        } iPositionFlag = iPositionFlag + ; if (iPositionFlag > iContentLineLen)
        {
        continue;
        } memset(szContentLine, 0x00, sizeof(szContentLine));
        strcpy(szContentLine, szContentLineBak + iPositionFlag); for (iPositionFlag = ; iPositionFlag < strlen(szContentLine); iPositionFlag ++)
        {
        if (szContentLine[iPositionFlag] == '\r' || szContentLine[iPositionFlag] == '\n' || szContentLine[iPositionFlag] == '\0')
        {
        szContentLine[iPositionFlag] = '\0';
        break;
        }
        } strncpy(pszOutput, szContentLine, iOutputLen-);
        break;
        }
        else if (szContentLine[] == '[')
        {
        break;
        }
        }
        break;
        }
        }
        } void GetConfigFileStringValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT8 *pDefaultVal, UINT8 *pszOutput, UINT32 iOutputLen, UINT8 *pszConfigFileName)
        {
        FILE *fp = NULL;
        UINT8 szWholePath[] = {}; if (pszSectionName == NULL || pszKeyName == NULL || pszOutput == NULL || pszConfigFileName == NULL)
        {
        printf("GetConfigFileStringValue: input parameter(s) is NULL!\n");
        return;
        } if (pDefaultVal == NULL)
        {
        strcpy(pszOutput, "");
        }
        else
        {
        strcpy(pszOutput, pDefaultVal);
        } GetCompletePath(pszConfigFileName, szWholePath);
        fp = fopen(szWholePath, "r");
        if (fp == NULL)
        {
        printf("GetConfigFileStringValue: open %s failed!\n", szWholePath);
        return;
        } GetStringContentValue(fp, pszSectionName, pszKeyName, pszOutput, iOutputLen); fclose(fp);
        fp = NULL;
        } INT32 GetConfigFileIntValue(UINT8 *pszSectionName, UINT8 *pszKeyName, UINT32 iDefaultVal, UINT8 *pszConfigFileName)
        {
        UINT8 szGetValue[] = {};
        INT32 iGetValue = ; if (pszSectionName == NULL || pszKeyName == NULL || pszConfigFileName == NULL)
        {
        printf("GetConfigFileIntValue: input parameter(s) is NULL!\n");
        return -;
        } GetConfigFileStringValue(pszSectionName, pszKeyName, NULL, szGetValue, -, pszConfigFileName); if (szGetValue[] == '\0' || szGetValue[] == ';')
        {
        iGetValue = iDefaultVal;
        }
        else
        {
        iGetValue = atoi(szGetValue);
        } return iGetValue;
        }

        heart_client.c:心跳发送

        #include "handsomecui.h"
        #include "heart.h" void *send_heart(void *addr)
        {
        int* client_sockfd = (int*)addr;
        printf("client_socket: %d\n", *client_sockfd);
        pd = (DATA_PACK *)malloc(sizeof(DATA_PACK));
        pd->data_type = ;
        strcpy(pd->name, "127.0.0.1");
        while(){
        // write(client_sockfd,pd,sizeof(DATA_PACK));
        send(*client_sockfd,pd,sizeof(*pd),);
        sleep(); //定时3秒
        }
        free(client_sockfd);
        free(pd);
        return NULL;
        }
      • 服务端代码
        • addfd.c:添加epoll事件
          #include "handsomecui.h"
          #include "addfd.h" void addfd(int epollfd, int fd, int flag){
          struct epoll_event event;
          memset(&event, 0x00, sizeof(event));
          event.data.fd = fd;
          event.events = EPOLLIN;
          if(flag){
          event.events |= EPOLLET;
          }
          epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
          setnonblocking(fd);
          }

          et.c:et工作模式

          #include "handsomecui.h"
          #include "et.h"
          #include "heart.h"
          extern s_t *s_head;
          #define MAX_LEN 1024
          extern volatile g_stop;
          void *et(void *arg){
          pth_etlt *pth_arg = (pth_etlt *)arg;
          struct epoll_event* events = pth_arg->events;
          int number = pth_arg->number;
          int epollfd = pth_arg->epollfd;
          int listenfd = pth_arg->listenfd;
          int i;
          DATA_PACK *data = (DATA_PACK *)malloc(sizeof(DATA_PACK));
          int p = ;
          DATA_PACK *num = (DATA_PACK *)malloc(sizeof(DATA_PACK));
          char *buf = (char *)malloc(sizeof(*num));
          int len = sizeof(*num); for(i = ; i < number; i++){
          int sockfd = events[i].data.fd;
          struct sockaddr_in client_address;
          if(sockfd == listenfd){
          socklen_t client_addresslen = sizeof(client_address);
          int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addresslen);
          addfd(epollfd, connfd, );
          printf("client ip: %s port: %d\n", inet_ntoa(client_address.sin_addr),ntohs(client_address.sin_port));
          s_t *p = (s_t *)malloc(sizeof(s_t)), *q;
          strcpy(p->peerip, inet_ntoa(client_address.sin_addr));
          strcpy(p->name, inet_ntoa(client_address.sin_addr));
          p->sockfd = sockfd;
          p->count = ;
          q = s_head->next;
          s_head->next = p;
          p->next = q; }
          else if(events[i].events & EPOLLIN){
          // printf("ET once\r\n");
          while(g_stop){
          //printf("g_stop = %d\n", g_stop);
          //memset(data, 0x00, sizeof(*data));
          int ret = recv(sockfd, data, MAX_LEN, );
          if(ret < ){
          if((errno == EAGAIN) || (errno == EWOULDBLOCK)){
          //printf("read later \r\n");
          break;
          }
          close(sockfd);
          break;
          }
          else if(ret == ){
          close(sockfd);
          }
          else{
          heart_handler(sockfd, data);
          // printf("接受的数据长度是: %d\n", ret);
          // printf("recv data from %d buf id %s %d %d \r\n", sockfd, data->name, data->num1, data->num2);
          if(data->data_type == ){
          if(data->num1 == 0xfffffff || data->num2 == 0xfffffff){
          // printf("输入的数字个数不对\n");
          WriteSysLog(data, );
          num->data_type = ;
          strcpy(num->name, "127.0.0.1");
          send(sockfd, buf, len, );
          break;
          }
          WriteSysLog(data, );
          strcpy(num->name, "127.0.0.1");
          num->data_type = ;
          num->num1 = data->num1 + data->num2;
          memcpy(buf, num, len);
          WriteSysLog(num, );
          send(sockfd, buf, len, );
          }
          }
          } }
          else{
          printf("做了另外的事情\r\n");
          }
          }
          pthread_exit((void *)data);
          }

          heart.c:心跳检测,心跳处理

          #include "handsomecui.h"
          #include "heart.h" extern s_t *s_head;
          extern volatile g_stop; void init_shead(){
          s_head = (s_t *)malloc(sizeof(s_t));
          } void heart_handler(int sockfd,DATA_PACK *pd)
          {
          s_t *cur = s_head->next; while( NULL != cur){ if(strcmp(cur->name,pd->name) == ){ cur->count = ; printf("客户端IP: %s :用户 %s 连接正常\n",cur->peerip,pd->name);
          }
          cur = cur->next;
          }
          } void *heart_check(void *p)
          {
          printf("心跳检测线程已开启!\n"); while(g_stop){
          check_handler();
          sleep();
          // printf("stop = %d\n", g_stop);
          }
          // puts("******");
          pthread_exit((void *));
          } void check_handler(){ s_t *temp = NULL; s_t **ppNode = &s_head->next; while(NULL != (*ppNode) && g_stop){ if((*ppNode)->count == ){
          g_stop = ;
          printf("客户端IP: %s :用户 %s 已经掉线!!\n",(*ppNode)->peerip,(*ppNode)->name); close((*ppNode)->sockfd); temp = *ppNode; *ppNode = (*ppNode)->next; free(temp); temp = NULL; return;
          }
          else if((*ppNode)->count > ){ printf("客户端IP: %s :用户 %s 连接异常!\n",(*ppNode)->peerip,(*ppNode)->name); (*ppNode)->count++; printf("count = %d\n",(*ppNode)->count); ppNode = &((*ppNode)->next); continue;
          }
          else if((*ppNode)->count == ){ (*ppNode)->count++; printf("count = %d\n",(*ppNode)->count); ppNode = &((*ppNode)->next);
          }
          else;
          } }

          log.c:滚动日志文件

          #include "handsomecui.h"
          #include "log.h" typedef unsigned char UINT8;
          typedef signed int INT32;
          typedef unsigned int UINT32; void WriteSysLog(DATA_PACK *str, int s_w){ char buf[], temp[];
          long MAXLEN = **;
          time_t timep;
          int fp;
          struct tm *p; time(&timep);
          p = localtime(&timep);
          memset(buf,,sizeof(buf));
          sprintf(buf,"%d-%d-%d %d:%d:%d : ",(+p->tm_year),(+p->tm_mon),\
          p->tm_mday,p->tm_hour, p->tm_min, p->tm_sec); if(!s_w)
          strcat(buf, "收到了");
          sprintf(temp, "%s", str->name);
          strcat(buf, temp);
          if(s_w)
          strcat(buf, "服务器发送了数据: ");
          if(!s_w)
          strcat(buf, "传来的数据: ");
          if(s_w < ){
          sprintf(temp, "%d", str->num1);
          strcat(buf, temp);
          strcat(buf, "\t");
          sprintf(temp, "%d", str->num2);
          if(!s_w)
          strcat(buf, temp);
          strcat(buf, "\n");
          }
          else
          strcat(buf, "发送数字数目不正确\n");
          UINT8 *pszHomePath = (UINT8 *)getenv("HOME");
          UINT8 szWholePath[] = {};
          UINT8 pszConfigFileName[];
          strcpy(pszConfigFileName, "log.txt");
          if (pszHomePath == NULL)
          {
          printf("GetCompletePath: Can't find home path!\n");
          return;
          }
          snprintf(szWholePath, sizeof(szWholePath)-, "/home/handsome/work/socket/Cuijunyong_3/log/%s", pszConfigFileName);
          //puts(szWholePath);
          fp = open(szWholePath,O_RDONLY|O_CREAT|O_WRONLY, );
          if(fp < )
          {
          fprintf(stderr, "open file 1 error: %s\n", strerror(errno));
          }
          else
          {
          off_t n = lseek(fp, , SEEK_END);
          // printf("文件的字节数是:%d\n", (int)n);
          if(n >= MAXLEN)
          {
          close(fp);
          int i;
          for(i = ; i < ; i++){
          strcpy(pszConfigFileName, "log");
          temp[] = i + '';
          temp[] = '\0';
          strcat(pszConfigFileName, temp);
          strcat(pszConfigFileName, ".txt");
          snprintf(szWholePath, sizeof(szWholePath)-, "/home/handsome/work/socket/Cuijunyong_3/log/%s", pszConfigFileName);
          puts(szWholePath);
          fp = open(szWholePath,O_RDONLY|O_CREAT|O_WRONLY, );
          n = lseek(fp, , SEEK_END);
          if(n >= MAXLEN){
          close(fp);
          continue;
          }
          else{
          puts(buf);
          write(fp, buf, strlen(buf));
          close(fp);
          return;
          }
          }
          }
          else
          {
          printf("%s", buf);
          write(fp, buf, strlen(buf));
          close(fp);
          }
          }
          }

          lt.c:lt工作模式

          #include "handsomecui.h"
          #include "lt.h" void lt(struct epoll_event* events, int number, int epollfd, int listenfd){
          char buf[BUF_SIZE];
          int i;
          for(i = ; i < number; i++){
          int sockfd = events[i].data.fd;
          if(sockfd == listenfd){
          struct sockaddr_in client_address;
          socklen_t client_addresslen = sizeof(client_address);
          int connfd = accept(listenfd, (struct sockaddr *)&client_address, &client_addresslen);
          if(connfd < ){
          printf("接受失败\r\n");
          exit();
          }
          addfd(epollfd, connfd, );
          }
          else if(events[i].events & EPOLLIN){
          printf("LT once\r\n");
          memset(buf, 0x00, sizeof(buf));
          int ret = recv(sockfd, buf, sizeof(buf)-, );
          if(ret <= ){
          printf("rec 0\r\n");
          close(sockfd);
          continue;
          }
          printf("recv data from %d buf is %s\r\n", sockfd, buf);
          }
          else{
          printf("做了另外的一些事\r\n");
          }
          }
          }

          server.c:服务器主代码

          #include "handsomecui.h"
          #include "lt.h"
          #include "et.h"
          #include "addfd.h"
          #include "heart.h"
          s_t *s_head = NULL;
          volatile g_stop = ; int main(int argc, char **argv){
          if(argc <= ){
          printf("请输入ip地址和端口\r\n");
          exit();
          }
          const char* ip = argv[];
          int port = atoi(argv[]);
          int ret = ;
          struct sockaddr_in address;
          bzero(&address, sizeof(address));
          address.sin_family = AF_INET;
          inet_pton(AF_INET, ip, &address.sin_addr);
          address.sin_port = htons(port); int listenfd = socket(AF_INET, SOCK_STREAM, );
          assert(listenfd >= ); ret = bind(listenfd, (struct sockaddr *)&address, sizeof(address));
          assert(ret != -); ret = listen(listenfd, );
          assert(ret != -); struct epoll_event events[MAX_SOCKET_NUMBERS];
          int epollfd = epoll_create();
          assert(epollfd != -); addfd(epollfd,listenfd, ); int err1;
          pthread_t pth1;
          init_shead();
          if( (err1 = pthread_create(&pth1, NULL, heart_check, (void *)) ) != ){
          puts("-------------------");
          fprintf(stderr, "pthread_creat : %s\n", strerror(err1)); exit();
          }
          DATA_PACK *num;
          while(g_stop){
          int err;
          int ret = epoll_wait(epollfd, events, MAX_SOCKET_NUMBERS, -);
          if(ret < ){
          printf("epoll等候失败\r\n");
          exit();
          }
          pthread_t pth;
          pth_etlt arg = {events, ret, epollfd, listenfd};
          if( (err = pthread_create(&pth, NULL, et, (void *)&arg) ) != ){ fprintf(stderr, "pthread_creat : %s\n", strerror(err));
          exit();
          }
          //pthread_join(pth, (void**)&num);
          //printf("num1 = %d num2 = %d\n", num->num1, num->num2);
          //lt(events, ret, epollfd, listenfd);
          }
          close(listenfd);
          return ;
          }

          setnonblocking.c:设置非阻塞

          #include "handsomecui.h"
          #include "setnonblocking.h" int setnonblocking(int fd){
          int old_option = fcntl(fd, F_GETFL);
          int new_option = old_option | O_NONBLOCK;
          fcntl(fd, F_SETFL, new_option);
          return new_option;
          }

      配置文件config.ini:

      [IPANDPORT]
      ;the name of IP
      IP=127.0.0.1
      ;the age of employee
      PORT= [TWONUMBER]
      ;the name of employer
      NUMBER1=
      ;the age of employer
      NUMBER2=

      原文地址:http://www.cnblogs.com/handsomecui/p/5694262.html

      上一篇:Python之线程池


      下一篇:2021/8/17随笔