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

一、实验目的

本实验基于搭建好的开发环境和硬件环境,通过编写简单的通信实验,验证开发环境,掌握多媒体开发编程基础,包括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.mfbz.cn/a/175374.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++语言核心及进阶

核心编程 内存分区模型 根据c执行将内存划分为5个区域&#xff1a; 代码区&#xff0c;存放函数体的二进制&#xff0c;即CPU执行的机器指令&#xff0c;并且是只读的&#xff1b;常量区&#xff0c;存放常量&#xff0c;即程序运行期间不能被改变的量。全局区&#xff08;静…

汇编-PUSHFD和POPFD标志寄存器值压栈和出栈

PUSHFD指令将32位EFLAGS寄存器内容压入堆栈&#xff0c; 而POPFD指令则将栈顶单元内容弹出到EFLAGS寄存器 格式&#xff1a;

finalshell上传文件失败的解决方法

问题描述 最近使用虚拟机docker部署项目&#xff0c;使用的finalshell远程连接虚拟机&#xff0c;然后奇怪的发现无法往里面传输文件&#xff0c;各种传输方式&#xff08;手动or直接拉拽&#xff09;都不行&#xff0c;直接报错如下&#xff1a; 原因分析 一开始我以为是新…

kibana 7安装

手动安装 下载 wget https://artifacts.elastic.co/downloads/kibana/kibana-7.17.15-linux-x86_64.tar.gz 解压 mv kibana-7.17.15-linux-x86_64.tar.gz /usr/local tar -zxvf kibana-7.17.15-linux-x86_64.tar.gz chown -R es:es kibana-7.17.15-linux-x86_64修改配置 s…

【HarmonyOS】获取备案需要的公钥和MD5签名问题

【关键字】 HarmonyOS、公钥、MD5签名 【问题描述】 有cp反馈Harmony App如何获取备案需要的公钥和MD5签名。 【解决方案】 1、获取备案需要的公钥&#xff1a; 1&#xff09;用记事本打开签名对应.cer文件 2&#xff09;里面的内容粘贴到一个可以查看证书的网站上面&…

23设计模式详解「全23种」

✍️作者简介&#xff1a;码农小北&#xff08;专注于Android、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a; 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&#x1f449;关注✨、点赞&…

【idea】解决idea 执行maven build总下载 Downloading maven-metadata.xml文件

可以看到如下日志中打印了执行的命令行&#xff0c;其中包含 --update-snapshots&#xff0c;是强制更新的意思。 日志内容如下&#xff1a; D:\env\jdk1.8.0_261\bin\java.exe --update-snapshots -s D:\env\apache-maven-3.8.6\conf\settings.xml -Dmaven.repo.localD:\env\…

苹果Siri怎么打开?教你两招轻松唤醒!

苹果Siri助手是苹果公司开发的智能语音助手。作为智能语音助手&#xff0c;Siri可以理解用户的指令&#xff0c;并给出相应的回答或执行相应的操作&#xff0c;帮助大家完成各种任务&#xff0c;比如发送短信、查询天气、播放音乐、设置提醒等等。 然而&#xff0c;还有一些小…

【软件工程师从0到1】- 继承 (知识汇总)

前言 介绍&#xff1a;大家好啊&#xff0c;我是hitzaki辰。 社区&#xff1a;&#xff08;完全免费、欢迎加入&#xff09;日常打卡、学习交流、资源共享的知识星球。 自媒体&#xff1a;我会在b站/抖音更新视频讲解 或 一些纯技术外的分享&#xff0c;账号同名&#xff1a;hi…

React函数组件渲染两次

渲染两次是因为react默认开启了严格模式 React.StrictMode标签作用&#xff1a; 1、识别不安全的生命周期 2、关于使用过时字符串 ref API 的警告 3、关于使用废弃的 findDOMNode 方法的警告 4、检测意外的副作用 5、检测过时的 context API 注释掉React.StrictMode即为关闭严…

webpack external 详解

作用&#xff1a;打包时将依赖独立出来&#xff0c;在运行时&#xff08;runtime&#xff09;再从外部获取这些扩展依赖&#xff0c;目的时解决打包文件过大的问题。 使用方法&#xff1a; 附上代码块 config.set(externals, {vue: Vue,vue-router: VueRouter,axios: axios,an…

springboot+jsp+bootstrap+java问卷调查系统

系统功能需求包含业务需求和功能需求&#xff0c;系统功能需求分析是在了解用户习惯、开发人员技术和实力等各个因素的前提下&#xff0c;对其进行深入分析&#xff0c;了解系统基本需求后&#xff0c;基本功能如下&#xff1a; 本课题要求实现优质的问卷调查系统&#xff0c;就…

JavaScript中的设计模式

目录 1.工厂模式 2.构造函数模式 构造函数中new关键字作用&#xff1a; 3.原型模式 4.组合模式 JavaScript中有四种常见的设计模式 &#xff0c;目的是为了批量创建对象 为了实现继承。 1.工厂模式 利用工厂模式批量创建对象 优点&#xff1a;可以批量创建对象 …

苹果录音删除了可以恢复吗?请收藏好这些方法!

录音可以用于记录重要的信息&#xff0c;比如会议记录、课堂讲解、创作灵感等等。通过录制好的音频&#xff0c;我们可以随时回放这些录音&#xff0c;以便后续参考和使用。 但难免会发生意外&#xff0c;我们有时候可能会不小心删除手机中的录音文件。苹果录音删除了可以恢复…

bootstarp+springboot基于Java的教学仪器设备商城销售网站_o9b00

1、商品分类功能 商品分类是教学仪器设备销售网站中十分重要的一部分&#xff0c;它能够提高用户在网站上的浏览速度&#xff0c;并方便用户快速找到自己需要的商品。因此&#xff0c;需要对该功能进行分析和设计&#xff0c;确保其体验性、可用性和易用性。可以将商品分为多个…

如何配置ESB单据集成接口

ESB企业服务总线在实际项目中主要用于各业务系统之间的集成&#xff0c;集成包括数据集成、应用集成以及业务单据集成等&#xff0c;ESB企业服务总线主要包含三部分&#xff1a;ESB设计器、SMC管理控制台以及Server运行环境&#xff0c;ESB设计器用于服务以及集成流程的开发&am…

04-React脚手架 集成Axios

初始化React脚手架 前期准备 1.脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目 1.包含了所有需要的配置&#xff08;语法检查、jsx编译、devServer…&#xff09;2.下载好了所有相关的依赖3.可以直接运行一个简单效果 2.react提供了一个用于创建react项目的脚手架库…

python -opencv形态学操作

python -opencv形态学操作 1.服饰和膨胀 1.服饰和膨胀 opencv 腐蚀通过cv2.erode实现&#xff0c;膨胀通过cv2.dilate实现&#xff0c;看一下下面代码&#xff1a; from ctypes.wintypes import SIZE from multiprocessing.pool import IMapUnorderedIterator import cv2 i…

springboot+bootstarp+jsp房屋租赁系统ssm_t65a9

小型房屋租赁系统主要有管理员、房东和租户三个功能模块。以下将对这三个功能的作用进行详情的剖析。 管理员模块&#xff1a;管理员是系统中的核心用户&#xff0c;管理员登录后&#xff0c;可以对后台系统进行管理。主要功能有个人中心、房东管理、租户管理、房源城市管理、房…