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

摘要:
C/S系统实现了两个数的和的任务。要求:实现配置文件实现日志滚动,设置非阻塞套接字,EPOLL实现客户端连接检测,设置心跳检测主线程+心跳检测线程+EPOLL的ET模式处理事务线程。注意:将volatile类型设置为跳出循环。简介:本实验分为五个文件夹:bin、config、include、log、src和makefile。以下是系统的详细内容和功能:bin:存储二进制文件,生成可执行文件,并仅通过在超级终端打开它来执行系统。

C/S系统实现两数求和

  • 任务要求

  1. 实现配置文件

  2. 实现日志滚动

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

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

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

    注意事项:设置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处理事物,两种工作模式(etlt

      • 主线程里面包含两个线程,心跳检测线程以及et/lt事物处理线程:

        • 心跳检测线程:

          检测用户是否掉线,每隔3秒计数器加1,当et接收到客户传来的数据包时,重置计数器,当计数器达到5的时候断开连接。用户用链表连接。

        • et事物处理线程:

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

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

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

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

        • lt事物处理线程:

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

      实现代码:

      • 客户端代码:
      • client.c:客户端主代码
        C/S系统实现两数求和(非阻塞+epoll+心跳包检测用户在线状况+滚动日志+配置文件.)第1张C/S系统实现两数求和(非阻塞+epoll+心跳包检测用户在线状况+滚动日志+配置文件.)第2张
        #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;
        }
        View Code

        config.c:配置文件

        C/S系统实现两数求和(非阻塞+epoll+心跳包检测用户在线状况+滚动日志+配置文件.)第1张C/S系统实现两数求和(非阻塞+epoll+心跳包检测用户在线状况+滚动日志+配置文件.)第4张
        #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] == '

      免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

      上篇ORA-24408: could not generate unique server group nameDubbo系列(3)_官方Demo说明下篇

      宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

      相关文章

      Java性能分析神器--VisualVM Launcher[1]

      Java性能分析神器1--VisualVM Launcher VisualVM 当你日复一日敲代码的时候,当你把各种各样的框架集成到一起的时候,看着大功告成成功运行的日志,有没有那么一丝丝迷茫和惆怅:这TM起的是什么玩意?每一行日志背后代表的是什么东西??他为什么就能跑起来了呢???? 这种时候不要慌,给大家推荐一款功能强大的插件:VisualVM Lau...

      Axios 各种请求方式传递参数格式

      为方便起见,为所有支持的请求方法提供了别名 在使用别名方法时, url、method、data 这些属性都不必在配置中指定 axios.request(config) axios.get(url[, config]) axios.delete(url[, config]) axios.head(url[, config]) axios.post(ur...

      JS的"多线程"

      这个系列的文章名为“JavaScript 进阶”,内容涉及JS中容易忽略但是很有用的,偏JS底层的,以及复杂项目中的JS的实践。主要来源于我几年的开发过程中遇到的问题。小弟第一次写博客,写的不好的地方请诸位斧正,觉得还有一些阅读价值的请帮忙分享下。这个“JavaScript 进阶”是一个系列文章,请大家鼓励鼓励,我尽快更新。另外,如果你有比较好的话题,也可...

      jQuery同步Ajax带来的UI线程阻塞问题及解决办法

      俗话说不作死就不会死,今天作死了一回,写了一个比较二逼的函数,遇到了同步Ajax引起的UI线程阻塞问题,在此记录一下。   事情起因是这样的,因为页面上有多个相似的异步请求动作,本着提高代码可重用性的原则,我封装了一个名为getData的函数,它接收不同参数,只负责获取数据,然后把数据return。基本的逻辑剥离出来是这样的: function get...

      winform中 跨线程启动UI

      C#的winform程序中,是不可以从UI窗口主线程之外的线程去直接操作窗口控件的。确切的解释是,不能从创建控件的线程以外的线程去处理控件的操作,比如修改属性等。以下是跨线程操作控件的报错信息:线程间操作无效: 从不是创建控件“textBox4”的线程访问它解决办法解决方案有两个:方法一,设定Control类的CheckForIllegalCrossThr...

      Git配置可视化的diff 和merge工具

      Windows下使用Git,msysgit是首选,但是msysgit的shell实在不给力,大小不能更改,字体难看。所以,在Windows下,在Cygwin下使用Git是个很不错的选择。 我们在提交代码前,或是合并代码,查看代码修改时,经常要diff一下看看都有哪些修改内容,diff的输出,晦涩难懂,修改多了的时候,简直像天书一样。Git 1.7以后,有了...