获取斗鱼直播间的弹幕信息
2016-01-15 23:50
399 查看
最近在知乎上看到这个话题,感觉很有趣,自己实验了,果然可以,特此分享:https://www.zhihu.com/question/29027665
实验准备:Wireshark、Linux、Chrome
实验步骤:
1、打开Chrome,进入一个弹幕比较丰富的直播间,比如22519号房间,获取server_config
F12审查元素,然后 Ctrl+F 查找 server_config,如果找不到查找 server 即可:
2、解码 server_config
双击选中 server_config (上图中红框中的内容)复制,在线解码:http://www.convertstring.com/EncodeDecode/UrlDecode
3、打开Wireshark监视网卡,并刷新斗鱼房间
点此查看Wireshark npf没有启动的解决方法
点击Wireshark左上角的 Interface List ,选择当前联网的网卡:
4、监听指定端口,找到gid
tcp.port==8053||tcp.port==8075||tcp.port==8019||tcp.port==8069||tcp.port==8049||tcp.port==8056||tcp.port==8005||tcp.port==8026||tcp.port==8074||tcp.port==8054
这里的端口都是上面根据 server_config 解码得到的
我们看到数据包的最上面几条,三次握手之后的第三条接收的数据里面就有gid:
这里看到gid=10
5、下载源代码,编译运行
源码在github上:https://github.com/fishioon/douyu/blob/master/danmu.cc
我们下载后放在linux下用 g++ 编译:
g++ danmu.cc -o danmu
然后运行:
./danmu 25519 10
上面的25519是主播的房间号,10是上面获取的gid
运行结果:
6、源代码解释:
实验准备:Wireshark、Linux、Chrome
实验步骤:
1、打开Chrome,进入一个弹幕比较丰富的直播间,比如22519号房间,获取server_config
F12审查元素,然后 Ctrl+F 查找 server_config,如果找不到查找 server 即可:
2、解码 server_config
双击选中 server_config (上图中红框中的内容)复制,在线解码:http://www.convertstring.com/EncodeDecode/UrlDecode
3、打开Wireshark监视网卡,并刷新斗鱼房间
点此查看Wireshark npf没有启动的解决方法
点击Wireshark左上角的 Interface List ,选择当前联网的网卡:
4、监听指定端口,找到gid
tcp.port==8053||tcp.port==8075||tcp.port==8019||tcp.port==8069||tcp.port==8049||tcp.port==8056||tcp.port==8005||tcp.port==8026||tcp.port==8074||tcp.port==8054
这里的端口都是上面根据 server_config 解码得到的
我们看到数据包的最上面几条,三次握手之后的第三条接收的数据里面就有gid:
这里看到gid=10
5、下载源代码,编译运行
源码在github上:https://github.com/fishioon/douyu/blob/master/danmu.cc
我们下载后放在linux下用 g++ 编译:
g++ danmu.cc -o danmu
然后运行:
./danmu 25519 10
上面的25519是主播的房间号,10是上面获取的gid
运行结果:
6、源代码解释:
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define BUFFER_SIZE 1024 #define DANMU_PORT 8601 //服务器弹幕发送端口 #define DANMU_IP "125.88.176.8" //弹幕服务器 #define USERNAME "username" //默认用户名 #define PASSWORD "passwd" //默认密码 typedef struct { //数据包结构体 int len; int code; int magic; char content[BUFFER_SIZE]; }MsgInfo; //连接服务器 int sock_conn(int port, const char* addr) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); servaddr.sin_addr.s_addr = inet_addr(addr); if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { fprintf(stderr, "connect socket failed, port:%d, addr:%s\n", port, addr); return -1; } return sockfd; } int sock_close(int sockfd) { close(sockfd); return 0; } //弹幕处理 int process_danmu(int msg_len, MsgInfo* msg) { if (msg_len <= 12 || msg->len <= 8) { printf("msg_len:%d\n", msg_len); return -1; } char* p = strstr(msg->content, "content@="); if (!p) return -1; char* content = p + sizeof("content@=")-1; p = strchr(content, '/'); if (!p) return -1; *p++ = 0; char* snick = p + sizeof("snick@=")-1; p = strchr(snick, '/'); if (!p) return -1; *p++ = 0; printf("%s:\t%s\n", snick, content); return 0; } //收发弹幕数据包 int douyu_danmu(int sockfd, const char* username, const char* passwd, int room_id, int group_id) { MsgInfo msg; //登陆请求 int ct_len = snprintf(msg.content, sizeof(msg.content), "type@=loginreq/username@%s=/password@=%s/roomid@=%d/ct@=2/", username, passwd, room_id); msg.len = ct_len + 1 + sizeof(msg.code) + sizeof(msg.magic); msg.code = msg.len; msg.magic = 0x2b1; //第一次握手 send(sockfd, &msg, msg.len+sizeof(msg.len), 0); //第二次握手 recv(sockfd, &msg, sizeof(msg), 0); //输出数据包内容 printf("recv:%s\n", msg.content); ct_len = snprintf(msg.content, sizeof(msg.content), "type@=joingroup/rid@=%d/gid@=%d/", room_id, group_id); msg.len = ct_len + 1 + sizeof(msg.code) + sizeof(msg.magic); msg.code = msg.len; msg.magic = 0x2b1; //第三次握手 send(sockfd, &msg, msg.len+sizeof(msg.len), 0); int ret = 0; while (true) { //接收弹幕 ret = recv(sockfd, &msg, sizeof(msg), 0); //处理弹幕 if (process_danmu(ret, &msg) == -1) { send(sockfd, &msg, 0, 0); } } return 0; } int main(int argc, char** argv) { if (argc < 2) { printf("usage: danmu room_id group_id\n"); return 0; } //可以不需要用户名和密码 const char* username = ((argc == 4) ? argv[3] : USERNAME); const char* passwd = ((argc == 4) ? argv[4] : PASSWORD); //第一个参数是rid int room_id = atoi(argv[1]); //第二个参数是gid int group_id = atoi(argv[2]); //打开连接 int sockfd = sock_conn(DANMU_PORT, DANMU_IP); //收发弹幕包 douyu_danmu(sockfd, username, passwd, room_id, group_id); return 0; }
相关文章推荐
- iOS网络1——NSURLConnection使用详解
- 字符串的输出
- AFNetwork 作用和用法详解
- 创建类并调用
- Eclipse-----Eclipse导入源码
- [乡土民间故事_徐苟三传奇]第廿一回_伍阎王弄巧反成拙
- npoi操作excel之设置某一列百分比某一列为数字
- Java学习1
- 从代码风格窥视一个工程师的技术水平[总结篇]
- HTML标签理解
- 两年读研QQ空间写给自己的感悟-写于2012.4.19
- Cocos2d-x 3.x 引擎目录结构
- JS之动态表格
- [乡土民间故事_徐苟三传奇]第二十回_伍阎王贪色受惩罚
- 乐讯机器人精简版2016迎春版【隆重发布】
- 【数组】Container With Most Water
- java基础介绍(转)
- [乡土民间故事_徐苟三传奇]第十九回_争棉被痛打蒋四棍
- Codeforces Gym 100231F Solitaire 折半搜索
- PHP Static延迟静态绑定