(一)BUG
经过群里的技术大佬们不懈努力,终于查出minihttp的bug出自tcp socket的意外断链,导致mjpeg的通信管道出问题。那么就需要对这种异常情况处理。
(二)超时处理
1-send超时返回
通过控制台printf排查,查出出bug时,是在【server_thread】线程里面调用send_mjpeg()函数时,卡在了send_to_client中的send这一socket发送函数内。
解决方法:
server_thread主循环开头加入超时处理协议:
SocketTimeoutChange(client_fd, 0,900);
函数定义:
void SocketTimeoutChange(int client_fd, int sec, int msec)
{
struct timeval timeout = {sec, msec};
int ret = setsockopt(client_fd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
}
2-超时之后关闭socket,修改send_to_client函数
int send_to_client(int i, char* buf, ssize_t size) {;
if (send_to_fd(client_fds[i].socket_fd, buf, size) < 0)
{
free_client(i);
return -1;
}
return 0;
}
3-重启通道
光是关闭连接不够的,通道被破坏了,重新连接也接收不到mjpeg,需要重启 :
int restart_channal()
{
printf("\tmjpegdead restart!-\t");
//stop
if (HI_MPI_VENC_StopRecvPic(g_mjpg_venc_chn) == HI_SUCCESS)
printf("--\t");
else
{
printf("->stop error\n");
return -1;
}
//reset
if (HI_MPI_VENC_ResetChn(g_mjpg_venc_chn) == HI_SUCCESS)
printf("--\t");
else
{
printf("->reset error\n");
}
//start
if (HI_MPI_VENC_StartRecvPic(g_mjpg_venc_chn) == HI_SUCCESS)
printf("--\t");
else
{
printf("->restart error\n");
}
printf("-mjpeg finish!\n");
return 0;
}
重启要重启在VENC的线程里面,不能在server_thread线程里面关完就在那个线程重启通道,会报错:
首先在send_to_fd里面设置一个标志位:
if (len < 0)
{
printf("~>len<0 error\t");
g_resetChannalFlag = true;
return -1;
}
在VENC_GetVencStreamProc函数(这也是个子线程)里面while主循环的一开始:
if (g_resetChannalFlag == true)
{
restart_channal();
g_resetChannalFlag = false;
}
(三)超时处理
到这里可以满足单连接,出现问题时直接关闭socket
但多连接时,其他连接继续运行有机会出现其他BUG,这是由于上一个socket的严重卡顿(上面设置了900ms)导致另一个本来好好的socket连接也因为本次卡顿,而导致远方客户端异常断连,但这时候诡异的时,他不会因为socket发不出去而关闭,甚至发了几帧之后会因为mjpeg通道被破坏而彻底停止发送,更加不会触发上面的socket断连后重启通道的机制。
这时候可以从两个角度解决:
1-想方设法触发上面的机制
2-另外设置机制
第一种方法比较简单,而且方便使用,或许可以通过调用send_to_socket函数发送一些无关数据来实现触发。
但遗憾的是一开始因为代码耦合性过强,暂时没想到该怎么实现,因此我用第二种方式处理:
1-VENC_SaveStream()判断H264帧和MJPEG帧不对等:
int t_mjpegErrorCounter=0;
HI_S32 VENC_SaveStream(int chn_index, PAYLOAD_TYPE_E enType, VENC_STREAM_S *pstStream) {
// printf("Chn: %d VENC_SaveStream %d packs: %d\n", chn_index, enType, pstStream->u32PackCount, pstStream);
HI_S32 s32Ret;
if (PT_H264 == enType) { s32Ret = VENC_SaveH264(chn_index, pstStream); t_mjpegErrorCounter++;}
else if (PT_MJPEG == enType) { s32Ret = VENC_SaveMJpeg(chn_index, pstStream); t_mjpegErrorCounter=0;}
else if (PT_JPEG == enType) { s32Ret = VENC_SaveJpeg(chn_index, pstStream); }
// else if (PT_H265 == enType) { s32Ret = SAMPLE_COMM_VENC_SaveH265(pFd, pstStream); }
else { return HI_FAILURE; }
if(t_mjpegErrorCounter>=100){
printf("mjpeg error\n");
sleep(1);
FreeAllMjpegClient();
g_resetChannalFlag = true;
g_DestoryErrorFlag = true;
t_mjpegErrorCounter = 0;
// restart_channal();
}
return s32Ret;
}
1-这片代码运行在【VENC_GetVencStreamProc】子线程里
2-睡眠1s是必须的,得让【server_thread】子线程里面向所有socket发送完本次的图像缓冲
3-FreeAllMjpegClient关掉所有客户端socket
4-g_resetChannalFlag置位标志这个循环重新到【VENC_GetVencStreamProc】子线程循环开始时会重新
5-其中g_DestoryErrorFlag是新设计的标志位,代表严重错误出现,全部mjpeg的客户端断连。
void FreeAllMjpegClient()
{
printf("~>free all client :\n");
for (uint32_t i = 0; i < MAX_CLIENTS; ++i)
{
if (client_fds[i].socket_fd < 0)
continue;
if (client_fds[i].type != STREAM_MJPEG)
continue;
free_client(i);
}
printf("\n");
}
在【server_thread】子线程的循坏一开始:
while (keepRunning) {
// waiting for a new connection
int client_fd = accept(server_fd, NULL, NULL);
printf("=>new socket\n\taccept:%d\t",client_fd);
if (client_fd == -1) break;
if(g_DestoryErrorFlag == true)
{
// sleep(3);
g_DestoryErrorFlag = false;
while(g_resetChannalFlag==true);
// g_DestoryErrorFlag == false;
// goto _FOUROFOUR;
}
……
其中下面那个while的作用是等待mjpeg通道重启完成,避免再次出现通道错误而进行阻塞。
执行文件及源码:
minihttp_http+mjpeg优化版本V200510.zip
(174.88 KB, 下载次数: 1)