面包屑图标 当前位置: 首页
AI资讯
热点详情

HTTP网页服务器与UDP上位机实现MJPG码流传输

AI热点日报
AI热点日报时间:2026-07-06
热点解读

在远程监控领域,MJPG码流传输虽然是一个经典话题,但实际落地时往往需要掌握一些关键技巧。最常见的两种实现方案——基于浏览器的HTTP网页服务器以及基于UDP的上位机,各有特点。本文以飞凌嵌入式OKMX8MP-C开发板为例,系统环境为Linux5 4 70+Qt5 15 0,详细梳理这两种MJPG流

在远程监控领域,MJPG码流传输虽然是一个经典话题,但实际落地时往往需要掌握一些关键技巧。最常见的两种实现方案——基于浏览器的HTTP网页服务器以及基于UDP的上位机,各有特点。本文以飞凌嵌入式OKMX8MP-C开发板为例,系统环境为Linux5.4.70+Qt5.15.0,详细梳理这两种MJPG流传输方案的完整实现流程。

基于HTTP网页服务器和UDP上位机的MJPG码流传输

先来看两种方式的差异。UDP上位机传输效率较高,代码编写也比较顺手,但客户端必须安装特定软件,跨平台兼容性是个难题。HTTP网页服务器则恰恰相反——客户端仅需一个浏览器,无论电脑、手机、平板,还是不同操作系统,都能轻松访问,省去了软件安装的麻烦。不过HTTP传输效率相对偏低,而且服务器端需要处理更复杂的HTTP协议。两种方案各有短板,对于嵌入式开发者来说,两者都需要熟练掌握。

一、HTTP网页服务器

首先拆解HTTP方式的实现。开发板端需要完成的首要任务是建立TCP服务器。参考下面这段代码:

int TCP_Server_Found(socklen_t* socket_found , char* ip , int port) {
struct sockaddr_in servaddr;
socklen_t addrsize = sizeof(struct sockaddr);
bzero(&servaddr , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(ip);
servaddr.sin_port = htons(port);
int ret;
IF( (*socket_found = socket(AF_INET , SOCK_STREAM , 0)) == -1)
{
printf("Create socket error: %s (errno :%d)n",strerror(errno),errno);
return -1;
}
int on = 1;
if(setsockopt(*socket_found , SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
printf("setsockopt errorn");
}
ret = bind(*socket_found , (struct sockaddr *)&servaddr , addrsize);
if(ret == -1)
{
printf("Tcp bind faiLED!n");
return -1;
}
if(listen(*socket_found , 5) == -1)
{
printf("Listen failed!n");
return -1;
}
return 0;
}

这里有一个实用小细节:setsockopt()并非强制要求,主要用来规避套接字建立时可能出现的常见异常。TCP服务器建立完成后,返回的socklen_t类型实参在后续流程中会用到。

接下来,HTTP服务器的TCP操作需要单独开启一个轮询线程,以便客户端发起握手。注意listen()只需执行一次,而accept()recv()必须放在死循环中:

pthread_create(&tid_tcp_web_recv , NULL , Thread_TCP_Web_Recv , NULL);

void * Thread_TCP_Web_Recv(void *arg) {
。。。
while(1) {
fd_socket_conn = accept(socket_web_server , (struct sockaddr *)&sockaddr_in_conn , &addrsize);
printf("fd_socket_conn = accept()n");
。。。
recv(fd_socket_conn , recvbuf , 1000 , 0);
}
。。。
}

MJPG帧通过Grab操作获取。这里有一个关键点:帧数据在Grab线程中写入,在TCP线程中读取。多线程同时访问共享资源,不加锁必然导致数据错乱。因此每次写入时必须先加锁,写完后及时解锁:

pthread_mutex_lock(&pmt);
pic_tmpbuffer = pic.tmpbuffer;
pic.tmpbytesused = buff.bytesused;
pic_tmpbytesused = pic.tmpbytesused;
pthread_cond_broadcast(&pct);
pthread_mutex_unlock(&pmt);

互斥锁和条件变量在使用前需要先初始化:

pthread_mutex_t pmt;
pthread_cond_t pct;

int main(int argc, char* argv[]) {
...
TCP_Server_Found(&socket_web_server , (char*)argv[2] , PORT_TCP);
pthread_mutex_init(&pmt , NULL);
pthread_create(&tid_tcp_web_recv , NULL , Thread_TCP_Web_Recv , NULL);
pthread_create(&tid_tcp_web_send , NULL , Thread_TCP_Web_Send , NULL);
...
while(1)
{
V4l2_Grab_Mjpeg(false , MJPEG_FILE_NAME);
...
}
...
}

发送环节也有讲究。在发送图片数据之前,必须先发送HTTP标准头部,这相当于为后续数据传输铺好道路:

#define STD_HEADER "Connection: closern"
"Server: MJPG-Streamer/0.2rn"
"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0rn"
"Pragma: no-cachern"
"Expires: Mon, 3 Jan 2000 12:34:56 GMTrn"
#define BOUNDARY "boundarydonotcross"

printf("preparing headern");
sprintf(buffer, "HTTP/1.0 200 OKrn"
"Access-Control-Allow-Origin: *rn"
STD_HEADER
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "rn"
"rn"
"--" BOUNDARY "rn");
if(write(fd, buffer, strlen(buffer)) < 0)
{
free(frame);
return;
}

标准头部发送完毕后,紧接着发送内容头部,其中Content-Type设置为image/jpeg。需要注意的是,HTTP协议中图片类型并不只有这一种。内容头部之后跟着图片数据以及boundary结尾标记,这样一帧完整的HTTP数据包就组装完毕,浏览器即可正确显示该图片:

sprintf(buffer, "Content-Type: image/jpegrn"
"Content-Length: %drn"
"X-Timestamp: %d.%06drn"
"rn", frame_size, (int)timestamp.tv_sec, (int)timestamp.tv_usec);
printf("sending intemdiate headern");
if(write(fd, buffer, strlen(buffer)) < 0)
break;
printf("sending framen");
if(write(fd, frame, frame_size) < 0)
break;
printf("sending boundaryn");
sprintf(buffer, "rn--" BOUNDARY "rn");
if(write(fd, buffer, strlen(buffer)) < 0)
break;

需要说明的是,TCP服务器发送MJPG流时处于死循环状态,客户端在发送GET指令后将持续接收图片数据。此时客户端相当于忙等待,无法再发送其他指令。从用户视角看,网页会一直显示加载状态——这其实是正常的运行现象。

二、UDP上位机

UDP方式的实现则简洁得多。首先创建一个UDP Socket:

int UDP_Send_Found(socklen_t* socket_found , struct sockaddr_in *addr , char* ip , int port) {
*socket_found = socket(AF_INET, SOCK_DGRAM, 0);
if(*socket_found == (~0))
{
printf("Create udp send socket failed!n");
return -1;
}
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = inet_addr(ip);
addr->sin_port = htons(port);
memset(addr->sin_zero, 0, 8);
return 0;
}

UDP方式发送文件比HTTP省事很多——只需将文件按固定长度切分成帧,逐帧发送即可:

while(fend > 0) {
memset(picture.data , 0 , sizeof(picture.data));
fread(picture.data , UDP_FRAME_LEN , 1, fp);
if(fend >= UDP_FRAME_LEN) {
picture.length = UDP_FRAME_LEN;
picture.fin = 0;
} else {
picture.length = fend;
picture.fin = 1;
}
//printf("sendbytes = %d n",sendbytes);
sendbytes = sendto(socket_send, (char *)&picture, sizeof(struct Package), 0, (struct sockaddr*)&addr,addr_len);
if(sendbytes == -1) {
printf("Send Picture Failed!dn");
return -1;
} else {
fend -= UDP_FRAME_LEN;
}
}

至此,两种方式的核心代码与实现思路均已清晰呈现。HTTP方式需要处理复杂的HTTP协议头部以及多线程同步问题,而UDP方式则更加简洁高效。在实际项目中究竟选择哪种方案,取决于你的应用场景:如果对跨平台移植性要求较高,建议优先考虑HTTP网页服务器;如果传输效率是主要瓶颈,那么UDP上位机无疑是更合适的选择。

热点追踪提示词
你是一名 AI 行业编辑,请围绕下面这条热点输出一份资讯解读:
热点:HTTP网页服务器与UDP上位机实现MJPG码流传输要求:
1. 先用一句话解释这条热点在讲什么
2. 再总结它为什么重要
3. 说明会影响哪些 AI 产品或内容方向
4. 最后给出 3 个适合资讯站使用的标题
来源:https://m.elecfans.com/article/1846384.html
服务器

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关热点
AI热点2026-07-06 20:47
百度官方出品度加剪辑口播自媒体必备工具

度加剪辑是百度官方出品的剪辑工具,面向口播自媒体创作者。支持视频剪辑、智能识别字幕,并与百度网盘打通,可快速导入素材。适用于泛知识类创作者制作高质量视频,覆盖从素材导入到成品输出的完整流程。

AI热点2026-07-06 20:46
基于AI的智能在线个性化锻炼计划生成工具 Workout Master

WorkoutMaster是一款基于AI的个性化锻炼计划生成工具,能根据用户目标、偏好及历史训练记录,动态输出专属方案,并实时自适应调整负重、组次等参数。支持定制目标与器械偏好,借助机器学习持续优化,随时随地即可接入使用,确保训练高效安全。

AI热点2026-07-06 20:46
Calorielens AI智能实时拍照分析餐点照片卡路里追踪应用

Calorielens是一款利用AI分析餐食照片的卡路里追踪应用。用户只需拍照,AI即可自动识别食物种类和分量并估算卡路里,省去手动输入步骤。应用还提供历史记录追踪功能,帮助把握热量趋势。AI估算精度可满足日常健康管理需求。

AI热点2026-07-06 20:46
百度旗下首个AI互动式搜索APP简单搜索

百度旗下“简单搜索”AI搜索引擎集成语音、图像、多媒体搜索及实时翻译,支持多模态交互与智能推荐。基于大模型技术,用户可通过对话式交互直接获取精准答案,适用于学习、旅行、生活、职业发展等场景,高效满足信息需求。

延伸阅读