功能描述:服务端发送所有可以收听的频道列表,由客户端先接收频道列表,然后进行选择收听哪一个频道,实现多个频道同时收听,要采取并发,选用多进程或者多线程,在这里,我采用的是多线程,因为创建一个线程的开销较小,线程拥有独立的堆栈和局部变量,线程是共用地址空间的,共享进程数据;创建,销毁,切换简单; 占用内存小;cup利用率高。
实现:
-
线程池:有管理线程,工作线程,任务队列。
没有任务时,线程池中会有一定数量的空闲线程,不会被销毁。
管理线程是控制线程的创建和销毁,线程的数量不超过线程池中线程的最大容量,不少于线程池中线程的最小容量。线程池中线程是临界资源,需要互斥访问。
-
server端:媒体库和main函数
1. 需要创建套接字,服务端是主动端,bind可以省略,使能多播
2. 初始化线程池
3. 获得频道列表,将获得的频道列表发送到组播中(利用线程池,将发送频道列表的任务添加到线程池的任务队列中)
4. 将每个频道的数据(频道下的音频文件)发送到组播中(将发送频道数据的任务添加到线程池中的任务队列中)
- 媒体库:实现功能:得到频道列表和单个频道数据
遇到的问题:不知道如何通过频道id得到单个频道的数据
创建一个内部数据类型:(解析每个频道得到一个内部结构体)
struct chn_context_st
{
struct mlib_st mlib_list;//频道列表
//包含mp3_path.gl_pathv(频道下所有音频的路径)和mp3_path.gl_pathc(频道下含有的音频个数)
glob_t mp3_path;
int mp3_index;//代表读的是第几个音频文件
int mp3_fd;//当前第mp3_index + 1个音频文件的文件描述符
int mp3_pos;//读第mp3_index + 1个音频读到的位置
}
解决:在得到频道列表的时候,使用了chn_context_st类型的指针数组,将每一个频道解析出来的chn_context_st类型的结构体的首地址存储到指针数组中。有了内部结构体,就可以通过chnid得到每个频道下音频的信息,从而可以读取每个音频的数据。
-
clinet端:main函数
1. 创建套接字,客户端是被动端,所以要bind,加入多播组。
2. 创建父子进程,父进程选择收听的频道,从组播中读取要收听的频道的数据,写入管道中
3. 子进程从管道中读取频道数据,进行进程替换,使用mplayer播放器,播放音频文件
-
协议接口:定义频道列表和单个频道列表的数据类型(向组播中发送)
1. 协议中定义的数据类型是往组播中发送的时候需要遵循的,在组播中取到的数据也是协议中的类型,server向组播中传送频道列表和频道数据需要遵守,媒体库中获取频道列表和频道数据时不需要遵守协议中的数据类型。
2. 为了让客户端知道接收的数据是频道列表和频道数据,协议中定义了一个共用体结构。
-
流量控制模块
采用令牌桶来进行流量控制
流量控制有两种方式:漏桶和令牌桶
漏桶适合偶尔一次的并发,漏桶是处理的速率是恒定的,若漏桶出现溢出,则丢弃。令牌桶是以一定的速率产生令牌,令牌桶存储的令牌有上限,若请求的令牌数量 > 令牌桶中可用的令牌的数量,则丢弃,适合一定程度的突发事件,