【socket】基于Linux使用select上报温度–服务端

select使用

*
select函数
select流程图
服务端代码实现

select函数

select监视并等待多个文件描述符的属性发生变化,它监视的属性分3类,分别是readfds(文件描述符有数据到来可读)、 writefds(文件描述符可写)、和exceptfds(文件描述符异常)。调用后select函数会阻塞,直到有描述符就绪(有数据可读、可写、 或者有错误异常),或者超时( timeout 指定等待时间)发生函数才返回。当select()函数返回后,可以通过遍历 fdset,来找到 究竟是哪些文件描述符就绪。

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

参数:
nfds:指待测试的fd的总个数,它的值是待测试的最大文件描述符加1
中间三个参数readset、writeset和exceptset指定要让内核测试读、写和异常条件的fd集合,如果不需要测试的可以设置为NULL;
timeout:设置select的超时时间,如果设置为NULL则永不超时;
select函数的返回值是就绪描述符的数目,超时时返回0,出错返回-1;

struct timeval
{
    long tv_sec;
    long tv_usec;
};

void FD_ZERO(fd_set *set);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);

select的缺点:

  • 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
  • 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
  • select支持的文件描述符数量太小了,默认是1024

可以从内核和select的关系来看:
(1)传向select的参数告诉内核:

①我们所关心的描述符。
②对于每个描述符我们所关心的条件。
③希望等待多长时间

(2)从select返回时,内核告诉我们:

①已准备好的描述符的数量。
②哪一个描述符已准备好读、写或异常条件

select流程图

【socket】基于Linux使用select上报温度--服务端

; 服务端代码实现

  • 通过命令行指定监听的端口;
  • 程序放到后台运行,并通过syslog记录程序的运行出错、调试日志;
  • 程序能够捕捉kill信号正常退出;
  • 服务器要支持多个客户端并发访问,可以选择多路复用、多进程或多线程任意一种实现;
  • 服务器收到每个客户端的数据都解析后保存到数据库中,接收到的数据格式为: “ID/时间/温度”,如192.168.0.26/2022-01-21 15:40:30/20.0C”;
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include "sqlite3.h"

#define BACKLOG             13
#define ARRAY_SIZE(x)       (sizeof(x)/sizeof(x[0]))

void print_usage(char* progname);
void sig_stop(int signum);
void sqlite_tem(char *buf);
int socket_listen(char *listen_ip, int listen_serv_port);

static int g_stop = 0;

int main(int argc, char **argv)
{
    int                     rv;
    int                     ret;
    int                     opt;
    int                     idx;
    int                     port;
    int                     log_fd;
    int                     ch = 1;
    int                     daemon_run = 0;

    int                     ser_fd = -1;
    int                     cli_fd = -1;
    struct sockaddr_in      cli_addr;
    socklen_t               cliaddr_len = 20;
    int                     maxfd = 0;
    int                     fds_array[1024];
    fd_set                  rdset;
    int                     found;
    int                     i;

    char                    *zErrMsg;
    sqlite3                 *db;
    char                    buf[1024];

    struct option            opts[] = {
            {"daemon", no_argument, NULL, 'd'},
            {"port", required_argument, NULL, 'p'},
            {"help", no_argument, NULL, 'h'},
            {NULL, 0, NULL, 0}
    };

    while ((opt = getopt_long(argc, argv, "dp:h", opts, &idx)) != -1)
    {
        switch (opt)
        {
        case 'd':
            daemon_run = 1;
            break;
        case 'p':
            port = atoi(optarg);
            break;
        case 'h':
            print_usage(argv[0]);
            return 0;
        }
    }

    if (!port)
    {
        print_usage(argv[0]);
        return 0;
    }

    if (daemon_run)
    {
        printf("Program %s is running at the background now\n", argv[0]);

        log_fd = open("receive_temper.log", O_CREAT | O_RDWR, 0666);
        if (log_fd < 0)
        {
            printf("Open the logfile failure : %s\n", strerror(errno));
            return 0;
        }

        dup2(log_fd, STDOUT_FILENO);
        dup2(log_fd, STDERR_FILENO);

        if ((daemon(1, 1)) < 0)
        {
            printf("Deamon failure : %s\n", strerror(errno));
            return 0;
        }
    }

    signal(SIGUSR1, sig_stop);

    if( (ser_fd = socket_listen(NULL, port)) < 0 )
    {
        printf("ERROR: %s server listen on serv_port %d failure\n", argv[0], port);
        return -2;
    }
    printf("server start to listen on serv_port %d\n",  port);

    for (i = 0;i < ARRAY_SIZE(fds_array);i++)
    {
        fds_array[i] = -1;
    }
    fds_array[0] = ser_fd;

    while (!g_stop)
    {
        FD_ZERO(&rdset);
        for (i = 0;i < ARRAY_SIZE(fds_array);i++)
        {
            if (fds_array[i] < 0)
                continue;
            maxfd = fds_array[i] > maxfd ? fds_array[i] : maxfd;
            FD_SET(fds_array[i], &rdset);
        }

        rv = select(maxfd + 1, &rdset, NULL, NULL, NULL);
        if (rv < 0)
        {
            printf("select failure :%s\n", strerror(errno));
            break;
        }
        else
        {
            if (rv == 0)
            {
                printf("select gettime out\n");
                continue;
            }
        }
        if (FD_ISSET(ser_fd, &rdset))
        {
            if ((cli_fd = accept(ser_fd, (struct sockaddr*) & cli_addr, &cliaddr_len)) < 0)
            {
                printf("accept new client failure:%s\n", strerror(errno));
                continue;
            }
            found = 0;

            for (i = 0;i < ARRAY_SIZE(fds_array);i++)
            {
                if (fds_array[i] < 0)
                {
                    printf("accrpt new client[%d] and add it into array\n", cli_fd);
                    fds_array[i] = cli_fd;
                    found = 1;
                    break;
                }
            }

            if (!found)
            {
                printf("accept new client[%d] but full, so refuse it\n", cli_fd);
                close(cli_fd);
            }
        }
        else
        {
            for (i = 0;i < ARRAY_SIZE(fds_array);i++)
            {
                if (fds_array[i] < 0 || !FD_ISSET(fds_array[i], &rdset))
                    continue;
                else
                {

                    memset(buf, 0, sizeof(buf));

                    rv = read(cli_fd, buf, sizeof(buf));
                    if (rv < 0)
                    {
                        printf("Read information from client failure:%s\n", strerror(errno));
                        close(cli_fd);
                        exit(0);
                    }
                    else if (rv == 0)
                    {
                        printf("The connection with client has broken!\n");
                        close(cli_fd);
                        exit(0);
                    }
                    else
                    {
                        printf("%s\n",buf);
                        sqlite_tem(buf);
                        printf("Database inserted successfully!\n");
                    }
                }
            }
        }
    }
    close(ser_fd);

    return 0;
}

void print_usage(char* progname)
{
    printf("-d(--daemon):let program run in the background.\n");
    printf("-p(--port):enter server port.\n");
    printf("-h(--help):print this help information.\n");

    return;
}

void sig_stop(int signum)
{
    if (SIGUSR1 == signum)
    {
        g_stop = 1;
    }

    return;
}

int socket_listen(char *listen_ip, int port)
{
    int                     rv = 0;
    int                     on = 1;
    int                     ser_fd;
    struct sockaddr_in      servaddr;

    if ( (ser_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 )
    {
        printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
        return -1;
    }

    setsockopt(ser_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(port);

    if( !listen_ip )
    {
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    else
    {
        if( inet_pton(AF_INET, listen_ip, &servaddr.sin_addr)  0 )
        {
            printf("Inet_pton() set listen IP address failure\n");
            rv = -2;
            goto cleanup;
        }
    }

    if( bind(ser_fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 )
    {
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
        rv = -3;
        goto cleanup;
    }

    if( listen(ser_fd, 64) < 0 )
    {
        printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
        rv = -4;
        goto cleanup;
    }

cleanup:
    if( rv < 0 )
        close(ser_fd);
    else
        rv = ser_fd;
    return rv;
}

void sqlite_tem(char *buf)
{
    int             nrow=0;
    int             ncolumn = 0;
    char          **azResult=NULL;
    int             rv;
    sqlite3        *db=NULL;
    char           *zErrMsg = 0;
    char            sql1[100];
    char           *ipaddr=NULL;
    char           *datetime=NULL;
    char           *temper=NULL;
    char           *sql = "create table if not exists temperature(ipaddr char(30), datetime char(50), temper  char(30))";

    ipaddr = strtok(buf,"/");
    datetime = strtok(NULL, "/");
    temper = strtok(NULL, "/");

    rv = sqlite3_open("tempreture.db", &db);
    if(rv)
    {
        printf("Can't open database:%s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return;
    }
    printf("opened a sqlite3 database named tempreture.db successfully!\n");

    int ret = sqlite3_exec(db,sql, NULL, NULL, &zErrMsg);
    if(ret != SQLITE_OK)
    {
        printf("create table fail: %s\n",zErrMsg);
    }

    if(snprintf(sql1,sizeof(sql1), "insert into temper values('%s','%s','%s')", ipaddr, datetime, temper) < 0)
    {
        printf("Failed to write data\n");
    }

    sqlite3_exec(db, sql1, 0, 0, &zErrMsg);
    sqlite3_free(zErrMsg);
    sqlite3_close(db);
    return;
}

优点:
基于select的I/O复用模型的是单进程执行可以为多个客户端服务,这样可以 减少创建线程或进程所需要的CPU时间片或内存资源的开销;此外几乎所有的平台上都支持select(),其良好跨平台支持.

当然它也有两个主要的缺点:
每次调用 select()都需要把fd集合从用户态拷贝到内核态, 之后内核需要遍历所有传递进来的fd,这时如果客户端fd很多时会导致系统开销很大;

单个进程能够监视的文 件描述符的数量存在最大限制,在Linux上一般为1024,可以通过setrlimit()、修改宏定义甚至重新编译内核等方式来提升这一限制,但是这样也会造成效率的降低;

Original: https://www.cnblogs.com/Ye-Wei/p/16728602.html
Author: 西故黄鹤楼
Title: 【socket】基于Linux使用select上报温度–服务端

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/606342/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Go语言中的零值坑记

    开箱即用 什么叫开箱即用呢?因为 Go语言的零值让程序变得更简单了,有些场景我们不需要显示初始化就可以直接用,举几个例子: 切片,他的零值是 nil,即使不用 make进行初始化也…

    Linux 2023年6月6日
    0122
  • ABCD四个顺序执行方法,拓展性延申

    今天在群里,有人问 &#x6709;&#x51E0;&#x4E2A;void&#x8FD4;&#x56DE;&#x503C;&amp…

    Linux 2023年6月7日
    0134
  • Spring Boot 项目启动错误 提示 java.lang.ClassNotFoundException org.apache.log4j.Logger

    问题描述 spring boot项目升级到2.x,启动时出现错误提示:java.lang.ClassNotFoundException: org.apache.log4j.Logg…

    Linux 2023年6月14日
    0101
  • 搭建Nginx四层反向代理

    需求背景: 前段时间公司因为业务需求需要部署一个正向代理,我已经分享出来了https://www.cnblogs.com/Dfengshuo/p/11911406.html,现有因…

    Linux 2023年6月8日
    0124
  • 6.18(反射和注解—>反射机制的作用)

    反射 获取Class对象三种方式第一种方式:类.class第二种方式:对象.getClass()第三种方式:Class.forName &#x5728;&#x4E0…

    Linux 2023年6月7日
    0125
  • C++ NFS挂载

    挂载NFS 挂载NFS时,常用的命令比如: #将远程目录挂载到本地/home/share目录下 mount -t nfs -o nolock 192.168.1.10:/tmp /…

    Linux 2023年6月8日
    082
  • 泛微 OA 前台 GetShell 复现

    自行搭建环境: 漏洞路径: /weaver/weaver.common.Ctrl/.css?arg0=com.cloudstore.api.service.Service_Chec…

    Linux 2023年5月28日
    0105
  • spring boot设置日志打印为控制台输出和文件输出

    日志打印 sources里建 logback-spring.xml ${CONSOLE_LOG_PATTERN} ${CONSOLE_LOG_CHARSET} ${FILE_LOG…

    Linux 2023年6月7日
    0123
  • 电脑中图标变白色教你怎么修复

    复制一下代码到文本文档中 另存为 .bat 然后点击好的配置文件右键以管理员身份运行 就会解决桌面变白的问题 @echo off taskkill /f /im explorer….

    Linux 2023年6月7日
    0102
  • Tomcat

    Tomcat Tomcat tomcat简介 tomcat的用处 部署tomcat 测试访问 访问Host Manager界面 访问Server Status tomcat简介 T…

    Linux 2023年6月6日
    0137
  • 操作系统实战45讲-10 设置工作模式与环境:建立计算机

    实验环境: 虚拟机软件:VMware® Workstation 16 Pro虚拟机:Ubuntu 20.04 生产虚拟硬盘 用 dd 命令(用指定大小的块拷贝一个文件,并在拷贝的同…

    Linux 2023年6月7日
    0128
  • 如何验收安卓PCBA主板的质量和性能

    .版本:v0.1作者:河东西望日期:2022-7-15. 对很多安卓智能设备厂商来说,他们的通用开发模式一般是:ODM/OEM设计开发主板PCBA(包括BSP驱动、原生AOSP系统…

    Linux 2023年6月7日
    0113
  • 设计模式——命令模式

    命令模式定义 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和回复的功能。 Receive接收者角色 该角色就是干活…

    Linux 2023年6月7日
    0114
  • XXE漏洞学习

    0x00 XXE漏洞介绍: 背景:2018-7-4 微信支付SDK漏洞(XXE漏洞),攻击者可以获取服务器中目录结构,文件内容,eg:代码,各种 私钥。敏感数据泄露 0x01什么是…

    Linux 2023年6月6日
    0113
  • 领导:谁再用redis过期监听实现关闭订单,立马滚蛋!

    在电商、支付等领域,往往会有这样的场景,用户下单后放弃支付了,那这笔订单会在指定的时间段后进行关闭操作,细心的你一定发现了像某宝、某东都有这样的逻辑,而且时间很准确,误差在1s内;…

    Linux 2023年5月28日
    095
  • SQLI-LABS(Less-3)

    Less-3(GET-Error based-Single quotes with twist-string) 打开 Less-3页面,可以看到页面中间有一句 Please inp…

    Linux 2023年6月6日
    093
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球