基于内存缓冲区的流媒体数据缓存排序(二)

C++ 实验
基于内存缓冲区的流媒体数据缓存排序(二)

要求:

针对一个流媒体节目的单线程下载进行处理;
节目数据包 无丢失、有乱序 ,假设乱序范围不超过10个数据包;
将收到的数据包直接写入缓冲区,缓冲区长度无限定;
排好序的数据如果超过一定长度,如30KB(可设定),则输出到文件;

实现要求
缓冲区类    

定义基于动态内存分配的缓冲区类,包括如下

数据成员:

•缓冲区,用于缓存数据(按偏移量写入,完成排序);
•记录缓冲区内收到的数据总量;
•缓冲区中数据在节目(文件)中的偏移量(缺了这项信息,无法写入文件);
•其他必要信息。
成员函数:
•构造函数、析构函数
•数据接收函数
•数据输出函数
测试程序

模拟网络流媒体的数据流到达:

•设定数据来源多媒体文件(本地磁盘);
•从多媒体文件读取一块数据(长度不超多1500B),模拟网络数据包;
•模拟的网络数据包:包括节目ID、数据包偏移量、数据包长度;
•将数据包发送给排序缓存类。
•将文件的所有数据按上述方式读入并发送给排序缓存类
•最好用类实现(不要求)

提示:用随机数生成每个数据包的长度:256~1500 Bytes


</pre><pre name="code" class="cpp">/*StreamBuffer.h*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>

using namespace std;

class StreamBuffer
{
public:
	StreamBuffer();
    StreamBuffer(int iLen);
	int ReceiveData(unsigned int offset, unsigned int bytes, char *pData); //接收数据包。
	int ContinueBytes(unsigned int &iDataOffset, char* &pData);	//统计缓冲区中排好序的数据的信息。
	int RemoveData(int iBytes); //释放缓冲区。
	~StreamBuffer();

private:
    char m_pData[30*1500];	//存数据的buffer,也可以用指针。
	unsigned int m_PacketOffset[20]; //存传入数据包的偏移量。
	unsigned int m_PacketBytes[20];	//存传入数据包的数据量(长度)。
	char m_PacketData[20][1500]; //存传入数据包的数据。
	int inPacketOffset; //写入缓冲区的数据的偏移量(标记下次位置)。
	int m_iPacketsNum; //存储数据包的数组下标(每20个一循环)。
	int outPackerOffset; // 缓冲区中排好序的数据块中第一个字节的偏移量数值.
    int m_iBufferLen;  //缓冲区中排好序的数据的字节数(长度)。
};


/*StreamBuffer.cpp*/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>

#include "StreamBuffer.h"

using namespace std;

StreamBuffer::StreamBuffer()
{
	m_iBufferLen = 0;
	m_iPacketsNum = 0;
    <span style="white-space:pre">	</span>m_iBufferLen = 0;
    <span style="white-space:pre">	</span>outPackerOffset = 0;
    <span style="white-space:pre">	</span>inPacketOffset = 0;
}

StreamBuffer::StreamBuffer(int iLen)
{
	m_iBufferLen = iLen;
	m_iPacketsNum = iLen;
    <span style="white-space:pre">	</span>m_iBufferLen = iLen;
    <span style="white-space:pre">	</span>outPackerOffset = iLen;
}

int StreamBuffer::ReceiveData(unsigned int offset, unsigned int bytes, char *pData)
{
	int iBytes, i, j;
	cout << "bytes = " << bytes << endl;


	m_PacketOffset[m_iPacketsNum] = offset; //记录传入的每个数据包的偏移量。
	m_PacketBytes[m_iPacketsNum] = bytes;  //记录传入的每个数据包的长度。

	// 存储每个数据包的数据。
	for(iBytes = 0; iBytes < abs(bytes); iBytes++)
	{
		m_PacketData[m_iPacketsNum][iBytes] = *(pData++);
	}

	m_iPacketsNum++;

   	//遍历每个数据包的偏移量信息,如果符合排序要求,就进行排序(即写入缓冲区)。
	for(i = 0; i < m_iPacketsNum; i++)
	{
		if(m_PacketOffset[i] == abs(inPacketOffset))
		{
			inPacketOffset += m_PacketBytes[i];
			for(j = 0; j < abs(m_PacketBytes[i]); j++)
			{
				m_pData[m_iBufferLen++] = m_PacketData[i][j];
			}
			i = 0;
		}
	}

	if(m_iPacketsNum == 20) //当接收了20个数据包后,清零,开始下一次的接收。
	{
		m_iPacketsNum = 0;
	}
	return iBytes;
}

int StreamBuffer::ContinueBytes(unsigned int &iDataOffset, char* &pData)
{
	//返回缓冲区中,排好序的数据的长度(单位字节数)。并通过引用参数返回如下信息
	//iDataOffset: 排好序的数据块中第一个字节的偏移量数值
	//pData:数据指针
	int iContinueBytes = 0;

	pData = m_pData;
	iDataOffset = outPackerOffset;
	iContinueBytes = m_iBufferLen;

	return iContinueBytes;
}

int StreamBuffer::RemoveData(int iBytes)
{
	//从缓冲区中把数据"删除",返回删除的字节数
	int iBytesRemoved=0, i;

	outPackerOffset += iBytes;
	m_iBufferLen = 100;

	for(i = 0; i < 100; i++)
	{
		m_pData[i] = m_pData[iBytes+i];
	}

	return iBytesRemoved;
}

StreamBuffer::~StreamBuffer()
{
	if(NULL != m_pData)
	{
		delete []m_pData;
	}
}

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>

#include "StreamBuffer.cpp"

using namespace std;

void GenDisOrder(int iSendOrder[],int iPacketNum);

int main()
{
	//该部分用于打开读写文件。
	FILE* fpSrcFile = NULL;
	FILE* fpDstFile = NULL;

	char srcfileName[500] = "TestFile//song1.mp3";
	char dstfileName[500] = "TestFile//result.mp3";

	fpSrcFile = fopen(srcfileName, "rb");
	if( fpSrcFile == NULL )
	{
	  cout<<"Cann't open file: "<<srcfileName<<endl;
	  return 1;
	}

	fpDstFile = fopen(dstfileName, "wb");
	if( fpDstFile == NULL )
	{
	  cout<<"Cann't create file: "<< dstfileName <<endl;
	  return 2;
	}


	const int MTU =  1500;	//最大传输单元,网络术语,表示一个数据包大最大尺寸,单位:字节
	int iReadBytes = 0;
	int iWantReadBytes;
    int iContinueBytes;
    int iUseBytes;
	unsigned int iOutDataOffset;
	char *pOutData;

	StreamBuffer MyBuffer; //用排序类创建对象。

	const int iMaxPacketNum = 20;  //每次读入20个数据包,然后以乱序的形式发给排序模块(StreamBuffer类)
	int iSendOrder[iMaxPacketNum]; //记录下发数据包的顺序
	unsigned int iPacketOffset[iMaxPacketNum]; //记录每个数据包中第一个字节数据的偏移量
	unsigned int iPacketLen[iMaxPacketNum]; //记录每个数据包中的数据长度
	char (*pDataBuf)[MTU]; //数据包缓冲区区。

	int iPacketNum;
	int i;
	int iPackNo;

    srand(100);//用固定值初始化,会生成固定的随机数序列,方便测试程序,否则用srand( (unsigned)time( NULL ) );
	pDataBuf = new char[iMaxPacketNum][MTU];

	iWantReadBytes = 1024;

   	do
	{
        iPacketNum = 0;
        for(i = 0; i < iMaxPacketNum; i++)	//初始化数据包长度为0,表示没有读入数据
        {
           iPacketLen[i] = 0;
 		}

		do
		{
			iPacketOffset[iPacketNum] = ftell(fpSrcFile); //获取文件的偏移量。
			iReadBytes = fread(pDataBuf[iPacketNum], 1, iWantReadBytes, fpSrcFile);
            iPacketLen[iPacketNum] = iReadBytes; //当前数据包读取成功,记录数据包长度,否则依旧是0
            iWantReadBytes = (iPacketOffset[iPacketNum] + iPacketNum * iPacketNum) % 500 + 400; //下一个数据包读取长度
            iPacketNum++;

        }
        while((iReadBytes > 0) && (iPacketNum < iMaxPacketNum));
        //读入一组数据包,如果文件结束:iReadBytes<1

        GenDisOrder(iSendOrder, iMaxPacketNum); //利用函数GenDisOrder对已经产生的20个数据包打乱顺序。

        //把刚刚已经读入一组数据包,乱序下发给排序模块
        for(i = 0; i < iMaxPacketNum; i++)	//只要长度不为0,就发给排序模块
        {
            iPackNo = iSendOrder[i];
            if(iPacketLen[iPackNo] > 0)//有数据,给给排序模块
            {
				MyBuffer.ReceiveData(iPacketOffset[iPackNo], iPacketLen[iPackNo], pDataBuf[iPackNo]);
                iContinueBytes = MyBuffer.ContinueBytes(iOutDataOffset, pOutData);
                iUseBytes = iContinueBytes - 100; //假设用了一部分

                if( iContinueBytes > (20*1024)) //缓冲区数据只要大于20 KB就写入到文件中。
                {
                   fseek(fpDstFile, iOutDataOffset, SEEK_SET);
                   fwrite(pOutData, iUseBytes, 1, fpDstFile);
                   MyBuffer.RemoveData(iUseBytes);
                }
            }
        }
   	}
   	while(iReadBytes > 0);//文件还没读取完。


	//输入结束,把缓冲区中剩余排好序的数据取出
    iContinueBytes = MyBuffer.ContinueBytes(iOutDataOffset, pOutData);
	if(iContinueBytes > 0)
	{
		fseek(fpDstFile, iOutDataOffset, SEEK_SET);
		fwrite(pOutData, iContinueBytes, 1, fpDstFile);
	}

    fclose(fpDstFile);
    fclose(fpSrcFile);

	delete []pDataBuf;

    return 0;
}


void GenDisOrder(int iSendOrder[], int iPacketNum)
{
    int i, j, k, n, temp;

    for(i = 0; i < iPacketNum; i++)//先产生顺序的序列:0,1,2,.....iPacketNum-1
    {
    	iSendOrder[i] = i;
    }

	if(iPacketNum < 5)
	{
		return;
	}

    n = rand() % (iPacketNum / 5) + 1; //置乱的次数,最多20%*2个数据包

   	for(i = 0; i < n; i++)
   	{
   		//交换j、k两个数据包的顺序
        j = rand() % (iPacketNum / 2) + 1;
        k = rand() % (iPacketNum - j);
        temp = iSendOrder[j];
        iSendOrder[j] = iSendOrder[k];
        iSendOrder[k] = temp;
   	}
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值