我的大学毕设,直播系统的设计
前言:大学时光匆匆而过转瞬即逝,再回首已经分不清是大一还是大四了。
1:项目环境:debian10,gcc 10.2.1版本,php7.4.3,mysql 5.7.26,nginx1.15.11
win10下:
前后端环境:nginx1.15.11,php7.4.3,mysql 5.7.26使用小皮面板。下载移步于:小皮面板
debian10下:
IM环境:gcc 10.2.1版本,
安装方法:
su root
apt update
apt install build-essential
//查看gcc与g++版本
gcc --version
g++ --version
还需要nginx与http-flv用于直播推拉流的环境
创建一个nginx文件夹用于存储 mkdir /nginx cd nginx 下载nginx wget http://nginx.org/download/nginx-1.18.0.tar.gz 下载Nginx-http-flv-module模块 wget https://github.com/winshining/nginx-http-flv-module/archive/master.zip 解压Nginx与http-flv模块 tar -zxvf nginx-1.18.0.tar.gz unzip nginx-http-flv-master.zip 安装相关依赖 apt install gcc //前面安装了gcc可以不用再次安装 apt install libpcre3 libpcre3-dev apt install openssl libssl-dev apt install zlib1g-dev 进入解压后的nginx文件我这里是1.18版本 cd nginx-1.18.0/ ./configure --add-module=/nginx/nginx-http-flv-master/ 这里--add-module=后面的路径一定要是绝对路径 一定要是 make && make install 至此环境搭建成功了
运行目录:/usr/local/nginx/sbin/nginx
配置目录:/usr/local/nginx/conf/nginx.conf
#我的nginx配置 #user nobody; worker_processes 1; events { worker_connections 1024; } rtmp_auto_push on; rtmp_auto_push_reconnect 1s; rtmp_socket_dir /tmp; rtmp { server { listen 8082; # 接受推流的端口号 chunk_size 8192; # 单一推流数据包的最大容量? application myapp { # myapp 模块,可以自行更换名字 live on; # 打开直播 # 非常重要, 设定让ngnix断开阻塞中的连接, 才能触发exec_record_done # 以及客户端的LFLiveKit reconnectCount自动重新连接才会好用 drop_idle_publisher 5s; meta off; # 为了兼容网页前端的 flv.js,设置为 off 可以避免报错 gop_cache off; # 支持GOP缓存,以减少首屏时间 allow play all; # 允许来自任何 ip 的人拉流 } } } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 8083; server_name localhost; location / { root html; index index.html index.htm; } location /live { # 拉流时的 uri ,可以自行修改 flv_live on; # 打开 http-flv 服务 chunked_transfer_encoding on; add_header 'Access-Control-Allow-Origin' '*'; # 允许跨域 add_header 'Access-Control-Allow-Credentials' 'true'; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
检查一下是否安装成功:nginx -V
检查配置文件:nginx -t
至此我们所有环境都安装完成;
2:代码实现等:
系统最核心的点就是websokcet的编码解码,具体细节可以看我之前的websokcet聊天室那一节:
//获取Sec-WebSocket-Key的值 char * fetchSecKey(const char * buf){ char *key; const char *flag="Sec-WebSocket-Key: "; int i=0, bufLen=0; key=(char *)malloc(256); memset(key,0, 256); if(!buf){ return NULL; } const char* keyBegin=strstr(buf,flag); if(!keyBegin){ return NULL; } //读取Sec-WebSocket-Key后面的 keyBegin+=strlen(flag); bufLen=strlen(buf); for(i=0;i<bufLen;i++){ if(keyBegin[i]==0x0A||keyBegin[i]==0x0D){ break; } key[i]=keyBegin[i]; } return key; } //获取加密后的值 char* get_comm_key(char *key){ const char *mofa="258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; char *temp_data=(char *)malloc(strlen(key)+strlen(mofa)+1); sprintf(temp_data,"%s%s",key,mofa); const char *base64_table="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; size_t len = strlen(temp_data); unsigned char sha1[SHA_DIGEST_LENGTH]; SHA1((const unsigned char *)temp_data,len,sha1); int sha1_len=strlen((const char *)sha1); //定义base64编码后的长度 int de_len=0; if(sha1_len%3==0){ de_len=(sha1_len/3)*4; }else{ de_len=(sha1_len/3+1)*4; } // 定义字符数组长度 char *code_value=(char *)malloc(de_len+1); code_value[de_len]='\0'; //三个一组依次写入 for(int i=0,j=0;i<sha1_len;i+=3,j+=4){ //这里是3个齐全的 if(i+3<sha1_len){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4 | sha1[i+1]>>4]; code_value[j+2]=base64_table[(sha1[i+1] & 0xf)<<2 | sha1[i+2]>>6]; code_value[j+3]=base64_table[sha1[i+2] & 0x3f]; }else{ //当没有3个时 int c=sha1_len-i; if(c==1){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4]; }else if(c==2){ code_value[j]=base64_table[sha1[i]>>2]; code_value[j+1]=base64_table[(sha1[i] & 0x3)<<4 | sha1[i+1]>>4]; code_value[j+2]=base64_table[(sha1[i+1] & 0xf)<<2]; } } } //末尾填写= switch(sha1_len % 3) { case 1: code_value[de_len-2] = '='; code_value[de_len-1] = '='; break; case 2: code_value[de_len-1] = '='; break; } free(temp_data); return code_value; } //加密获得电脑段key char * computeAcceptKey(const char * buf){ char * clientKey=fetchSecKey(buf); // char * serverKey=(char *)malloc(100); char * serverKey=get_comm_key(clientKey); free(clientKey); printf("sever_key=%s\n",serverKey); return serverKey; } //握手 void shakeHand(int connfd,const char *serverKey){ char *responseHeader=(char *)malloc(sizeof("a")*200); if(!connfd){ return; } if(!serverKey){ return; } memset(responseHeader,'\0',200); sprintf(responseHeader, "HTTP/1.1 101 Switching Protocols\r\n"); sprintf(responseHeader, "%sUpgrade: websocket\r\n", responseHeader); sprintf(responseHeader, "%sConnection: Upgrade\r\n", responseHeader); sprintf(responseHeader, "%sSec-WebSocket-Accept: %s\r\n\r\n", responseHeader, serverKey); send(connfd,responseHeader,strlen(responseHeader),0); free(responseHeader); } //数据解码 char* dcode(char *buf,int buf_len){ int ret =0; char* mask = NULL; int mask_num=0; u_int len = (buf[1]&0x7f); if(len==126){ //数据真实长度 len=((buf[2]) | (buf[3] << 8)); if(buf[2]==0){ len=(buf[3] & 0x000000ff); }else{ len=((buf[2]&0x00ff)<<8 | (buf[3] & 0x00ff)); } mask_num=2; // 获取真实长度 }if(len==127){ return NULL; } //开始解码 mask=buf+2+mask_num; char* tmp_msg; tmp_msg=(char*)malloc(sizeof(char)*len+1); tmp_msg[len]=0; char *raw_data =buf+4+2+mask_num; for(int i=0;i<len;i++){ tmp_msg[i] = raw_data[i] ^ mask[i % 4]; } return (char*)tmp_msg; } //数据编码 char* ecode(char *buf){ int buf_len=strlen(buf); char* send_buffer=NULL; if(buf_len<126){ send_buffer=(char *)malloc(buf_len+3); memset(send_buffer,0,buf_len+3); send_buffer[0] = 0x81; send_buffer[1] = buf_len; memcpy(send_buffer+2,buf,buf_len); }else if(buf_len<65535){ send_buffer=(char *)malloc(buf_len+5); memset(send_buffer,0,buf_len+5); send_buffer[0] = 0x81; send_buffer[1] = 126; send_buffer[2]=(buf_len>>8) & 255; send_buffer[3]=buf_len & 255; memcpy(send_buffer+4,buf,buf_len); } return send_buffer; }
前端就没啥好说的;
3:系统运行:
第一步:启动debian中的nginx环境;
cd /usr/local/nginx/sbin/nginx &&./nginx
如图就是开启成功。
进入IM文件夹运行命令:gcc main.cpp -lstdc++ -lm -lpthread -lcrypto -o main && ./main
如果提示错误就执行./main直到成功为止
如果需要修改端口则在main.cpp中修改:
接下来就是前后端运行了:
启动小皮中的这两个环境
创建一个网站,设置根目录,然后在浏览器中输入对应的域名就可以了
对了记得修改文件中的数据库账号密码等等以及导入数据库
导入数据库文件
开直播只需要下载一个obs中设置对应的obs和推流码就可以了
最后附上项目下载地址地址:
链接:https://pan.baidu.com/s/1fDhOwlbpZtyC1CaZ2bNv8A?pwd=53fq
提取码:53fq