APUE-树莓派温度监控项目-断线重连

1、说明

客户端需要实现一下功能

  • 如果网络socket异常(如网络断线、服务器端退出),在网络故障恢复后客户端程序能够自动重连;
  • 在网络故障出错期间,定时采样正常进行,在此瞬间所有采样的数据(临时存储到SQLite数据库中;
  • 网络故障恢复之后,客户端程序自动将之前暂存到数据库中的数据上报销服务器上去, 并从里面删除。

我原本打算直接通过send()==0判断服务器端关闭连接,但后面发现这样不好实现其他功能。

另外,我写的重连函数reconnect_socket()函数在重连成功后才会返回,这会导致断连期间数据只存储了一次

//断线重连
int reconnect_socket(char *serv_ip,int port)
{
    int     retry_count = 0;
    int     conn_fd = -1;

    while( conn_fd < 0 && retry_count < MAX_RETRY_COUNT)
    {
        conn_fd = connect_socket(serv_ip, port);
        if( conn_fd < 0 )
        {
            dbg_print("Failed to reconnect:%s\n",strerror(errno));
            retry_count++;
            close(conn_fd);
            sleep(5);
        }
        else
        {
            dbg_print("Reconnect successfully,new fd:%d\n", conn_fd);
            break;
        }
    }
    return conn_fd;
}

2、getsockopt()/setsockopt()函数

#include <sys/types.h>
#include <sys/socket.h>
  
int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
返回值:若成功则返回0,若出错则返回-1

参数说明:

  • sockfd:必须指向一个打开的套接字描述符。
  • level:标识了选项应用的协议。
  • optname:要设置或获取套接字选项的名字。
  • optval:指向函数设置或获取值得地址,即保存选项值的缓存区。
  • optlen:指定了optval指向的对象的大小。

3、实现代码:

int             sock_opt;
struct tcp_info optval;//获取状态信息
int             opt_len = sizeof(optval);
int             net_state = 1;


db = open_database();
conn_fd = connect_socket(serv_ip, port);

while(1)
{
    sock_opt = getsockopt(conn_fd,IPPROTO_TCP,TCP_INFO,&optval,(socklen_t *)&opt_len);
    if( net_state)//正常
    {
        if( optval.tcpi_state != TCP_ESTABLISHED )//optval.tcpi_state表示当前连接状态,TCP_ESTABLISHED 状态,即已建立连接。如果连接状态不是 TCP_ESTABLISHED,则表明连接已断开
        {
            net_state = 0;//将网络状态标记为故障
            dbg_print("Server disconnect.\n");
        }
        else//连接正常,发送数据
        {
            rv = data_string(s_buf);
            send_size = send_data(conn_fd,s_buf);
            if( send_size < 0 )//发送失败
            {
                dbg_print("Send data failure:%s\n",strerror(errno));
            }
        }
    }
    else //网络状态故障
    {
        rv = get_temperature(&temperature);
        rv = get_datetime(datetime);
        
        store_database(db,&temperature,datetime);//将数据暂存扫数据库中
        dbg_print("Data saved to database:%.2f %s\n",temperature, datetime);
        
        conn_fd = connect_socket(serv_ip,port);//重连
        if( conn_fd > 0 )//成功
        {
            net_state = 1;//重连成功,将网络状态标记为正常
            select_database(db, conn_fd);//发送数据库中的数据
        }
    }
}

delete_database(db);
close(conn_fd);
sqlite3_close(db);

posted @ 2024-03-14 15:08  梨子Li  阅读(12)  评论(0编辑  收藏  举报