项目名称:基于IPV4的流媒体广播系统
项目需求:
目前需要实现基于客户机和服务器模型的网络音频点播系统。
本音频系统可以广泛应用在语音教室和公共广播等多种场所。该软件分为服务器和客户机两个部分,
服务器运行在PC上,客户机可运行在PC机或嵌入式设备伤,服务器以多播的方式向局域网中所有的客户
机发送数据,客户机可以根据自己的选择来决定要接受的数据。如题简示
其中S端是服务器端,C端是客户端,从S端发送数据给C端,发送一个.mp3的文件。S端会发送一些节目单(类似发送广播节目一样)。data和list从lib传过来,用main将几个模块综合起来。广播是S端发过来,然后自己选择。点播是C端为主动。
C端父进程负责从网络上接受数据,传送给子进程,子进程负责来播放,其中用到了进程间通信(管道,共享,消息队列)。
#include客户端client.h#include #include #include #include #include #include #include #include #include #include #include #include #include "client.h" struct client_conf_st client_conf = { .rcvport = DEFAULT_RCVPORT, .mgroup = DEFAULT_MGROUP, .player_cmd = DEFAULT_PLAYERCMD}; //打印帮助信息 static void printhelp(void) { printf("-P --port 指定接受端口n"); printf("-M --mgroup 指定多播组n"); printf("-p --player 指定播放器命令行n"); printf("-H --help 显示帮助n"); } static size_t writen(int fd,const char *buf,size_t len) { int ret,pos=0; while (len>0) { ret = write(fd,buf+pos,len); if (ret<0) { if(errno == EINTR) continue; perror("write()"); return -1; } len -= ret; pos += ret; } return pos; } //管道进行父子间通信 int main(int argc, char **argv) { int c,sd,val,pd[2],len,ret; int index = 0; pid_t pid; struct sockaddr_in localaddr,serveraddr,raddr; socklen_t serveraddr_len,raddr_len; struct ip_mreqn mreq; int chosenid; struct option argarr[] = {{"port",1,NULL,'P'},{"mgroup",1,NULL,'M'}, {"player",1,NULL,'p'},{"help",0,NULL,'H'}, {NULL,0,NULL,0}}; //有那些opt是带参数的,如果有的话加一个冒号。 //分析命令行,man getopt_long while (1) { c = getopt_long(argc,argv,"P:M:p:H",argarr,&index); if (c<0) break; switch (c) { case 'P': client_conf.rcvport = optarg; break; case 'M': client_conf.mgroup = optarg; break; case 'p': client_conf.player_cmd = optarg; break; case 'H': printhelp();//打印帮助信息 exit(0); break; default: break; } } sd = socket(AF_INET,SOCK_DGRAM,0); if (sd<0) { perror("socket"); exit(1); } //man 7 ip 加入多播组 点分式转大整数 inet_pton(AF_INET,client_conf.mgroup,&mreq.imr_multiaddr); inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address); mreq.imr_ifindex = if_nametoindex("eth0"); if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))<0) { perror("setsocket()"); exit(1); } val =1; if(setsockopt(sd,IPPROTO_IP,IP_MULTICAST_LOOP,&val,sizeof(val))<0) { perror("setsocket()"); exit(1); } localaddr.sin_family = AF_INET; localaddr.sin_port = htons(atoi(client_conf.rcvport)); inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr.s_addr); if (bind(sd,(void *)&localaddr,sizeof(localaddr))<0) { perror("bind()"); exit(1); } if (pipe(pd)<0) { perror("pipe()"); exit(1); } pid = fork(); if (pid<0) { perror("fork()"); exit(1); } if (pid == 0) { //子进程:调用解码器 close(sd); close(pd[1]);//关闭管道的写端 dup2(pd[0],0);//重定向 if (pd[0] > 0) close(pd[0]); execl("/bin/sh","sh","-c",client_conf.player_cmd,NULL);//子进程调用解码器 perror("execl()"); exit(1); } //parent父进程:从网络上收包,发送给子进程 //收节目单 struct msg_list_st *msg_list; msg_list = malloc(MSG_LIST_MAX); if (msg_list == NULL) { perror("malloc()"); exit(1); } serveraddr_len = sizeof(serveraddr); while (1) { //存放地址为msg_list len = recvfrom(sd,msg_list,MSG_LIST_MAX,0,(void *)&serveraddr,&serveraddr_len); if (len < sizeof(struct msg_list_st)) { fprintf(stderr,"message is too small.n"); continue; } if (msg_list->chnid != LISTCHNID) { fprintf(stderr,"chnid is not match.n"); continue; } break; } //打印节目单并选择频道 struct msg_listentry_st *pos; for (pos = msg_list->entry;(char *)pos < (((char*)msg_list) + len) ;pos = (void *)(((char*)pos)+ntohs(pos->len))) { printf("channel %d:%sn",pos->chnid,pos->desc); } //free(msg_list); puts("please enter:"); while (ret < 1) { ret = scanf("%d",&chosenid); if (ret != 1) exit(1); } //收频道包,发送给子进程 fprintf(stdout,"chosenid = %dn",ret); struct msg_channel_st *msg_channel; msg_channel = malloc(MSG_CHANNEL_MAX); if (msg_channel == NULL) { perror("malloc()"); exit(1); } raddr_len = sizeof(raddr); while (1) { //存放地址为msg_channel len = recvfrom(sd,msg_channel,MSG_CHANNEL_MAX,0,(void *)&raddr,&raddr_len); if (raddr.sin_addr.s_addr != serveraddr.sin_addr.s_addr||raddr.sin_port!=serveraddr.sin_port) { fprintf(stderr,"Ignore:adress not match.n"); continue; } if (len < sizeof(struct msg_channel_st)) { fprintf(stderr,"Ignore:message is to small.n"); continue; } //暂停 if (msg_channel->chnid == chosenid) { fprintf(stdout,"accepted msg:%d recieved.n",msg_channel->chnid); if (writen(pd[1],msg_channel->data,len-sizeof(chnid_t))<0)//往管道口写入 { exit(1); } } break; } free(msg_channel); close(sd); exit(0); }
#ifndef CLIENT_H__ #define CLIENT_H__ #define DEFAULT_PLAYERCMD "/usr/bin/mpg123 - > /dev/null" struct client_conf_st { char* rcvport; //跨网络的数据传输绝对不会出现指针 char* mgroup; char* player_cmd; }; //全局变量在文件进行使用的时候,要在.h文件中进行声明 extern struct client_conf_st client_conf; #endif客户端 服务器端 服务器配置头文件.h
#ifndef SERVER_CONF_H__ #define SERVER_CONF_H__ #define DEFAULT_MEDIADIR "/var/media" #define DEFAULT_IF "eth0" enum { RUN_DAEMON =1 , //守护进程运行 RUN_FORGROUND //前台方式运行 }; struct server_conf_st { char *rcvport; //默认接收端口 char *mgroup; //默认多播组号码 char *media_dir;//媒体库的位置 char runmode; //运行模式(前台或者后台) char *ifname; //网络设备 }; extern struct server_conf_st server_conf; extern int serversd; extern struct sockaddr_in sndaddr; #endif // !SERVER_CONF_H__媒体库.c
#include#include #include #include #include #include #include #include #include #include #include "medialib.h" #include "mytbf.h" #include "server_conf.h" #define PATHSIZE 1024 #define LINEBUFSIZE 1024 #define MP3_BITRATE 100 //封装的结构体 struct channel_context_st { chnid_t chnid; char *desc; glob_t mp3glob; int pos; int fd; __off_t offset; mytbf_t *tbf;//流量控制 }; static struct channel_context_st channel[MAXCHNID + 1]; static struct channel_context_st *path2entry(const char *path) { char pathstr[PATHSIZE]; char linebuf[LINEBUFSIZE]; FILE *fp; struct channel_context_st *me; static chnid_t curr_id =MINCHNID; strncpy(pathstr,path,PATHSIZE); strncat(pathstr,"/desc.text",PATHSIZE); fp = fopen(pathstr,"r"); if (fp == NULL) { syslog(LOG_INFO,"%s is not a channel dir(can't find desc.text)",path); return NULL; } if (fgets(linebuf,LINEBUFSIZE,fp) == NULL) { syslog(LOG_INFO,"%s is not a channel dir(can't find desc.text)",path); return NULL; } fclose(fp); me = malloc(sizeof*me); if (me == NULL) { syslog(LOG_ERR,"malloc():%sn",strerror(errno)); return NULL; } me->tbf = mytbf_init(MP3_BITRATE/8*3,MP3_BITRATE/8*10); if (me->tbf == NULL) { syslog(LOG_ERR,"mytbf_init(): %sn",strerror(errno)); free(me); return NULL; } me->desc = strdup(linebuf); strncpy(pathstr,path,PATHSIZE); strncat(pathstr," struct server_conf_st server_conf = {.rcvport = DEFAULT_RCVPORT, .mgroup = DEFAULT_MGROUP, .media_dir = DEFAULT_MEDIADIR, .runmode = RUN_DAEMON, .ifname = DEFAULT_IF }; int serversd; struct sockaddr_in sndaddr; static struct mlib_listentry_st *list; //打印帮助信息 static void printhelp(void) { printf("-P 指定接受端口n"); printf("-M 指定多播组n"); printf("-D 指定媒体库位置n"); printf("-F 前台运行n"); printf("-I 制定网络设备n"); printf("-H 显示帮助n"); } static void daemonize_exit(int s) { thr_list_destory(); thr_channel_destoryall(); mlib_freechnlist(list); syslog(LOG_WARNING,"signal-%d caught,exit now.",s); closelog(); exit(0); } static int daemonize(void) { __pid_t pid; int fd; pid = fork(); if (pid<0) { //perror("fork()"); syslog(LOG_ERR,"fork():%s",strerror(errno)); return -1; } if (pid>0) //parent exit(0); fd = open("/dev/null",O_RDWR); if (fd < 0) { //perror("open()"); syslog(LOG_WARNING,"open():%s",strerror(errno)); return -2; } else { dup2(fd,0); dup2(fd,1); dup2(fd,2); if (fd > 2) close(fd); } setsid(); chdir("/"); umask(0); return 0; } static int socket_init(void) { struct ip_mreqn mreq; serversd = socket(AF_INET,SOCK_DGRAM,0); if (serversd < 0) { syslog(LOG_ERR,"socket():%s",strerror(errno)); exit(1); } inet_pton(AF_INET,server_conf.mgroup,&mreq.imr_multiaddr); inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);//any adress mreq.imr_ifindex = if_nametoindex(server_conf.ifname); if (setsockopt(serversd,IPPROTO_IP,IP_MULTICAST_IF,&mreq,sizeof(mreq))<0) { syslog(LOG_ERR,"setsockopt(IP_MULTICAST_IF):%s",strerror(errno)); exit(1); } //bind(); sndaddr.sin_family = AF_INET; sndaddr.sin_port = htons(atoi(server_conf.rcvport)); inet_pton(AF_INET,server_conf.mgroup,&sndaddr.sin_addr.s_addr); return 0; } int main(int argc,char **argv) { openlog("netradio",LOG_PID|LOG_PERROR,LOG_DAEMON); int c,i,err; //信号意外结束,定义三个信号的行为 struct sigaction sa; sa.sa_handler = daemonize_exit; sigemptyset (&sa.sa_mask);//信号集 清空 __sigaddset(&sa.sa_mask,SIGINT); __sigaddset(&sa.sa_mask,SIGQUIT); __sigaddset(&sa.sa_mask,SIGTERM); sigaction(SIGTERM,&sa,NULL);//时常用来打断一个行为,对信号之前的状态不关心,接到信号终止守护进程的时候,会执行回调函数 sigaction(SIGINT,&sa,NULL); sigaction(SIGQUIT,&sa,NULL); while (1) { c=getopt(argc,argv,"M:P:FD:I:H"); if (c<0) break; switch (c) { case 'P': server_conf.rcvport = optarg; break; case 'M': server_conf.mgroup = optarg; break; case 'F': server_conf.runmode = RUN_FORGROUND; break; case 'D': server_conf.media_dir = optarg; break; case 'I': server_conf.ifname = optarg; break; case 'H': printhelp();//打印帮助信息 exit(0); break; default: abort(); break; } } if (server_conf.runmode == RUN_DAEMON) { if (daemonize()!=0) exit(1); } else if (server_conf.runmode == RUN_FORGROUND) { } else { //fprintf(stderr,"EINVALn"); syslog(LOG_ERR,"EINVAL server_conf.runmode."); exit(1); } socket_init(); int list_size; err = mlib_getchnlist(&list,&list_size); if (err) { syslog(LOG_ERR,"mlib_getchnlist():%s",strerror(err)); exit(1); } //一个线程专门创建节目单 err = thr_list_create(list,list_size); if (err) exit(1); //100个线程专门发送频道的数据,一个线程服务一个server for (i = 0; i < list_size; i++) { thr_channel_create(list+i); if (err) { fprintf(stderr,"thr_channel_create():%sn",strerror(err)); exit(1); } } syslog(LOG_DEBUG,"%d channel threads create. ",i); while (1) pause(); exit(0); }
耗时2个月,288节课,完结!!!!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)