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);