简明扼要的说下, 就两个线程,一个 负责收数据,一个负责发心跳包。

步骤如下,

进程1,调用 发包函数,发送连接请求,然后再发送 获取弹幕类型请求,就一直循环接收数据。

进程2,循环函数,每隔45秒向服务器发一次心跳包。

因为斗鱼自己定义了 包头,,所以来在发包之前,先发送包数据。12个字节,

消息头部:消息长度 4字节 +消息类型4字节+加密字段2字节(默认为0)+保留字段2字节(默认为0)

然后就要把要发的内容 加上 “\0”,utf-8 编码后就能发送了

完整的 消息是:包头 + 内容 +”\0”;

上Python代码:

main.py

import socket
import time
import threading
import multiprocessing
from barrage_func import * # 导入自定义方法 SERVER_DOMAIN = "openbarrage.douyutv.com" # 弹幕服务器 域名
SERVER_PORT = 8601; # 弹幕服务器 端口
ROOM_ID = 288016; #房间ID global FIX_TAIL #拼接处理后被丢弃的数据,防止弹幕丢失
FIX_TAIL = ""
global gl_client #全局socket
gl_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) def init_socket():
global gl_client
host_ip = socket.gethostbyname(SERVER_DOMAIN)
gl_client.connect((host_ip, SERVER_PORT))
def sendDate(client,data):
data = data + '\0' #斗鱼独创序列化文本数据,结尾必须为'\0'
data_length = length = len(data)+8 #斗鱼协议在尾部加了 消长度4字节,消息类型2字节(689),加密字段1字节,保留字段1字节,
code = 689 # 消息类型
# 消息头部:消息长度+消息类型+加密字段(默认为0)+保留字段(默认为0)
head = data_length.to_bytes(4, 'little') + data_length.to_bytes(4, 'little') + code.to_bytes(2,'little')+ (0).to_bytes(2,'little')
# head = int.to_bytes(data_length, 4, 'little') + int.to_bytes(data_length, 4, 'little') + int.to_bytes(code, 4,'little')
client.sendall(head) # 发送头部部分
msg = (data).encode('utf-8') # 使用utf-8编码 数据部分
client.sendall(bytes(msg)) # 发送数据部分 def getdanmu(client):
login = 'type@=loginreq/roomid@=%s/' % ROOM_ID
sendDate(client,login)
joingroup = 'type@=joingroup/rid@=%s/gid@=-9999/' % ROOM_ID
sendDate(client,joingroup)
while True:
try:
part_body = client.recv(1024,socket.MSG_WAITALL)
if not part_body: #如果 服务器发送终止连接b'',则终止会话
break
msg_str = part_body.decode(encoding="utf-8", errors="ignore")
get_type(msg_str) except Exception as e:
print("getdanmu未知错误: %s" % e)
continue def get_type(msg_str):
global FIX_TAIL
msg_str = FIX_TAIL + msg_str
msg_arr = msg_str.split("type@=")
FIX_TAIL = msg_arr.pop()
for value in msg_arr:
type_temp = value.split("/")
if len(type_temp) >= 2:
type_name = type_temp[0]
if type_name == "chatmsg":
chatmsg =BRRAGE_FUC.get_chatmsg(value) #获取弹幕类
print("["+chatmsg.nn+"]: "+chatmsg.txt)
# pass
elif type_name == "dgb":
dgb = BRRAGE_FUC.get_Dbg(value) #获取礼物类
print("感谢[{}] ,赠送的 {} 个 '{}'".format(dgb.nn,int(dgb.gfcnt) * int(dgb.hits),dgb.gfid))
# pass
elif type_name == "uenter":
uenter=BRRAGE_FUC.get_uenter(value) #获取进入房间类
print("欢迎 ["+ uenter.nn+"] " + "进入直播间")
# pass
elif type_name == "spbc":
spbc = BRRAGE_FUC.get_spbc(value) # 获取房间广播类
print("{} 房间,[{}]赠送给[{}] {} 个 '{}'".format(spbc.drid,spbc.sn,spbc.dn,spbc.gc, spbc.gn)) def keep_alive(client):
''' 客户端每隔 45 秒发送心跳信息给弹幕服务器 '''
while True:
alive_msg = "type@=mrkl/" #新版本
# alive_msg = "type@=keeplive/tick@=%s/" % int(time.time()) #旧版本
sendDate(client,alive_msg)
time.sleep(20) if __name__ == '__main__':
init_socket()
p1 = multiprocessing.Process(target=getdanmu, args=(gl_client,))
p2 = multiprocessing.Process(target=keep_alive, args=(gl_client,))
p1.start()
p2.start()

这里引用了 2个文件,

1个是定义了4个类发言弹幕Brrage_Msg(),赠送礼物Brrage_Dgb(),用户进入房间Brrage_Enter(),广播消息Brrage_Spbc ()

1 个是 写了静态方法BRRAGE_FUC 对上面的类进行 赋值

barrage_func.py

import time
from barrage_info import *
class BRRAGE_FUC(object):
''' 常被调用的静态方法 ''' #提取发言弹幕
@staticmethod
def get_chatmsg(msg):
brrage_msg =Brrage_Msg()
#获取当时时间 eg: '2019-02-16 18:50:02'
brrage_msg.time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
key_value_list = msg.split("/")
for key_value_temp in key_value_list:
key_value=key_value_temp.split("@=",1)
if len(key_value)==2:
if key_value[0]=="rid":
brrage_msg.rid =str(key_value[1])
elif key_value[0]=="uid":
brrage_msg.uid =str(key_value[1])
elif key_value[0]=="nn":
brrage_msg.nn =str(key_value[1])
elif key_value[0]=="txt":
brrage_msg.txt=str(key_value[1])
elif key_value[0]=="cid":
brrage_msg.cid =str(key_value[1])
elif key_value[0]=="nl":
brrage_msg.nl =str(key_value[1])
elif key_value[0]=="level":
brrage_msg.level =str(key_value[1])
elif key_value[0]=="bnn":
brrage_msg.bnn =str(key_value[1])
elif key_value[0]=="bl":
brrage_msg.bl =str(key_value[1])
elif key_value[0]=="brid":
brrage_msg.brid =str(key_value[1])
return brrage_msg #提取送礼物弹幕
@staticmethod
def get_Dbg(msg):
brrage_dgb = Brrage_Dgb()
# 获取当时时间 eg: '2019-02-16 18:50:02'
brrage_dgb.time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
key_value_list = msg.split("/")
for key_value_temp in key_value_list:
key_value = key_value_temp.split("@=",1)
if len(key_value) == 2:
if key_value[0] == "rid":
brrage_dgb.rid = key_value[1]
elif key_value[0] == "uid":
brrage_dgb.uid = key_value[1]
elif key_value[0] == "nn":
brrage_dgb.nn = key_value[1]
elif key_value[0] == "sn":
brrage_dgb.sn = key_value[1]
elif key_value[0] == "gfid":
brrage_dgb.gfid = key_value[1]
elif key_value[0] == "gfcnt":
brrage_dgb.gfcnt = key_value[1]
elif key_value[0] == "hits":
brrage_dgb.hits = key_value[1]
return brrage_dgb #提取用户进房通知弹幕
@staticmethod
def get_uenter(msg):
brrage_enter = Brrage_Enter()
key_value_list = msg.split("/")
for key_value_temp in key_value_list:
key_value = key_value_temp.split("@=",1)
if len(key_value) == 2:
if key_value[0] == "rid":
brrage_enter.rid = key_value[1]
if key_value[0] == "uid":
brrage_enter.uid = key_value[1]
if key_value[0] == "nn":
brrage_enter.nn = key_value[1]
if key_value[0] == "nl":
brrage_enter.nl = key_value[1]
return brrage_enter #飞机、火箭 广播消息
@staticmethod
def get_spbc(msg):
brrage_spbc = Brrage_Spbc()
key_value_list = msg.split("/")
for key_value_temp in key_value_list:
key_value = key_value_temp.split("@=",1)
if len(key_value) == 2:
if key_value[0] == "rid":
brrage_spbc.id = key_value[1]
if key_value[0] == "drid":
brrage_spbc.drid = key_value[1]
if key_value[0] == "uid":
brrage_spbc.uid = key_value[1]
if key_value[0] == "sn":
brrage_spbc.sn = key_value[1]
if key_value[0] == "dn":
brrage_spbc.dn = key_value[1]
if key_value[0] == "gn":
brrage_spbc.gn = key_value[1]
if key_value[0] == "gc":
brrage_spbc.gc = key_value[1]
if key_value[0] == "gb":
brrage_spbc.gb = key_value[1]
if key_value[0] == "gfid":
brrage_spbc.gfid = key_value[1]
return brrage_spbc

barrage_info.py

class Brrage_Base(object):
rid = "" # 房间号
uid = "" # 用户id
nn = "nn" # 用户昵称
time = "0000-00-00 00:00:00" # 时间 class Brrage_Msg(Brrage_Base):
"""表示为“弹幕”消息,type固定为 chatmsg"""
def __init__(self):
self.txt="txt" #弹幕文本内容
self.cid="" #弹幕唯一 ID
self.nl=0 #贵族等级
self.level =0 #用户等级
self.bnn = "" # 徽章昵称
self.bl = 0 # 徽章等级
self.brid=0 #徽章房间 id class Brrage_Dgb(Brrage_Base):
'''表示为“赠送礼物”消息,type固定为 dgb '''
def __init__(self): self.gfid=0 #礼物 id
self.gfcnt =1 #礼物个数:默认值 1
self.hits=1 #礼物连击次数:默认值 1(表示 1 连击) class Brrage_Enter(Brrage_Base):
''' 表示为“用户进房通知”消息,type固定为 uenter '''
def __init__(self):
self.nl = 0 # 贵族等级 class Brrage_Spbc(Brrage_Base):
''' 房间内礼物广播,type固定为 spbc'''
def __init__(self):
self.drid = 0 #赠送房间 rid ,默认为0
self.sn = "" # 赠送者昵称
self.dn = "" # 受赠者昵称
self.gn = "" # 礼物名称
self.gc = 1 # 礼物数量
# self.gs = "" # 广播样式
self.gb = 1 # 是否有礼包(0-无礼包,1-有礼包)
# self.es = 1 # 广播展现样式(1-火箭,2-飞机)
self.gfid = 1 #礼物 id

运行 main.py 看效果,,比 官方弹幕 还 全,,官方应该 对 tcp 粘包没处理好。

下面的是C#的代码,原理都一样

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading; namespace danmu
{
class Program
{
private static string SERVER_DOMAIN = "openbarrage.douyutv.com";
private static int SERVER_PORT = ;
private static int ROOM_ID = ;
private static string FIX_TAIL = String.Empty; //拼接处理后被丢弃的数据,防止弹幕丢失
class BrrageMsg
{
public string Name = String.Empty;
public string Txt = String.Empty;
}
static void Main(string[] args)
{
try
{
Socket tcpClient = InitTcp(SERVER_DOMAIN, SERVER_PORT);
Thread getDanmuThread = new Thread(GetDanmu);
getDanmuThread.Start(tcpClient);
Thread keepAliveThread = new Thread(KeepAlive);
keepAliveThread.Start(tcpClient);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
} static Socket InitTcp(string host, int port)
{
IPHostEntry hostInfo = Dns.GetHostEntry(host);
IPAddress ipAddress = hostInfo.AddressList[]; //域名转IP
IPEndPoint ipe = new IPEndPoint(ipAddress, port);
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Connect(ipe);
return s;
}
static byte[] DataToBytes(string data)
{
string dantaNew = data + "\0";
byte[] bodyDataByte = Encoding.UTF8.GetBytes(dantaNew);
byte[] cType = BitConverter.GetBytes(); int dataLength = dantaNew.Length + cType.Length + ;
byte[] dataLengthByte = BitConverter.GetBytes(dataLength);
byte[] dataLengthByte2 = BitConverter.GetBytes(dataLength);
byte[] result = new byte[dataLength + ]; Array.Copy(dataLengthByte, , result, , );
Array.Copy(dataLengthByte2, , result, , );
Array.Copy(cType, , result, , );
Array.Copy(bodyDataByte, , result, , bodyDataByte.Length);
byte[] source = new byte[result.Length];
Array.Copy(result, , source, , result.Length);
return result;
} static void GetDanmu(object obj)
{
Socket tcpClient = (Socket)obj;
string login = "type@=loginreq/roomid@=" + ROOM_ID + "/";
byte[] loginBytes = DataToBytes(login);
tcpClient.Send(loginBytes);
string joingroup = "type@=joingroup/rid@=" + ROOM_ID + "/gid@=-9999/";
byte[] joingroupBytes = DataToBytes(joingroup);
tcpClient.Send(joingroupBytes);
string recvStr = "";
byte[] recvBytes = new byte[];
int bytes;
while (true)
{
bytes = tcpClient.Receive(recvBytes, recvBytes.Length, );//从服务器端接受返回信息
recvStr = Encoding.UTF8.GetString(recvBytes, , bytes);
ShowMsg(recvStr);
}
} static BrrageMsg GetMsgType(string[] msgType)
{
BrrageMsg brrageMsg = new BrrageMsg();
foreach (string keyValueTemp in msgType)
{
string[] keyValue = Regex.Split(keyValueTemp, "@=", RegexOptions.IgnoreCase);
if (keyValue.Length >= )
{
string key = keyValue[];
string[] textArr = new string[keyValue.Length - ];
Array.Copy(keyValue, , textArr, , keyValue.Length - );
string value = String.Join("@", textArr);
if (key =="nn")
{
brrageMsg.Name = value;
}
if ((key == "txt"))
{
brrageMsg.Txt = value;
}
}
}
return brrageMsg;
}
static void ShowMsg(string msg)
{
msg = FIX_TAIL + msg;
string[] chatmsgArray = Regex.Split(msg, "type@=", RegexOptions.IgnoreCase);
FIX_TAIL = chatmsgArray[chatmsgArray.Length - ]; //截取最后的丢弃数据,放在下个包的开头,防止数据丢失
string[] newChatmsgArrayArr = new string[chatmsgArray.Length - ];
Array.Copy(chatmsgArray, , newChatmsgArrayArr, , chatmsgArray.Length - ); foreach (string t in newChatmsgArrayArr)
{
string[] msgType = t.Split('/');
if (msgType.Length >= )
{
string type = msgType[];
if (type == "chatmsg")
{
BrrageMsg brrageMsg=GetMsgType(msgType);
string result = String.Format("[{0}]: {1}", brrageMsg.Name, brrageMsg.Txt);
Console.WriteLine(result);
}
}
}
}
static void KeepAlive(object obj)
{
Socket tcpClient = (Socket)obj;
byte[] aliveMsg = DataToBytes("type@=mrkl/");
while (true)
{
tcpClient.Send(aliveMsg);
Thread.Sleep();
}
}
}
}

斗鱼连接弹幕Demo_pythonC#的更多相关文章

  1. .NET斗鱼直播弹幕客户端(下)

    .NET斗鱼直播弹幕客户端(下) 在上篇文章中,我们提到了如何使用.NET连接斗鱼TV直播弹幕的基本操作.然而想要做得好,做得容易扩展,就需要做进一步的代码整理. 本文将涉及以下内容: 介绍如何使用R ...

  2. .NET斗鱼直播弹幕客户端(2021)

    .NET斗鱼直播弹幕客户端(2021) 离之前更新的两篇<.NET斗鱼直播弹幕客户端>已经有一段时间,近期有许多客户向我反馈刚好有这方面的需求,但之前的代码不能用了--但网上许多流传的No ...

  3. .NET斗鱼直播弹幕客户端(上)

    现在直播平台由于弹幕的存在,主播与观众可以更轻松地进行互动,非常受年轻群众的欢迎.斗鱼TV就是一款非常流行的直播平台,弹幕更是非常火爆.看到有不少主播接入弹幕语音播报器.弹幕点歌等模块,这都需要首先连 ...

  4. c# winfrom实时获取斗鱼房间弹幕

    效果图如下: 通过webBrowser获取,时钟控件刷新弹幕,正则匹配数据,用第二个webBrowser显示弹幕内容.老话,并没完善.请自行完善.有个dll是用来屏蔽webBrowser的声音的,可能 ...

  5. 斗鱼刷弹幕js代码

    对于一个网络喷子(like me)来说,喷人必须高效. var script=document.createElement("script"); script.type=" ...

  6. Flutter 实现虎牙/斗鱼 弹幕效果

    老孟导读:用Flutter实现弹幕功能,轻松实现虎牙.斗鱼的弹幕效果. 先来一张效果图: 实现原理 弹幕的实现原理非常简单,即将一条弹幕从左侧平移到右侧,当然我们要计算弹幕垂直方向上的偏移,不然所有的 ...

  7. 【Python3爬虫】斗鱼弹幕爬虫

    在网上找到了一份斗鱼弹幕服务器第三方接入协议v1.6.2,有了第三方接口,做起来就容易多了. 一.协议分析 斗鱼后台协议头设计如下: 这里的消息长度是我们发送的数据部分的长度和头部的长度之和,两个消息 ...

  8. Android弹幕功能实现,模仿斗鱼直播的弹幕效果

    转载出处:http://blog.csdn.net/sinyu890807/article/details/51933728 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即 ...

  9. selenium实战之斗鱼弹幕

    python3.6.selenium.chromedriver 先上代码 from selenium import webdriver from time import sleep driver=we ...

随机推荐

  1. MySQL的ODBC安装错误问题!

    MySQL的ODBC安装时候可能会出错,主要原因是缺少VC支持库,需要2010版本的VC支持库!!X86和X64分别对应MySQL对应的ODBC,不能安装一个两个都搞定,如果需要安装两个ODBC驱动, ...

  2. 不准使用xib自定义控制器view的大小

    1.AppDelegate.m // // 文 件 名:AppDelegate.m // // 版权所有:Copyright © 2018年 leLight. All rights reserved. ...

  3. [poj 1185] 炮兵阵地 状压dp 位运算

    Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用"H" 表示),也可能是平原(用&quo ...

  4. AngularJS(五)——filter(过滤器)

    前言 过滤器可以用一个管道字符(|)添加到表达式和指令中.比如做ITOO的时候或者其他项目的时候,总是想统一货币日期等显示,以前的做法,不断循环该控件或该标签,然后循环转换.如果利用AngularJS ...

  5. 使用remove_constants工具查看Oracle是否使用绑定变量

    https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:1163635055580 http://blog.csdn.n ...

  6. c++运算符重载-如何决定作为成员函数还是非成员函数

    The Decision between Member and Non-member The binary operators = (assignment), [] (array subscripti ...

  7. P1452 Beauty Contest 旋转卡壳

    \(\color{#0066ff}{题目描述}\) 贝茜在牛的选美比赛中赢得了冠军"牛世界小姐".因此,贝西会参观N(2 < = N < = 50000)个农场来传播善 ...

  8. luogu2522 [HAOI2011]Problem b

    luogu2522[HAOI2011]Problem b 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公 ...

  9. 10.6-10.7 牛客网NOIP模拟赛题解

    留个坑... upd:估计这个坑补不了了 如果还补不了就删了吧

  10. Python Web开发中的WSGI协议简介

    在Python Web开发中,我们一般使用Flask.Django等web框架来开发应用程序,生产环境中将应用部署到Apache.Nginx等web服务器时,还需要uWSGI或者Gunicorn.一个 ...