• 斗鱼连接弹幕Demo_pythonC#


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

    步骤如下,

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

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

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

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

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

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

    上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 + ''   #斗鱼独创序列化文本数据,结尾必须为''
        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 = "0"  # 房间号
        uid = "0"  # 用户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 = 8601;
            private static int ROOM_ID = 288016;
            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[0]; //域名转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 + "";
                byte[] bodyDataByte = Encoding.UTF8.GetBytes(dantaNew);
                byte[] cType = BitConverter.GetBytes(689);
    
                int dataLength = dantaNew.Length + cType.Length + 8;
                byte[] dataLengthByte = BitConverter.GetBytes(dataLength);
                byte[] dataLengthByte2 = BitConverter.GetBytes(dataLength);
                byte[] result = new byte[dataLength + 4];
    
                Array.Copy(dataLengthByte, 0, result, 0, 4);
                Array.Copy(dataLengthByte2, 0, result, 4, 4);
                Array.Copy(cType, 0, result, 8, 4);
                Array.Copy(bodyDataByte, 0, result, 12, bodyDataByte.Length);
                byte[] source = new byte[result.Length];
                Array.Copy(result, 0, source, 0, 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[1024];
                int bytes;
                while (true)
                {
                    bytes = tcpClient.Receive(recvBytes, recvBytes.Length, 0);//从服务器端接受返回信息
                    recvStr = Encoding.UTF8.GetString(recvBytes, 0, 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 >= 2)
                    {
                        string key = keyValue[0];
                        string[] textArr = new string[keyValue.Length - 1];
                        Array.Copy(keyValue, 1, textArr, 0, keyValue.Length - 1);
                        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 - 1];   //截取最后的丢弃数据,放在下个包的开头,防止数据丢失
                string[] newChatmsgArrayArr = new string[chatmsgArray.Length - 1];
                Array.Copy(chatmsgArray, 0, newChatmsgArrayArr, 0, chatmsgArray.Length - 1);
    
                foreach (string t in newChatmsgArrayArr)
                {
                    string[] msgType = t.Split('/');
                    if (msgType.Length >= 2)
                    {
                        string type = msgType[0];
                        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(40000);
                }
            }
        }
    }
  • 相关阅读:
    Facade Pattern简单随笔
    Bridge Pattern简单随笔
    重构:Encapsulate Collection
    VS2008 + Silverlight初步学习
    Proxy Pattern设计模式简单随笔
    Composite Pattern简单随笔
    重构:Move Method 笔记
    [ubuntu] ubuntu13.04 添加右键脚本功能
    Flex的“事件之旅”
    Flex与JavaScript交互(二)
  • 原文地址:https://www.cnblogs.com/likehc/p/10427130.html
Copyright © 2020-2023  润新知