最近客户要拿几块板子到现场环境跑测试,而这套软件也需要根据实际情况作修改,而且客户也要不断增加功能。于是写了一套远程升级软件,因为只是调试时使用,所以不会有报文CRC校验之类的功能。
升级流程由服务器端发起,客户端负责把文件保存到指定目录下。重启后会自动升级。这里把整个流程分为几个状态,并且把当前状态放进报文里,接收方根据报文里的状态值分析报文内容。状态及流程如下:
停止状态
开始状态
主 -> 启动升级报文(0x01)
回复报文(0x01) <- 从
传输状态
主 -> 传输报文(0x02) 流水号 长度(H) 长度(L) 报文内容
回复报文(0x02) 流水号 <- 从
结束状态
主 -> 0x03
0x03 <- 从
下面直接贴代码,服务器端:
/* 升级状态定义 */
#define UPGRADE_STOP (0)
#define UPGRADE_START (1)
#define UPGRADE_TRANSMIT (2)
#define UPGRADE_FINISH (3)
/* 升级相关结构体 */
struct upgrade_ctrl_s{
char file_name[256]; /* 用于升级的文件名 */
int fd;
off_t file_size;
unsigned int hub_id;
unsigned int send_cnt;
unsigned char frame_id; /* 流水号 */
};
struct upgrade_ctrl_s g_upgrade_ctrl;
// 结构体初始化
void upgrade_init(void)
{
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
}
// 启动升级流程
int upgrade_start(int sfd, int hub_id)
{
struct stat file_stat;
unsigned char buf_send[2048] = {0};
g_upgrade_ctrl.fd = open("file name", O_RDONLY);
if ( g_upgrade_ctrl.fd < 0 )
{
printf("error: can not open file: %s\r\n", "file name");
return 0;
}
if ( stat("file name", &file_stat) < 0 )
{
close(g_upgrade_ctrl.fd);
return 0;
}
g_upgrade_ctrl.file_size = file_stat.st_size;
g_upgrade_ctrl.hub_id = hub_id;
g_upgrade_ctrl.frame_id = 0;
g_upgrade_ctrl.send_cnt = 0;
/* 发启动报文 */
buf_send[0] = PROT_FLAG_SERVER;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_START;
buf_send[3] = PROT_FLAG_SERVER;
(void)send(sfd, buf_send, 4, 0);
return 1;
}
// 文件数据传输
int upgrade_send(int sfd)
{
unsigned char buf_send[2048] = {0};
int ret;
g_upgrade_ctrl.frame_id++;
buf_send[0] = PROT_FLAG_SERVER;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_TRANSMIT;
buf_send[3] = g_upgrade_ctrl.frame_id;
ret = read(g_upgrade_ctrl.fd, &buf_send[6], 1000); /* 每次发1000字节 */
if ( ret <= 0 )
return ret;
g_upgrade_ctrl.send_cnt += ret;
buf_send[4] = (unsigned char)(ret >> 8);
buf_send[5] = (unsigned char)ret;
ret += 6;
buf_send[ret] = PROT_FLAG_SERVER;
ret++;
(void)send(sfd, buf_send, ret, 0);
return ret;
}
void upgrade_send_error(int sfd)
{
unsigned char buf_send[4];
/* 发因为错误而结束报文 */
buf_send[0] = PROT_FLAG_SERVER;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_STOP;
buf_send[3] = PROT_FLAG_SERVER;
(void)send(sfd, buf_send, 4, 0);
}
// 处理中继升级相关报文
void svr_meter_upgrade(int sfd, unsigned char *buff, unsigned int len)
{
unsigned char buf_send[2048] = {0};
if ( buff[2] == UPGRADE_START )
{/* 起始协议 */
if ( buff[3] == 1 )
{
if ( upgrade_send(sfd) > 0 )
{
return;
}
else
{
/* 发启动报文 */
buf_send[0] = PROT_FLAG_SERVER;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_START;
buf_send[3] = PROT_FLAG_SERVER;
(void)send(sfd, buf_send, 4, 0);
}
}
else
{
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
}
}
else if ( buff[2] == UPGRADE_TRANSMIT )
{
if ( buff[3] != g_upgrade_ctrl.frame_id )
{
/* 传输出错,等待重新再来 */
close(g_upgrade_ctrl.fd);
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
upgrade_send_error(sfd);
}
else
{
if ( g_upgrade_ctrl.send_cnt == g_upgrade_ctrl.file_size )
{// 传输完成
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
buf_send[0] = PROT_FLAG_SERVER;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_FINISH;
buf_send[3] = PROT_FLAG_SERVER;
(void)send(sfd, buf_send, 4, 0);
}
else
{
upgrade_send(sfd);
}
}
}
}
客户端:
/* 升级状态定义 */
#define UPGRADE_STOP (0)
#define UPGRADE_START (1)
#define UPGRADE_TRANSMIT (2)
#define UPGRADE_FINISH (3)
#define UPGRADE_FILE_NAME "/data/upgrade.tar.gz"
/* 升级相关结构体 */
struct upgrade_ctrl_s{
int fd; /* 保存文件句柄 */
unsigned char frame_id; /* 流水号 */
unsigned int timeout_cnt; /* 超过一定时间没升级交互就退出升级流程 */
};
struct upgrade_ctrl_s g_upgrade_ctrl;
void prot_meter_upgrade_handle(unsigned char *buf, unsigned int len)
{
unsigned char buf_send[2048] = {0};
unsigned int data_len;
if ( buf[1] == UPGRADE_STOP )
{// 服务器端主动停止升级流程
if ( g_upgrade_ctrl.fd > 0 )
close(g_upgrade_ctrl.fd);
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
}
else if ( buf[1] == UPGRADE_START )
{/* 开始升级 */
if ( g_upgrade_ctrl.fd > 0 )
close(g_upgrade_ctrl.fd);
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
g_upgrade_ctrl.fd = open(UPGRADE_FILE_NAME, O_RDWR|O_CREAT|O_TRUNC);
if ( g_upgrade_ctrl.fd >= 0 )
{
g_upgrade_ctrl.frame_id = 1;
g_upgrade_ctrl.timeout_cnt = 3;
buf_send[3] = 1;
}
else
{
buf_send[3] = 0;
}
buf_send[0] = PROT_FLAG_TX;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_START;
buf_send[4] = PROT_FLAG_TX;
tcp_client_send(buf_send, 5);
}
else if ( buf[1] == UPGRADE_TRANSMIT )
{
if ( g_upgrade_ctrl.frame_id != buf[2] )
{/* 流水号不一致,出错 */
if ( g_upgrade_ctrl.fd > 0 )
close(g_upgrade_ctrl.fd);
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
buf_send[0] = PROT_FLAG_TX;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = UPGRADE_TRANSMIT;
buf_send[3] = buf[2] + 100; /* 与接收的流水号不一致,服务端就知道出错了 */
buf_send[4] = PROT_FLAG_TX;
tcp_client_send(buf_send, 5);
return;
}
else
{
data_len = buf[3];
data_len <<= 8;
data_len |= buf[4];
write(g_upgrade_ctrl.fd, &buf[5], data_len);
buf_send[0] = PROT_FLAG_TX;
buf_send[1] = PROT_METER_UPGRADE;
buf_send[2] = 2;
buf_send[3] = buf[2];
buf_send[4] = PROT_FLAG_TX;
tcp_client_send(buf_send, 5);
g_upgrade_ctrl.timeout_cnt = 3;
g_upgrade_ctrl.frame_id++;
return;
}
}
else if ( buf[1] == UPGRADE_FINISH )
{// 传输完成
if ( g_upgrade_ctrl.fd > 0 )
close(g_upgrade_ctrl.fd);
sleep(1);
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
}
}
int upgrade_is_busy(void)
{
if ( g_upgrade_ctrl.timeout_cnt > 0 )
{
g_upgrade_ctrl.timeout_cnt--;
if ( g_upgrade_ctrl.timeout_cnt == 0 )
{// timeout
if ( g_upgrade_ctrl.fd > 0 )
close(g_upgrade_ctrl.fd);
memset(&g_upgrade_ctrl, 0, sizeof(g_upgrade_ctrl));
}
}
return g_upgrade_ctrl.timeout_cnt;
}
end