福州大学《嵌入式系统综合设计》实验三:多媒体开发基础编程

本文主要是介绍福州大学《嵌入式系统综合设计》实验三:多媒体开发基础编程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、实验目的

本实验基于搭建好的开发环境和硬件环境,通过编写简单的通信实验,验证开发环境,掌握多媒体开发编程基础,包括SOCKET编程、多线程编程和线程同步知识。

二、实验内容

基于套接字、多线程、同步锁机制实现多媒体文件的收发;

发送端Ubuntu的PC机读取文件,每1024个字节组成一个包通过TCP报文发送到接收端;接收SE5上启动2个线程,线程1接收报文并将报文存入缓存;线程2通过缓存读取报文存入文件中;要求线程1和线程2之间通过同步锁进行线程同步。

三、开发环境

开发主机:Ubuntu 22.04 LTS

硬件:算能SE5

本地如果有SE5硬件,则可以PC机作为客户端,SE5作为服务器端。本地如果没有SE5硬件,只有云空间,则可以直接将客户端和服务器端都通过云空间实现,机在云空间的SE5模拟环境中实现。

四、实验器材

开发主机 + 云平台(或SE5硬件)

五、实验过程与结论

5.1 原理流程

硬件部署环境如下图所示:

如上图所示,可以利用PC作为客户端,SE5作为服务器端,将PC机的文件传送至SE5中。如果是云平台开发,可以直接将客户端和服务器端都放在云平台的模拟器中。此时,即在一台机器内既实现客户端也实现服务器端,设置服务器端的通信地址为回环地址(127.0.0.1)。

客户端程序采用TCP协议进行文件收发。客户端程序采用单线程处理,在和服务器端建立连接后,循环读取流媒体文件,并进行套接字发送。客户端运行流程包含了:

  1. 创建套接字
  2. 输入执行文件名,传输文件名,服务器地址和端口四个参数
  3. 连接服务器的ip地址及端口
  4. 读取需要发送的媒体文件
  5. 启动TCP发送文件,
  6. 循环读取流媒体文件,直到结束后断开连接。

3-1 客户端操作流程图

接收端作为服务端采用多线程进行编程。主线程用于接收连接后接收客户端发送的报文存入缓存。另起一个线程用于从缓存中读取数据包并存入文件中。服务器端的运行流程包含如下关键步骤:

  1. 创建套接字描述符
  2. 绑定ip地址和端口便于客户端接入
  3. 监听是否有客户端发出连接请求
  4. 收到连接请求后启动接收和写文线程
  5. 将接受的报文存入缓存中,同时从缓存读取报文存入文件中
  6. 传输完成后重新等待连接请求。

3-2 服务端操作流程图

5.2 关键代码解析
5.2.1 客户端

由于需要用到套接字进行编程,因此在头文件上需要包含一些必要的头文件:

#include <iostream>       
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>

创建套接字,可以直接利用操作系统的SOCKET接口实现,关键函数如下:

int sockfd;
struct sockaddr_in servaddr;if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)   //创建套接字并判断是否成功
{printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);return 0;
}memset(&servaddr, 0, sizeof(servaddr));    //初始化结构体
servaddr.sin_family = AF_INET;             //设置地址家族
servaddr.sin_port = htons(atoi(argv[3]));  //设置端口//发出连接请求判断是否连接成功
if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)        
{printf("connect error: %s(errno: %d)\n", strerror(errno), errno);return 0;
}

至此,客户端主动向服务器发送链接。

在发送端可通过fopen打开文件,通过fread函数读取流媒体文件:   

if ((fq = fopen(argv[1], "rb")) == NULL){/*判断文件是否打开*/close(serverFd);return -1;}
......
/*循环读取文件并发送*/
size_t readLen = fread(buffer, 1, sizeof(buffer), fq);

发送端启动TCP发送,这里的write函数中调用的sockfd是套接字的句柄:

while (!feof(fq)){/*循环读取文件并发送*/size_t readLen = fread(buffer, 1, sizeof(buffer), fq);if (readLen != write(serverFd, buffer, readLen)){printf("write error.\n");break;}
}
5.2.2 服务器端

服务器端由于涉及到多线程,因此需要包含多线程头文件。并且服务器端还涉及到缓冲区,本实例可以通过队列方法设计缓冲区,因此可以包含队列头文件。还有涉及到同步锁机制,因此还需要包含同步锁头文件,具体如下:

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <unistd.h>
#include <thread>
#include <mutex>
#include <queue>

服务器端首先也需要创建套接字,并等待客户端发起连接,服务器端的关键代码如下:

int main(int argc, char **argv)
{int listenFd, clientFd;struct sockaddr_in servaddr;if ((listenFd = socket(AF_INET, SOCK_STREAM, 0)) < 0){/*创建套接字*/printf("create socket error\n");return -1;}memset(&servaddr, 0, sizeof(servaddr));                     //初始化结构体servaddr.sin_family = AF_INET;                              //设置地址族协议servaddr.sin_addr.s_addr = htonl(INADDR_ANY);               //设置地址servaddr.sin_port = htons(6666);                            //设置默认端口if (bind(listenFd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){/*绑定套接字地址和端口*/printf("bind socket error\n");return -1;}if (listen(listenFd, 10) < 0){/*开启监听*/printf("listen socket error\n");return -1;}struct sockaddr_in client_addr;socklen_t size = sizeof(client_addr);if ((clientFd = accept(listenFd, (struct sockaddr *)&client_addr, &size)) < 0){/*建立连接*/printf("accept socket error\n");return -1;}std::thread write_thread(writeThread);size_t readLen = 0;while (true){/*循环读取客户端消息*/char buff[MAXBUFF] = {0};readLen = read(clientFd, buff, MAXBUFF);if (readLen <= 0)break;std::string data(buff, readLen);g_mx.lock();                                        //上锁g_dataQue.push(data);g_mx.unlock();                                      //解锁}write_thread.join();close(clientFd);close(listenFd);return 0;
}

注意,在上述函数中定义了写文件线程:

std::thread write_thread(writeThread);

并且在主线程中启动了写文件线程:

write_thread.join();

接收线程执行函数:

std::queue<std::string> g_dataQue;                   //全局队列
std::mutex g_mx;                                     //互斥锁void writeThread()
{/*写线程*/FILE *out_put = fopen("recv_data.mp4", "w+");sleep(1);                                        //休眠一秒,确保队列中有数据while (true){/*从队列中读取数据并存储*/if (g_dataQue.size() == 0)break;g_mx.lock();std::string data = g_dataQue.front();g_dataQue.pop();g_mx.unlock();fwrite((void *)data.data(), 1, data.size(), out_put);}fclose(out_put);
}

如上所示,同步锁用于进行缓冲区的读写同步。上述实例中通过std::mutex实现同步。

g_lock.lock();         //上锁
​​​​​​​g_lock.unlock();       //解锁

这篇关于福州大学《嵌入式系统综合设计》实验三:多媒体开发基础编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/404883

相关文章

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

CSS3 最强二维布局系统之Grid 网格布局

《CSS3最强二维布局系统之Grid网格布局》CS3的Grid网格布局是目前最强的二维布局系统,可以同时对列和行进行处理,将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,本文介... 深入学习 css3 目前最强大的布局系统 Grid 网格布局Grid 网格布局的基本认识Grid 网

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

CentOS系统Maven安装教程分享

《CentOS系统Maven安装教程分享》本文介绍了如何在CentOS系统中安装Maven,并提供了一个简单的实际应用案例,安装Maven需要先安装Java和设置环境变量,Maven可以自动管理项目的... 目录准备工作下载并安装Maven常见问题及解决方法实际应用案例总结Maven是一个流行的项目管理工具

基于Python开发PPTX压缩工具

《基于Python开发PPTX压缩工具》在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,不便于传输和存储,所以本文将使用Python开发一个PPTX压缩工具,需要的可以了解下... 目录引言全部代码环境准备代码结构代码实现运行结果引言在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.