# 网络收音机项目 **Repository Path**: Li_Dong_Yue/Network_Radio_Project ## Basic Information - **Project Name**: 网络收音机项目 - **Description**: 项目背景:用户可以通过菜单上的节目单来选择自己感兴趣的节目,进而收听到相应的节目信息 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2023-02-25 - **Last Updated**: 2023-07-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## **网络收音机** **项目介绍:** 客户端链接到组播内,收到服务端发送过来的频道列表,用户可以根据自己的喜好,选择什么频道,有儿童歌曲,流行音乐,京剧,脱口秀四个节目,在频道简介前会有一个频道id,在选择对应的频道id后收听对应的节目内容 **服务端介绍:** 在服务端存放着媒体库,媒体库中存放有儿童歌曲,流行音乐,京剧,脱口秀四个节目的具体内容 ![媒体库频道列表](C:\Users\LDY\Desktop\李东岳的项目\项目演示截图\网络收音机项目\媒体库频道列表.png) ![媒体库节目列表](C:\Users\LDY\Desktop\李东岳的项目\项目演示截图\网络收音机项目\媒体库节目列表.png) **媒体库接口模块**:通过对媒体库目录的分析来获取文件具体的路径地址,此模块封装了两个接口 发送节目单模块调用获取节目单内容接口,此接口会通过glob分析出上层路径kids,music,opera..,将这些路径以字符串形式存放在传参进来的结构体中,再次执行glob递进到每一个频道文件的内部,获取到descr的路径,打开此文件获取到次频道的简介内容,再回调给接口,此时接口函数获得频道的名和频道内部的说明 发送节目内容模块调用获取节目内容接口,此接口会通过传入的不同的频道id号进入到不同的频道目录内获取到对应的节目内容,并把内容存入传给此接口的数组内 **发送节目单模块**:首先创建一个进程,此进程只会不停的发送整合号的节目单列表这一结构体 通过调用媒体库发送节目单模块接口,将单个频道以结构体数组的方式传入,此时结构体数组内每一个数组成员存放不同的频道名和频道内容,将结构体数组内所有的单个频道整合在一个结构体中,通过套接字进行udp传输 **发送节目内容模块**:创建进程,主函数在执行时会进行四次调用,会创建四个进程在后台跑着 通过调用媒体库发送节目内容模块调用获取节目内容接口,将数组传入,获取到数据后,将数组内的节目数据传通过套接字发送给客户端 **主函数**首先调用发送节目单模块内的获取节目单内容函数=>传入单个节目单的结构体,再调用发送节目单的函数=>组织节目单并发送给客户端, 接着再循环调用四次发送节目内容某块,每一次都会创建一个进程去循环发送每个频道内的节目内容 **客户端介绍:** 客户端通过双方约定的协议加入到组播内,首先进行接收发送节目单模块发送过来的组织好的节目单结构体,将其打印出来,然后用户选择频道,选择频道后,创建一个管道,创建一个子进程,子进程执行exec替换为myplayer的进程,父进程将从服务端接收到的内容数据写入管道中,子进程将管道的内容灌入myplayer内来进行播放音乐 **问题:** 1.本寻思是要在当频道一个节目播放完毕后,再播放下一节目,但是在跳转下一个节目过程中遇到了文件指针用完的问题,每次执行open去打开文件的内容时后开始很正常,读着读着就失败了,说文件指针用完。 解决方法:原因是我将open放在了一循环内,导致多次打开文件,后来将打开下一个节目单独封装在一个函数内,当这个节目播放完毕,调用open_next来打开下一个节目再播放下一个节目,就解决了 原因是之前写 2.媒体库在将两个接口合并在一起执行的时候,出现段错误,但是仅仅一个接口的时候就可以正常运行,后来发现是我初每次调用接口的时候有一个地方初始化了两次,导致段错误 解决方法:将两个初始化接口都整合在一块写,进行初始化就解决了 ### 协议: ~~~c **proto.h** ```c #ifndef __PROTO_H #define __PROTO_H #define GROUPADDR "235.2.2.2" #define RCVPORT 1234 #define MAXCHNNR 200 #define MINCHNID 1 #define MAXCHNID MAXCHNNR #define CHNLISTID 0 #define MAXMSG 512 // 频道列表 // 单个 struct entry_st { int8_t chnid; /*MINCHNID~MAXCHNID频道id号*/ int8_t len; // 频道介绍自述数据长度 char descr[1];//存放频道的介绍内容 }__attribute__((packed)); //整个列表结构 struct chnlist_st { int8_t chnid; /*MUST BE CHNLISTID频道id号*/ struct entry_st listEntry[1];//存放单个列表中全部整合在一块的内容 }__attribute__((packed)); // 频道数据 struct chndata_st { int8_t chnid; /*MINCHNID~MAXCHNNR频道id号*/ char data[1];//存放频道内节目的具体数据 }__attribute__((packed)); union msg_st { int8_t chnid; struct chnlist_st chnlist; struct chndata_st chndata; }; #endif ``` ~~~ ### 服务端: **main.c** ```c #include #include #include #include "mediaLib.h" #include "thrChnList.h" #include "thrChnData.h" #include #include #include #define BUFSIZE 1024 #if 1 /* int sd; static void net_init(void) { struct ip_mreqn imr; struct sockaddr_in rcvAddr,localaddr; sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd == -1) { perror("socket()"); exit(1); } inet_aton(GROUPADDR, &imr.imr_multiaddr); inet_aton("0.0.0.0", &imr.imr_address); imr.imr_ifindex = if_nametoindex("ens33"); if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) { perror("setsockopt()"); close(sd); exit(1); } localaddr.sin_family = AF_INET; localaddr.sin_port = htons(RCVPORT); localaddr.sin_addr.s_addr = INADDR_ANY; if(bind(sd,(void *)&localaddr,sizeof(localaddr)) < 0) { perror("bind()"); close(sd); exit(1); } } */ int main(void) { struct medLibList_st *ml = NULL;//创建一个频道列表结构体指针 int chnr = 0;//定义频道 int i; ml = malloc(sizeof(struct medLibList_st));//开辟频道列表指针 if(ml == NULL) { perror("malloc()"); exit(1); } mediaLibGetChnList(&ml, &chnr); thrListInit(ml, chnr); printf("%d\n",chnr); for (i = 1; i <= chnr; i++) { thrDataInit(ml[i].chnid); } /* char buf1[1024]; while(1) { mediaLibReadChnData(1,buf1,1024); puts(buf1); sleep(1); } */ pause(); exit(0); } #endif #if 0 static int mycp(int rfd, int wfd); int main(int argc, char *argv[]) { struct medLibList_st *ml = NULL; int chnr = 0; int i; ml = malloc(sizeof(struct medLibList_st)); if(ml == NULL) { perror("malloc()"); exit(1); } mediaLibGetChnList(&ml, &chnr); int pfd[2]; pid_t pid; if (pipe(pfd) == -1) { perror("pipe()"); exit(1); } pid = fork(); if (-1 == pid) { perror("fork()"); goto ERROR; } if (0 == pid) { close(pfd[1]); // mycp(pfd[0], 1); dup2(pfd[0],0); close(pfd[0]); execl("/usr/bin/mplayer","mplayer","-",NULL); exit(0); } close(pfd[0]); mycp(ml[1].chnid, pfd[1]); close(pfd[1]); wait(NULL); return 0; ERROR: close(pfd[0]); close(pfd[1]); exit(1); } static int mycp(int rfd, int wfd) { char buf[BUFSIZE] = {}; int cnt; while (1) { cnt = mediaLibReadChnData(rfd, buf, BUFSIZE); if (-1 == cnt) { return -errno; } if (0 == cnt) break; write(wfd, buf, cnt); } } #endif ``` #### 媒体库接口模块 **mediaLib.c** ```c #include #include #include "mediaLib.h" #include #include #include #include #include #include #include #include #include #define PATHSIZE 1024 #define BUFSIZE 512 static char *chnbuf[PATHSIZE];//用来存储频道路径 static int num;//频道号 static void glob_chn_func(void)//解析频道函数 { glob_t globres; char path[PATHSIZE]; int i; strncpy(path,MEDIALIBPATH,PATHSIZE); strncat(path,"/*",PATHSIZE); glob(path,0,NULL,&globres); num = globres.gl_pathc; for(i = 0;ichnid = chnid; memset(path_ex,0,PATHSIZE); memset(path,0,PATHSIZE); // puts(chnbuf[chnid]); strncpy(path_ex,chnbuf[chnid-1],PATHSIZE); strncat(path_ex,"/descr.txt",PATHSIZE); strncpy(path,chnbuf[chnid-1],PATHSIZE); strncat(path,"/*",PATHSIZE); //puts("hhhh"); glob(path,0,NULL,&globres); for(i=0,j=0;i< globres.gl_pathc;i++) { if(strcmp(path_ex,globres.gl_pathv[i]) != 0) { res->mp3file[j++] = globres.gl_pathv[i]; res->count = j; } } // globfree(&res->globres); res->curr = 0; res->offset = 0; res->fd = open(res->mp3file[0],O_RDONLY); if(res->fd < 0) { perror("open()"); exit(1); } channel[chnid] = *res; // globfree(&globres); free(res); res = NULL; } } static void open_next(int8_t chnid)//打开下一个节目文件 { if(channel[chnid].curr >= channel[chnid].count) { // return 0; channel[chnid].curr = 0; } channel[chnid].fd = open(channel[chnid].mp3file[channel[chnid].curr],O_RDONLY); if(channel[chnid].fd < 0) { perror("open()"); exit(1); } } int mediaLibGetChnList(struct medLibList_st **mlib, int *n)//获取单个频道节目单接口 { struct medLibList_st *mymlib; int i; glob_chn_func(); if(inited) { mp3Init();//初始化一次就行 inited = 0; } mymlib = malloc(sizeof(struct medLibList_st)); if(mymlib == NULL) { perror("malloc()"); return -1; } for(i = 1;i 0) { channel[chnid].offset += retlen; return retlen; } } #if 0 int main(void) { char buf1[1024]; mediaLibReadChnData(1,buf1,1024); puts(buf1); } static int mycp(int rfd, int wfd); int main(int argc, char *argv[]) { int pfd[2]; pid_t pid; if (pipe(pfd) == -1) { perror("pipe()"); exit(1); } pid = fork(); if (-1 == pid) { perror("fork()"); goto ERROR; } if (0 == pid) { close(pfd[1]); // mycp(pfd[0], 1); dup2(pfd[0],0); close(pfd[0]); execl("/usr/bin/mplayer","mplayer","-",NULL); exit(0); } close(pfd[0]); mycp(2, pfd[1]); close(pfd[1]); wait(NULL); return 0; ERROR: close(pfd[0]); close(pfd[1]); exit(1); } static int mycp(int rfd, int wfd) { char buf[BUFSIZE] = {}; int cnt; while (1) { cnt = mediaLibReadChnData(rfd, buf, BUFSIZE); if (-1 == cnt) { return -errno; } if (0 == cnt) break; write(wfd, buf, cnt); } } #endif ``` **mediaLib.h** ```c #ifndef __MEDIALIB_H #define __MEDIALIB_H #define MEDIALIBPATH "../medias" struct medLibList_st { int8_t chnid; char *descr; }; int mediaLibGetChnList(struct medLibList_st **mlib, int *n); ssize_t mediaLibReadChnData(int8_t chnid, void *buf, size_t size); #endif ``` #### 发送节目内容模块 **thrChnData.c** ```c #include #include #include #include #include #include #include #include #include #include #include #include #include #include "proto.h" #include "thrChnData.h" #include "mediaLib.h" int send_t(int chnid) { int sd; struct ip_mreqn imr; struct sockaddr_in rcvAddr; ssize_t num = 0; char buf[MAXMSG]; struct chndata_st *vls; int size; sd = socket(AF_INET,SOCK_DGRAM,0); if(sd < 0) { perror("socket()"); exit(1); } inet_aton(GROUPADDR,&imr.imr_multiaddr); inet_aton("0.0.0.0",&imr.imr_address); imr.imr_ifindex = if_nametoindex("ens33"); if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&imr,sizeof(imr)) < 0) { close(sd); perror("setsockopt()"); exit(2); } rcvAddr.sin_family = AF_INET; inet_aton(GROUPADDR,&rcvAddr.sin_addr); rcvAddr.sin_port = htons(RCVPORT); while (1) { num = mediaLibReadChnData(chnid,buf,MAXMSG); // puts(buf); size = sizeof(struct chndata_st)+strlen(buf); vls=malloc(size); vls->chnid = chnid; strcpy(vls->data,buf); // puts(vls->data); if(num == 0) { printf("文件内容为空\n"); exit(1); } if(num < 0) { printf("获取文件内容失败!\n"); exit(1); } if (sendto(sd, vls, size, 0,(void *)&rcvAddr, sizeof(rcvAddr)) == -1) { perror("sendto()"); // return -3; } // usleep(1); } close(sd); } int thrDataInit(int8_t chnid) { pid_t pid; char buf1[1024]; pid = fork(); if(pid < 0) { perror("fork()"); return -4; } if(pid == 0) { send_t(chnid); exit(0); } } /* void thrDataDestroy(void) { wait(NULL); } */ ``` **thrChnData.h** ```c #ifndef __THRCHNDATA_H #define __THRCHNDATA_H int thrDataInit(int8_t chnid); void thrDataDestroy(void); #endif ``` #### 发送节目单模块 **thrChnList.c** ```c #include #include #include #include #include #include #include #include #include #include #include #include "thrChnList.h" #include "proto.h" #define BUFSIZE 512 static pthread_t tid_list; static struct chnlist_st *list; static int totalsize = 0; extern int sd; static void *fun_list(void *s) { int sd; struct ip_mreqn imr; struct sockaddr_in rcvAddr; sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd == -1) { perror("socket()"); exit(1); } inet_aton(GROUPADDR, &imr.imr_multiaddr); inet_aton("0.0.0.0", &imr.imr_address); imr.imr_ifindex = if_nametoindex("ens33"); if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &imr, sizeof(imr)) == -1) { perror("setsockopt()"); close(sd); exit(1); } rcvAddr.sin_family = AF_INET; inet_aton(GROUPADDR, &rcvAddr.sin_addr); rcvAddr.sin_port = htons(RCVPORT); /* struct entry_st *entry; struct chnlist_st *list; char buf[BUFSIZE]; int bufcnt; int i; int cnt = 0; list->chnid = 0; for(i = 1; i <= arg->n; i++) { strcpy(buf, arg->ml[i].descr); cnt += strlen(buf); } list = malloc(sizeof(struct chnlist_st) + cnt + 2*(arg->n)); cnt = 0; for(i = 1; i <= arg->n; i++) { strcpy(buf, arg->ml[i].descr); bufcnt = strlen(buf) + 1; entry = malloc list->listEntry + cnt = realloc(list->listEntry + cnt, bufcnt + 3); ((list->listEntry) + cnt) -> chnid = arg->ml[i].chnid; cnt++; ((list->listEntry) + cnt) -> len = bufcnt; cnt ++; strcpy(((list->listEntry) + cnt) -> descr , arg->ml[i].descr); cnt += bufcnt; } */ /* struct entry_st *pos; for(pos = list->listEntry ; (char *)pos < (((char *)list) + totalsize) ; pos = (void *)(((char *)pos)+ pos->len)) { printf("channel %d :%s\n",pos->chnid,pos->descr); } */ while(1) { if(sendto(sd, list,totalsize , 0, (void *)&rcvAddr, sizeof(rcvAddr)) == -1) { perror("sendto()"); close(sd); exit(1); } sleep(1); } close(sd); } int thrListInit(struct medLibList_st *mlib, int n) { int err; struct entry_st *entry; int i,cnt = 0,size = 0; char buf[MAXMSG]; totalsize = sizeof(int8_t); // puts(mlib[1].descr); for(i = 1; i <= n; i++) { totalsize += sizeof(struct entry_st) + strlen(mlib[i].descr); } list = malloc(totalsize); if(list == NULL) { perror("malloc()"); exit(1); } list->chnid = 0; // printf("%d\n",list->chnid); entry = list->listEntry; for(i = 1; i <= n; i++) { size = sizeof(struct entry_st) + strlen(mlib[i].descr); // printf("%d\n",size); entry->chnid = mlib[i].chnid; entry->len = size; // printf("%d\n",entry->len); strcpy(entry->descr,mlib[i].descr); entry = (void *)(((char *)entry) + size); } // puts(list->listEntry[1].descr); err = pthread_create(&tid_list, NULL, fun_list, NULL); if(err) { perror("pthread_create()"); exit(1); } return 0; } void thrListDestroy(void) { pthread_cancel(tid_list); pthread_join(tid_list, NULL); } /* int main() { struct medLibList_st *ml = NULL; int n = 0; ml = malloc(sizeof(struct medLibList_st)); if(mediaLibGetChnList(&ml, &n) < 0) { exit(1); } thrListInit(ml,n); pause(); } */ ``` **thrChnList.c** ```c #ifndef __THRCHNLIST_H #define __THRCHNLIST_H #include "mediaLib.h" int thrListInit(struct medLibList_st *mlib, int n); void thrListDestroy(void); #endif ``` **makefile** ```c CFLAGS+=-pthread LDFLAGS+=-pthread server:main.o thrChnData.o thrChnList.o mediaLib.o gcc $(CFLAGS) $^ -o $@ $(LDFLAGS) clean: rm *.o ``` ### **客户端** **client.c** ```c #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "proto.h" static ssize_t write_all(int fd,const char *buf,size_t len) { int ret; int pos = 0; while(len > 0) { ret = write(fd,buf,len); if(ret < 0) { if(errno == EINTR) continue; perror("write()"); exit(1); } len -= ret; pos += ret; } return pos; } int main() { int sd; int pd[2]; int pid; int chosenid; int len,len1; struct sockaddr_in localaddr,saveraddr,remoteaddr; struct ip_mreqn mreq; socklen_t saddr_len,raddr_len; union msg_st buf; //建立socket套接字 sd = socket(AF_INET,SOCK_DGRAM,0); if(sd < 0) { perror("socket()"); exit(1); } //加入多播组 inet_aton(GROUPADDR,&mreq.imr_multiaddr); inet_aton("0.0.0.0",&mreq.imr_address); mreq.imr_ifindex = if_nametoindex("ens33"); if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq)) < 0) { perror("setsockopt()"); close(sd); exit(1); } //绑定本机地址 localaddr.sin_family = AF_INET; localaddr.sin_port = htons(RCVPORT); localaddr.sin_addr.s_addr = INADDR_ANY; if(bind(sd,(void *)&localaddr,sizeof(localaddr)) < 0) { perror("bind()"); close(sd); exit(1); } if(pipe(pd) < 0) { perror("pipe()"); close(sd); exit(1); } //接受节目单 struct chnlist_st *msg_list; msg_list = malloc(MAXMSG); if(msg_list == NULL) { perror("malloc()"); exit(1); } saddr_len = sizeof(saveraddr); while(1) { len = recvfrom(sd,msg_list,MAXMSG,0,(void *)&saveraddr,&saddr_len); if(len < sizeof(struct chnlist_st)) { // fprintf(stderr,"message is small\n"); continue; } if(msg_list->chnid != CHNLISTID) { // fprintf(stderr,"chnid is not\n"); continue; } break; } //打印节目单选择频道 struct entry_st *pos; for(pos = msg_list->listEntry ; (char *)pos < (((char *)msg_list) + len) ; pos = (void *)(((char *)pos)+(pos->len))) { printf("channel %d :%s\n",pos->chnid,pos->descr); } free(msg_list); printf("请输入您想选择的频道:"); scanf("%d",&chosenid); pid = fork(); if(pid < 0) { perror("fork()"); close(sd); exit(1); } if(pid == 0) { //子进程:调用解码器,播放 close(sd); close(pd[1]); //不需要写端 dup2(pd[0],0); //重定向到标准输入 close(pd[0]); /* int fd1,cnt; char ce[1024]; fd1 = open("./test",O_WRONLY | O_TRUNC); while(1) { cnt = read(0,ce,1024); if(cnt == 0) break; write(fd1,ce,cnt); } */ execl("/usr/bin/mpg123","mpg123","-",NULL); perror("execl()"); exit(0); } //父进程:收包发给子进程 /* //接受节目单 struct chnlist_st *msg_list; msg_list = malloc(MAXMSG); if(msg_list == NULL) { perror("malloc()"); exit(1); } saddr_len = sizeof(saveraddr); while(1) { len = recvfrom(sd,msg_list,MAXMSG,0,(void *)&saveraddr,&saddr_len); if(len < sizeof(struct chnlist_st)) { fprintf(stderr,"message is small\n"); continue; } if(msg_list->chnid != CHNLISTID) { fprintf(stderr,"chnid is not\n"); continue; } break; } //打印节目单选择频道 struct entry_st *pos; for(pos == msg_list->listEntry ; (char *)pos < (((char *)msg_list) + len) ; pos = (void *)(((char *)pos)+ntohs(pos->len))) { printf("channel %d :%s\n",pos->chnid,pos->descr); } free(msg_list); printf("请输入您想选择的频道:"); scanf("%d",&chosenid); */ //接收频道包 struct chndata_st *msg_channel; msg_channel = malloc(MAXMSG); if(msg_channel == NULL) { perror("malloc()"); exit(1); } raddr_len = sizeof(remoteaddr); while(1) { len = recvfrom(sd,msg_channel,MAXMSG,0,(void *)&remoteaddr,&raddr_len); // puts(msg_channel->data); /* if(remoteaddr.sin_addr.s_addr != saveraddr.sin_addr.s_addr || remoteaddr.sin_port != saveraddr.sin_port) { fprintf(stderr,"error:addres not math\n"); continue; } */ if(len < sizeof(struct chndata_st)) { fprintf(stderr,"error:too small\n"); continue; } if(msg_channel->chnid == chosenid) { // puts(msg_channel->data); if(write_all(pd[1],msg_channel->data,len) < 0) { perror("write_all()"); exit(1); } // puts("ok"); } } free(msg_channel); close(sd); exit(0); } ```