一种链接弹幕的方法及移动终端与流程

文档序号:16811141发布日期:2019-02-10 13:39阅读:207来源:国知局
一种链接弹幕的方法及移动终端与流程

本发明涉及终端领域,尤其涉及一种链接弹幕的方法及移动终端。



背景技术:

随着互联网技术的快速发展,移动终端的功能越来越丰富,用于提升用户体验的弹幕功能也出现在移动终端上。

传统方案中通常都是将个人计算机(personalcomputer,pc)客户端的弹幕方案移植到移动终端当中。而对于移动终端有其特性,如网络不稳定,使用的是移动的4g网络,并且移动终端会进程移动,例如,移动终端移动到网络信号不好的地方;或者,从3g网络切换到4g网络;或者,从4g网络切换到无线保真(wirelessfidelity,wifi)网络;或者,从wifi切换到4g网络等。同时对于弹幕链接也会由于客户端的各种原因或者由于账号的原因,在链接时服务器会判断客户端有效性,从而会返回错误码等,因此客户端也需要考虑服务器的错误码。

传统方案中对于移动终端的客户端连接服务器的弹幕连接,只考虑了功能的可用性,并没有考虑到移动终端的特性,例如,网络不稳定,移动终端电量消耗,发热等特性。因此,移动终端和弹幕服务器之间的连接容易中断,并且恢复连接的过程缓慢。



技术实现要素:

本发明实施例提供了一种链接弹幕的方法及移动终端,用于对链接弹幕的过程进行了优化,提高了链接弹幕的处理效率。

本发明实施例的第一方面提供了一种链接弹幕的方法,包括:确定基于c++语言的消息订阅者抽象接口;确定所述抽象接口的防止对象拷贝功能;生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理;通过所述订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息;对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;确定每一个消息的订阅者。

在一种可能的实施例中,所述确定基于c++语言的消息订阅者抽象接口包括:定义消息订阅者的抽象接口类messageobserver,classmessageobserver;定义所述抽象接口类的析构函数virtual~messageobserver();定义抽象接口getobservermessagetype;定义抽象接口onrecvpackmsg。

在一种可能的实施例中,所述确定所述抽象接口的防止对象拷贝功能包括:将拷贝构造函数定义成空实现的函数private:voidoperator=(constmessageobserver&);将所述拷贝构造函数定义成私有的空实现的函数messageobserver(constmessageobserver&)。

在一种可能的实施例中,所述生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理包括:定义一个消息管理者类classobservermanager;生成std::multimap<std::string,messageobserver*>map_push_observer,并通过函数multimap对消息的订阅者进行管理;生成所述函数multimap的对象map_push_observer来存储所述订阅者。

在一种可能的实施例中,所述通过所述订阅者管理类observermanager的接口向编写接口提供订阅者来进行订阅感兴趣的消息包括:通过接口voidsetmessagetypeobserver(messageobserver*_taskobserver)提供给所有订阅者来往消息管理对象中注册的所有订阅者;通过抽象接口getobservermessagetype获取订阅者需要订阅的所有消息类型;获取返回值_cmdtypes,所述返回值存储了所有需要订阅的消息类型;定义一个迭代器对象std::set<std::string>::iteratoritr;初始化迭代器itr=_cmdtypes.begin();编写for循环来遍历得到每一个消息类型;调用multimap容器的insert接口向容器中插入订阅者。

在一种可能的实施例中,所述对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者包括:创建一个消息解析器pdecoder;将消息存储到mq.read(buf,len)中的buf中;通过调用所述消息解析器来解析所述buf获取消息的类型type;创建一个接口函数来对所述消息进行分发给对应的订阅者。

在一种可能的实施例中,所述创建一个接口来对所述消息进行分发给对应的订阅者包括:通过multimap容器的count函数获取所述消息的订阅者个数n;定义一个迭代器std::multimap<std::string,messageobserver*>::itratoritr;调用find函数来从迭代器中查找对应的订阅者;查找到后将所述订阅者存储到迭代器itr中;通过for循环将所述消息逐个的分发到每一个订阅者。

本发明实施例的第二方面提供了一种移动终端,包括:第一确定单元,用于确定基于c++语言的消息订阅者抽象接口;第二确定单元,用于确定所述抽象接口的防止对象拷贝功能;生成单元,用于生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理;订阅单元,用于通过所述订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息;解析分发单元,用于对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;第三确定单元,用于确定每一个消息的订阅者。

本发明第三方面提供了一种电子设备,包括存储器、处理器,其特征在于,所述处理器用于执行存储器中存储的计算机管理类程序时实现如上述第一方面任意一项所述的链接弹幕的方法的步骤。

本申请的第四方面提供了一种计算机可读存储介质,所述计算机可读存储介质中存储有指令,当其在计算机上运行时,使得计算机执行上述各方面所述的方法。

本申请的第五方面提供了一种包含指令的计算机程序产品,当其在计算机上运行时,使得计算机执行上述各方面所述的方法。

附图说明

图1为本发明实施例提供的一种链接弹幕的方法的流程图;

图2为本发明实施例提供的一种移动终端的结构示意图;

图3为本发明实施例提供的一种可能的电子设备的硬件结构示意图;

图4为本发明实施例提供的一种可能的计算机可读存储介质的硬件结构示意图。

具体实施方式

本发明实施例提供了一种链接弹幕的方法及移动终端,用于对链接弹幕的过程进行了优化,提高了链接弹幕的效率。

下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。

本申请的说明书和权利要求书及上述附图中的术语“第一”、“第二”、“第三”、“第四”等(如果存在)是用于区别类似的对象,而不必用于描述特定的顺序或先后次序。应该理解这样使用的数据在适当情况下可以互换,以便这里描述的实施例能够以除了在这里图示或描述的内容以外的顺序实施。此外,术语“包括”和“具有”以及他们的任何变形,意图在于覆盖不排他的包含,例如,包含了一系列步骤或单元的过程、方法、系统、产品或设备不必限于清楚地列出的那些步骤或单元,而是可包括没有清楚地列出的或对于这些过程、方法、产品或设备固有的其它步骤或单元。

实施例一

请参阅图1,本发明实施例提供的一种链接弹幕的方法的流程图,包括:

101、确定基于c++语言的消息订阅者抽象接口。

本实施例是基于c++语言来开发一种接口的设计,对消息订阅者设计一种统一的接口和抽象类,消息订阅者则必须实现抽象类的所有接口,并且消息管理类需要对所有消息订阅者进行管理。首先需要定义消息订阅者的抽象接口类messageobserver,classmessageobserver

{定义公开函数:public:messageobserver(){},其不需要实现什么功能是空功能;

定义其析构函数,并且必须加virtual表示是虚的析构函数,因为其是底层基类,为了防止内存泄露必须定义成虚函数,virtual~messageobserver(){};定义抽象接口函数getobservermessagetype表示获取订阅者订阅的所有消息类型,并且代码中加入了=0表示是一个抽象接口;

其中返回类型是stl容器的set容器,其中消息类型都是一个string类型,virtualconststd::set<std::string>&getobservermessagetype()=0;std::set<std::string&gt;表示可以存储多个不同的消息类型,并且消息类型不能相同。然后定义另一个抽象接口onrecvpackmsg,表示底层网络收到网络数据推送给订阅者。

此函数也必须是虚函数,并且必须加=0表示是抽象接口,其中参数conststd::string&_cmdtype表示当前推送的消息类型,其中参数constchar*pservermessage表示整个消息内容,virtualvoidonrecvpackmsg(conststd::string&_cmdtype,constchar*pservermessage)=0。

102、确定抽象接口的防止对象拷贝功能。

接下来由于此订阅者本实施例不允许其进行对象之间的拷贝,所以需要将拷贝构造函数定义成空实现的函数并且需要放到私有函数中。将赋值拷贝函数定义成私有的空实现的函数,private:voidoperator=(constmessageobserver&);将拷贝构造函数定义成私有的空实现的函数,messageobserver(constmessageobserver&);}至此,订阅者的接口类已完成其所有的定义。后续所有具体的订阅者则基于抽象类messageobserver来定义自己的实现。

103、生成消息订阅者管理类,订阅者管理类用于对订阅者进行管理。

首先定义一个消息管理者类classobservermanager;本实施例是基于消息类型来设计订阅者,每一种订阅者可以订阅自己感兴趣的消息类型,同时一个消息类型也可能有多个订阅者。那么其需要成员变量来存储所有的消息订阅者,同时有的消息订阅者会订阅多个消息类型。本实施例使用stl的multimap容器来对消息订阅者进行管理,之所以使用multimap是因为每一个消息订阅者可能会订阅多个消息,同时由于消息是很频繁的,所以也需要快速的能够查找到消息的所有订阅者,所以本实施例使用multimap来管理消息订阅者和查询消息订阅者。创建了一个multimap的对象map_push_observer来存储订阅者,其中multimap的健值是std::string表示消息的类型,multimap的vlaue值是messageobserver表示订阅者的对象指针,std::multimap&lt;std::string,messageobserver*&gt;map_push_observer。

104、通过订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息。

此接口提供给所有订阅者来往消息管理对象中注册的所有订阅者,必须是继承于本实施例之前编写的订阅者,基于抽象类messageobserver,voidsetmessagetypeobserver(messageobserver*_taskobserver){

接下来则需要获取订阅者需要订阅的所有消息类型。由于订阅者提供了统一的抽象接口getobservermessagetype来获取;

conststd::set<std::string>&_cmdtypes=_taskobserver->

其中返回值_cmdtypes则存储了所有需要订阅的消息类型,getobservermessagetype();接下来需要遍历这个返回值获取所有的消息类型;定义一个迭代器对象std::set&lt;std::string&gt;::iteratoritr;初始化迭代器itr=_cmdtypes.begin();接下来编写for循环来遍历得到每一个消息类型,循环的结束则判断迭代器是否等于_cmdtypes.end();for(;itr!=_cmdtypes.end();从迭代器中得到消息类型是strtype,++itr){stringstrtype=(*itr);接下来则调用multimap容器的insert接口来往容器中插入一个订阅者;map_push_observer.insert(std::pair&lt;std::string,messageobserver*&gt;(strtype,_taskobserver));其中strtype则表示订阅的消息类型,taskobserver则表示订阅者。

}

}至此此接口则完成了对订阅者进行注册的实现。

105、对消息订阅者管理类进行消息解析并将消息分发给所有的订阅者。

在步骤2中,移动终端收到了服务器的网络数据后,都会存到消息队列mq中。对队列中的每一条消息我们都需要进行解析消息,然后调用消息分发管理类来将消息进行分发给对应的订阅者。

首先需要创建一个消息的解析器pdecoder,messagedecoder*pdecoder=newmessagedecoder();然后从队列中读取一条消息来进行解析;

那么消息则存储到了buf中,mq.read(buf,len);然后调用消息的解析器来解析消息buf,从消息解析器中获取消息的类型type,pdecoder-&gt;parse(buf);从而得到了消息的类型为cmdtype,std::stringcmdtype=pdecoder-&gt;getitem("type");接下来则需要对消息进行分发。本实施例编写一个接口来对消息进行分发给对应的订阅者;voiddispatchmessage(char*pmessage,stringcmdtype){首先通过multimap容器的count函数来获取该消息的订阅者个数n,intn=map_push_observer.count(cmdtype);首先通过multimap容器的count函数来获取该消息的订阅者个数n,然后定义一个迭代器std::multimap&lt;std::string,messageobserver*&gt;::itratoritr;然后调用find函数来从迭代器中查找对应的订阅者。

查找到后存储到迭代器itr中,接下来则编写for循环来逐个的分发到每一个订阅者,itr=map_push_observer.find(cmdtype);

for(inti=0;i&lt;n;i++){

首先需要判断订阅者是否存在;

if(it!=map_push_observer_.end()&&it->second)

{然后对每一个订阅者调用其对应的消息接收接口onrecvpackmsg;

当消息订阅者订阅了消息类型为cmdtype的消息时,则会被触发onrecvpackmsg,会将消息分发给订阅者;从而订阅者可以对消息进行进一步的处理,it-&gt;second-&gt;onrecvpackmsg(cmdtype,pmessage);例如,是礼物消息订阅者,则可以在用户界面上展示当前收到的礼物消息;是弹幕消息订阅者,则可以在用户界面上展示当前收到的弹幕消息内容。

}

++it;

}

}至此完成了消息的订阅者的设计,订阅者的管理,订阅消息的分发。

106、确定每一个消息的订阅者。

本实施例确定每一个弹幕消息的订阅者。

classdanmumessageobserver:publicmessageobserver{

其必须实现messageobserver的所有抽象接口函数,public;

conststd::set<std::string>&getobservermessagetype(){

首先定义一个静态的容器对象types;

staticstd::set&lt;std::string&gt;types;

判断下如果types已经存储了所有需要订阅的消息则直接返回;否则没有存入订阅的消息。

if(!types.empty()){

returntypes;

}

types.insert(danmu_type);

通过调用insert函数则可以往容器对象中插入一个消息类型。本实施例是弹幕消息则订阅danmu_type;

}实现另一个抽象接口onrecvpackmsg。

voidonrecvpackmsg(conststd::string&_cmdtype,constchar*pservermessage){

当消息订阅分发管理模块收到弹幕消息后,则会调用此接口来将弹幕消息分发给订阅者。本实施例是弹幕消息订阅者,则会收到弹幕消息;收到弹幕消息后,则可以将其放到显示区域进行弹幕内容的显示,那么其中pservermessage则是对应的弹幕消息。

本实施例则调用ui显示层的弹幕显示接口来显示弹幕。

danmuuishowpuishow=newdanmuuishow();

puishow-&gt;showdanmu(pservermessage);然后调用其showdanmu来将弹幕显示到弹幕显示区域中。

}

本发明实施例,通过对消息类型的分类,再通过消息管理模块则对所有的消息订阅者进行管理,当接收到消息后,来分发给订阅者。消息管理者在不知道订阅者的情况下能够进行管理,提高了链接弹幕的处理效率,使用户无感知进而提高用户体验。

可选的,在上述图1对应的实施例的基础上,本发明实施例提供的链接弹幕的方法的可选实施例中,所述确定基于c++语言的消息订阅者抽象接口包括:定义消息订阅者的抽象接口类messageobserver,classmessageobserver;定义所述抽象接口类的析构函数virtual~messageobserver();定义抽象接口getobservermessagetype;定义抽象接口onrecvpackmsg。

可选的,在上述图1对应的实施例的基础上,本发明实施例提供的链接弹幕的方法的可选实施例中,所述确定所述抽象接口的防止对象拷贝功能包括:将拷贝构造函数定义成空实现的函数private:voidoperator=(constmessageobserver&);将所述拷贝构造函数定义成私有的空实现的函数messageobserver(constmessageobserver&)。

可选的,在上述图1对应的实施例的基础上,本发明实施例提供的链接弹幕的方法的可选实施例中,所述生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理包括:定义一个消息管理者类classobservermanager;生成std::multimap&lt;std::string,messageobserver*&gt;map_push_observer,并通过函数multimap对消息的订阅者进行管理;生成所述函数multimap的对象map_push_observer来存储所述订阅者。

可选的,在上述图1对应的实施例的基础上,本发明实施例提供的链接弹幕的方法的可选实施例中,所述通过所述订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息包括:通过接口voidsetmessagetypeobserver(messageobserver*_taskobserver)提供给所有订阅者来往消息管理对象中注册的所有订阅者;通过抽象接口getobservermessagetype获取订阅者需要订阅的所有消息类型;获取返回值_cmdtypes,所述返回值存储了所有需要订阅的消息类型;定义一个迭代器对象std::set&lt;std::string&gt;::iteratoritr;初始化迭代器itr=_cmdtypes.begin();编写for循环来遍历得到每一个消息类型;调用multimap容器的insert接口向容器中插入所述订阅者。

可选的,在上述图1对应的实施例的基础上,本发明实施例提供的链接弹幕的方法的可选实施例中,所述对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者包括:创建一个消息解析器pdecoder;将消息存储到mq.read(buf,len)中的buf中;通过调用所述消息解析器来解析所述buf获取消息的类型type;创建一个接口函数来对所述消息进行分发给对应的订阅者。

可选的,在上述图1对应的实施例的基础上,本发明实施例提供的链接弹幕的方法的可选实施例中,所述创建一个接口来对所述消息进行分发给对应的订阅者包括:通过multimap容器的count函数获取所述消息的订阅者个数n;定义一个迭代器std::multimap&lt;std::string,messageobserver*&gt;::itratoritr;调用find函数来从迭代器中查找对应的订阅者;查找到后将所述订阅者存储到迭代器itr中;通过for循环将所述消息逐个的分发到每一个订阅者。

实施例二

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,基于socket客户端设计链接方案,具体实施例如下:

步骤1、创建套接字socket的相关数据,并与弹幕服务器进行链接。移动终端创建套接字socket的相关数据,并与弹幕服务器进行链接。其中,相关数据包括intsockfd=socket(af_inet,sock_stream,0)、地址族servaddr.sin_family=ip、端口号(servaddr.sin_port=htons(port)和系统函数intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))。

步骤2、确定网络中断对象socketbreaker。移动终端确定网络中断对象socketbreaker,该socketbreaker用于触发socket中断当前链接。具体的,移动终端定义一个对象函数socketbreakerpipe,该对象函数socketbreakerpipe用于对socket链接触发其终止当前链接。

步骤3、创建网络的选择对象select。

移动终端创建网络的选择对象select,该select用于监控socket的状态信息。移动终端创建socketselectselect(pipe)对象连监控socket的状态信息。具体的,先将创建的socket与select对象进行关联,从而使得select对象对socket进行监控。select具体包括:用于对socket的异常情况进行关联的函数select.exception_fd_set(socket);用于对socket的读写进行关联的函数select.write_fd_set(socket)。当socket产生了异常或者可以进行发送网络数据,或者可以读取网络数据时select都可以监控到,例如,使用函数intselectret=select.select(timeout)查看当前socket的状态。对返回值进行判断,如果返回值selectret为0,则表示设置的timeout超时了,本次链接失败。

步骤4、创建事件对象event。

移动终端创建事件对象event,该event用于通知socket的状态信息。移动终端创建事件对象event,用于通知上层当前的socket的状态信息。

具体包括:函数event.onerror(connecttimeouterr),用于调用事件对象event的onerror函数,并且参数设置为connecttimeouterr,表示链接超时错误;函数event.onerror(connectingerr),如果返回值selectret&lt;0,则表示链接出错,调用函数event.onerror(connectingerr)通知上层当前socket出错。否则说明此次链接是成功的,则继续后续的网络发包和收包操作;函数event.onconnect(),用于调用事件对象event的onconnect告知上层当前链接成功了。

步骤5创建socket的线程对象thread和执行thread的执行函数void__runthread()。

移动终端创建socket的线程对象thread和执行thread的执行函数void__runthread()。首先创建一个线程对象threadthread_,同时编写一个线程执行的函数void__runthread(),然后基于boost基础库来将线程执行函数进行绑定并传递给线程的执行函数,如thread(boost::bind(&mtcpclient::__runthread,this)),从而此线程的执行逻辑则是取执行_runthread函数的功能。当所有数据都初始化好后,则移动终断开调用线程对象的函数start来开始此线程功能的执行,如thread_.start()。

步骤6、在执行函数中创建while循环监控socket的状态。

移动终端在执行函数中创建while循环监控socket的状态。通过创建(编写)的while循环不断的监控socket的状态来处理相关事件。此while循环的整个逻辑功能是在runthread函数逻辑中,因此socket的独立线程会执行此段代码逻辑。创建的一个while循环来不断的从socket中接收弹幕服务器发送过来的网络数据,并且不断的从socket来将客户端需要发送的网络数据发送个弹幕服务器。

首先查看socket的状态,while(true){intselectret=select.select();

若当前无可读数据无可写数据,并且没有异常,则直接返回等待下个状态,if(0==selectret){continue;}

若当前socket产生了异常,则使用事件对象event来告知上层当前出先了io读写错误,if(0&gt;selectret){event.onerror(ioerr);

通过return结束当前循环,return;},否则接下来对socket查看其是否有异常;

通过select对象来判断socket是否在异常集合exception_fd_isset中,从而知道当前socket是否异常;

if(select.exception_fd_isset(socket)){event.onerror(ioerr);

并且通过return结束当前循环return;}

步骤7、读取socket中接收到的弹幕服务器发送的数据。

移动终端读取socket中接收到的弹幕服务器发送的数据。移动终端判断socket是否可以读取弹幕服务器的网络数据,具体过程如下:通过select对象来判断socket是否在读取集合read_fd_isset中,从而知道当前socket是否可以读数据;此时通过调用event对象的onread来告知上层当前socket有数据可以读取,从而可以读取弹幕服务器的网络数据,if(select.read_fd_isset(socket)){event.onread();}

步骤8、将需要发送的数据通过socket发送至弹幕服务器。

将需要发送的数据通过socket发送至弹幕服务器。移动终端判断socket是否可以发送网络数据到弹幕服务器的网络数据,具体过程如下:通过select对象来判断socket是否在读取集合里面write_fd_isset从而知道当前socket是否可以发送数据;此时通过调用event对象的onsend来告知上层当前socket可以发送网络数据,从而可以将客户端的数据发送到弹幕服务器,if(select.write_fd_isset(socket_)){event.onsend();}}在此步骤中编写的while循环会不断的监控socket是否出现异常,是否可以读取数据,是否可以发送数据到服务器,从而使得程序中不断的接收弹幕服务器数据,发送数据给弹幕服务器。

本发明实施例中,创建套接字socket的相关数据,并与弹幕服务器进行链接;确定网络中断对象socketbreaker,socketbreaker用于触发socket中断当前链接;创建网络的选择对象select,select用于监控socket的状态信息;创建事件对象event,event用于通知socket的状态信息;创建socket的线程对象thread和执行thread的执行函数void__runthread();在执行函数中创建while循环监控socket的状态;读取socket中接收到的弹幕服务器发送的数据;将需要发送的数据通过socket发送至弹幕服务器。本发明实施例,对弹幕链接采用了异步模式,为一个socket链接创建一个独立的线程来执行整个socket的链接过程,对链接弹幕的过程进行了优化,将socket收到数据后,通过事件的形式告知上层,使得上层只用处理网络数据,不需要关注socket的具体实现,实现了分层设计的思想。提高了链接弹幕的效率。

实施例三

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,基于socket的上层的消息队列的设计,来处理socket收到的数据和需要发送的数据,具体实施例如下:

步骤1、确定c++的类来封装网络数据的处理过程。

本步骤的功能则是对应步骤1中的上层代码,其会收到event的所有事件消息。首先本实施例设计一个c++的类来封装所有网络数据的处理过程,并且将发生数据,接收数据,以及整个socket的过程中对socket的错误进行了分离的设计,使得逻辑更为清晰,接收数据事件只负责收到的数据,发生数据事件只负责发送数据,socket错误事件只负责socket错误处理。classtcpclient{其中设计了一个消息队列来存储所有需要发送给服务器的网络数据,同时也会实现所有event的事件消息。对于每一个网络数据,都是一串字符串,所以每一条消息本文使用一个char*的数组来存储;而由于消息队列需要频繁的存储网络消息,取出网络消息,因此对于数据结构和算法来说,比较适合使用链表结构来存储,链表具有快速的插入数据和删除数据的功能。}

步骤2、生成标准模板库stl的容器来存储消息。

本实施例使用标准模板库(standardtemplatelibrary,stl)的容器list链表容器来存储网络消息,stl::list&lt;char*&gt;listmessage;定义一个这样的对象listmessage则是本实施例的消息队列存储过程。当需要发送一条网络数据时,则会先将其存储到消息队列中,因为消息的发送本实施例采用异步的过程,防止当socket不可以发送数据时,产生丢包,而通过消息队列则可以将所有需要发送的网络数据进行缓存起来,当socket可以发送数据时则再从消息队列中取出数据进行发送。具体存储消息如下:调用list类的push_back函数则将一条消息数据存储到了链表中,存储到链表的最尾端,listmessage.push_back(sendbuff);通过调用list类的front函数则可以从链表中取出链表中最前面的消息数据,取出消息如下:char*buf=listmessage.front();并且需要调用list类的pop_front函数来将消息数据从链表中摘除掉,listmessage.pop_front()。

步骤3、创建事件的onread事件,onread表示读取了弹幕服务器下发的网络数据。

在步骤1中会收到底层socket的事件函数包括onerror事件,onread事件,onsend事件。voidonread(){对于onread事件,则是当底层代码发送socket可以读取网络数据时,会通知到本层代码来读取网络数据。本实施例则可以调用系统函数recv来读取socket中的网络数据,ssize_tret=recv(socket,(char*)buf,len,0);其中buf则是用于存储读取到的数据,len则是读取的长度。此函数调用完成则会将网络数据存储到buf中,从而完成了一条网络数据的接收,收到数据后则会存储到消息队列中;定义一个消息队列messagequeuemq;其中mq则是消息队列对象,同时本实施例支持多个线程同时往消息队列中存入收取到的消息,也支持多个线程同时从消息队列中取消息。所以本实施例定义一个mutexwrite_mutex;互斥对象write_mutex;然后定义一个锁来对互斥对象来加锁,scopedlocklock(write_mutex_);然后再调用消息队列对象来存储收到的网络数据,mq.write(buf,len);表示存储了一条消息到消息队列中。本实施例则将收到的所有消息都放到消息队列中mq中。}

步骤4、创建事件的onsend事件,onsend表示可以发送数据到弹幕服务器。

voidonsend(){对于onsend事件,则是当底层代码发现当前socket可以发送网络数据时,则会从消息队列中取出消息进行发送;对于所有发送给服务器的消息本文会使用一个消息队列来进行缓存,从而可以使得应用层可以多线程的来发送数据。定义一个消息队列messagequeuemq2;其中mq2是消息队列对象,发送消息则是不断的从消息队列中取出消息进行发送。为了支持多线程本实施例定义一个mutexread_mutex;互斥对象read_mutex;然后定义一个锁来对互斥对象来加锁,scopedlocklock(read_mutex_);然后再调用消息队列对象来取出需要发送的网络数据进行发送,mq2.read(buf,len);从而得到需要发送的数据buf,然后使用系统函数来将消息进行发送send(socket,buf,len);}

步骤5、创建事件的onerror事件,onerror表示socket在整个链接或者是收发数据时遇到了错误。

voidonerror(intnstatus){对于onerror事件,则是当底层代码发现当前socket链接发生了错误,则会通知应用层,socket产生了错误,需要进行处理,并且给出了具体的状态码。当收到状态码后,为了更好的设计,所以需要对状态码进行区分,有些状态码是不需要进行重连的,有些状态码则是需要重连的,具体此处不做限定。}}

本实施例基于分层的设计思路,上层将底层的网络收发进行解耦合,提供更好的分层设计,优化编码,逻辑处理更为清晰。本实施例中设计上层的socket的消息处理队列,来处理socket的收到的数据和需要发送的数据,提高了链接弹幕的处理效率。

实施例四

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,基于socket状态码来设计链接方案,设计是否进行重连,具体实施例如下:

步骤1、生成存储状态码的映射表。

首先状态码有很多种,当socket出现错误时,需要拿当前产生的状态码去查询是否需要进行重连。一旦涉及到查询则需要考虑查询的性能。本实施例基于标准模板库stl容器的map容器来对状态码进行存储。查询时则基于map容器则查找速度会非常快,map&lt;int,bool&gt;mapstatus;本实施例定义一个map&lt;int,bool&gt;型的对象mapstatus。其中。第一个参数int是map容器的key值,用于存储socket的状态码,第二个参数bool用于存储当前状态码是否需要进行重连。接下来所有状态码是已知的,那么需要对所有状态码进行标记并存储到map容器对象中。状态码中用于指示错误的状态码也可称为错误码。

步骤2、确定套接字socket整个链接过程中所有可能的状态及状态码,状态码与可能的状态一一对应。

所有状态码则包括整个socket的整个使用过程中可能产生的所有情况如下:

kiniterr,表示初始化错误码。ksocketthreadstart,表示socket初始化线程。ksocketthreadstarterr,表示socket初始化线程错误码。ktcpconnectiperr,,表示socket链接ip时产生了错误码。ktcpconnecttimeouterr,表示socket产生了超时。ktcpioerr,表示socket发送数据产生了io错误码。ktcpdisconnectedbyremote,表示socket被服务器关闭了链接。ktcpdisconnected,表示socket链接断开了。对于以上所有的状态码本实施例使用整数来表示。并且所有错误码不能有相同的数值。

本实施例使用c++语言的一个技巧枚举变量类型来设计,以使得编译器会帮助检查所有状态码的值是不重复的。

enumsocketstatus{kiniterr=1,ksocketthreadstart,

ksocketthreadstarterr,ktcpconnectiperr,ktcpconnecttimeouterr,

ktcpioerr,ktcpdisconnectedbyremote,ktcpdisconnected,

};

本实施例定义了一个枚举变量enumsocketstatus;在枚举变量中把所有状态码都包含进来。并且对第一个状态码赋值为1,那么后续其他的状态码则会自动的在第一个状态码的基础上加1,从而使得所有状态码的值不可能重复。例如ksocketthreadstart的值则是2,ksocketthreadstarterr的值则是3,具体此处不再赘述。

步骤3、将状态码存储到映射表中。

对于所有这些状态码来说,有的是链接时产生了错误码,有的是数据传输过程中产生了错误码,有的是属于正常的关闭socket链接。那么对于重连时则需要区别对待,有些是发送了错误,马上要进行重连,从而使得用户可以马上看到弹幕,防止弹幕链接断开;而有些属于客户端正常关闭,或者服务器正常关闭客户端不需要进行重连。接下来则需要对mapstatus容器对象进行状态码的赋值。下面对具体代码进行说明。

mapstatus[kiniterr]=true;此行代码表示往mapstatus容器中存储健值为kiniterr,并且是否重连设置为true,表示此状态码需要重连。

mapstatus[ksocketthreadstart]=false;此行代码表示往mapstatus容器中存储健值为ksocketthreadstart,并且是否重连设置为false,由于只是进行了初始化所以不需要重连。

mapstatus[ksocketthreadstarterr]=true;此行代码表示往mapstatus容器中存储健值为ksocketthreadstarterr,并且是否重连设置为true,由于是创建线程错误了,所以需要进行重连。

mapstatus[ktcpconnectiperr]=true;此行代码表示往mapstatus容器中存储健值为ktcpconnectiperr,并且是否重连设置为true,由于是连接ip时产生了错误了,所以需要进行重连。

mapstatus[ktcpconnecttimeouterr]=true;此行代码表示往mapstatus容器中存储健值为ktcpconnecttimeouterr,并且是否重连设置为true,由于产生了超时错误,所以需要进行重连。

mapstatus[ktcpioerr]=true;此行代码表示往mapstatus容器中存储健值为ktcpioerr,并且是否重连设置为true,由于产生了数据读写错误,所以需要进行重连。

mapstatus[ktcpdisconnectedbyremote]=false;此行代码表示往mapstatus容器中存储健值为ktcpdisconnectedbyremote,并且是否重连设置为false,由于是服务器关闭了链接,所以不需要进行重连。

mapstatus[ktcpdisconnected]=true;此行代码表示往mapstatus容器中存储健值为ktcpdisconnected,并且是否重连设置为true,由于是链接不稳定断开了,所以需要进行重连。至此,已经将所有状态码的值存储到了mapstatus容器中,并且对每一种状态码是否进行重连都设置好了是否重连。

步骤4、根据状态码判断是否需要进行重连的查询。

有了查询接口当socket发送错误后,依据错误码来查询是否重连的接口,如果需要重连则自动进行重连,否则断开链接。如果有其他的新的状态码需要加入,则可以首先在enumsocketstatus中加入一个状态码类型,然后在mapstatus容器中对其进行赋值操作。接下来为了设计更好的封装性,所以本实施例会设计一个接口来提供查询一个状态码是否需要进行重连。具体过程如下:

boolgetretryflag(intnstauts)

{其中,接口返回值为bool性变量,为true表示要重连,为false表示不重连;接口名称是getretryflag;其中,只有一个参数intnstauts,表示查询的状态码;

然后需要使用map容器的查询接口find接口:

map&lt;int,bool&gt;::iteratoritr;首先定义一个map容器的迭代器;

itr=mapstatus.find(nstauts);然后调用其find接口来查询当前状态码,查询结果则存储在itr迭代器中,其中迭代器的value值则是结果值。

retrun(*itr).value;那么则将迭代器的value值作为返回结果。

}

有了状态码查询接口,则可以通过查询来获得当前的状态是否需要进行重连,从而给应用层提供了基本的技术支持,应用层依据查询结果来进行是否重连。

本发明实施例,基于socket状态码来设计链接弹幕的方案,当移动终端的网络不稳定断开了弹幕时,移动终端也可以使用自动重连技术重连弹幕,提高了链接弹幕的处理效率,使用户无感知进而提高用户体验。

实施例五

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,基于服务器下发的错误码来设计链接方案,设计是否进行重连,具体实施例如下:

在弹幕链接过程中,弹幕服务器会对弹幕链接的客户端的身份进行核查,同时也会对链接的协议进行检查,包括:检测协议类型,检查协议字段。同时也会对每个字段判断是否合法,是否有非法字符,或者是否有错误协议等。同时对于链接过程中客户端也可能会发送一些异常数据等等,因此弹幕服务器会对客户端的不同情况返回不同的错误码。例如,客户端登陆弹幕服务器,其使用的token字段并不是客户端账号密码登陆的token,此时弹幕服务器会返回特定的错误码表示该链接token错误。

具体的,如观看直播时,链接弹幕服务器给的房间号是在直播平台不存在的,所以此时弹幕服务器也会返回特定错误码;当客户端退出房间时,此时会断开弹幕服务器的链接,此时弹幕服务器会返回特定的错误码;同样的如果当前用户的网络不稳定,客户端长时间没有给服务器发送特定的心态数据包,服务器判断客户端网络不稳定,也会认为客户端丢失了链接,此时也会返回特定的错误码;当客户端与服务器进行网络数据传输时,可能也存在一定概率客户端发送给服务器的数据不正确,那么服务器也会发送特定错误码给客户端;还可能对于用户有一个账号多个设备的情况,同一个账号同时使用多个不同的设备登录同一个直播间,服务器也会返回指定的错误码。因此对于不同的错误码需要特殊对待,只有一些错误码才需要进行重连。具体的实现方式如下所示:

步骤1、生成错误码消息订阅者类errormessageobserver。

生成一个服务器错误消息的订阅者,classerrormessageobserver:publicmessageobserver{其必须实现messageobserver的所有抽象接口函数,public:conststd::set<std::string>&getobservermessagetype(){首先定义一个静态的容器对象types,staticstd::set&lt;std::string&gt;types;如果types已经存储了所有需要订阅的消息则直接返回。if(!types.empty()){returntypes;}否则没有存入订阅的消息,types.insert(error_type);通过调用insert函数则可以往容器对象中插入一个消息类型。本实施例中若是错误消息则订阅error_type。}

步骤2、确定错误码消息订阅者的接口。

实现另一个抽象接口onrecvpackmsg。voidonrecvpackmsg(conststd::string&_cmdtype,constchar*pservermessage){当消息订阅分发管理模块收到错误消息后,则会调用此接口来将错误消息分发给订阅者,本实施例是错误消息订阅者,则会收到服务器发送过来所有的错误消息,那么其中pservermessage则是对应的弹幕消息。

首先需要对消息进行解析,也从错误消息中获取消息的错误码。例如收到一条同一个账号登录多个设备并且进入同一个直播间时会收到服务器的消息如下:type@=error/code@=500;其中type@=error表示此消息的消息类型为error。其中code@=50表示此消息的错误码为500。

步骤3、对消息进行解析。

收到消息后,需要对消息进行解析。首先需要创建一个消息的解析器,pdecodermessagedecoder*pdecoder=newmessagedecoder();接着调用解析器的parse接口来对消息进行解析,pdecoder-&gt;parse(pservermessage);解析完成后,需要获取其code字段的值,从而得到错误码的值,interrorcode=pdecoder-&gt;getitem("code");调用其getitem接口从而得到code字段的错误码的值,并将其赋值给errorcode变量。得到了errorcode后,需要errorcode来具体实现对应的逻辑功能:首先本实施例会对errorcode进行分类,有些是需要进行重连的,有些是不需要进行重连的,有些是需要再单独处理的。需要重连的错误码本实施例会将其放到一个枚举变量中,enumretrycode{kdisconnect=50,表示错误码是50,由于网络不稳定造成链接丢失;kdataerror=60,表示错误码是60,由于其数据字段丢失;kioerror=70,表示错误码是70,由于传输过程中数据错误。};那么对于此类错误码则都是需要重连的。

步骤4、生成错误码的map容器并对错误码进行关联。

同样的本实施例会设计一个map容器来存储重连的错误码,当服务器发送了错误消息后,则会查询重连的错误码。同样的定义另一个map容器存储不需要重连的错误码。

本实施例定义了一个mapretry容器对象,map&lt;int,bool&gt;mapretry。mapretry[kdisconnect]=true;mapretry[kdataerror]=true;mapretry[kioerror]=true;对于不重连的操作如下:enumnotretrycode{ktick=500,表示错误码是500,由于其同一个账号登陆多个设备同时登陆同一个直播间,弹幕服务器会踢掉一个客户端。此时则不能进行重连。kclientdisconnect=501,表示错误码是501,表示客户端主动关闭了当前的链接,因此不需要进行重连。

}

同样的定义另一个map容器存储不需要重连的错误码。

map&lt;int,bool&gt;mapnotretry;

mapnotretry[ktick]=false;

mapnotretry[kclientdisconnect]=false。

步骤5、确定具体消息的错误码来重连逻辑。

当收到一条错误消息后,首先需要依据错误码来查询是否需要重连,然后再查询是否不需要重连,具体的过程如下:

map&lt;int,bool&gt;::iteratoritr;

首先定义一个迭代器。

itr=mapretry.find(errorcode);

然后使用从错误消息中获取的错误码来进行查询。

if(itr==mapretry.end()){

说明没有在重连错误码中查找到;

接下来则需要查找不需要重连的错误码;

itr=mapnotretry.find(errorcode);

if(itr==mapnotretry.end()){

如果没有找到说明也不在不需要重连的错误码中,那么说明这些错误码需要进一步的进行处理;

if(errorcode==800){

其中错误码800表示房间号错误,此时客户端则可以从全局信息中心获取房间信息。

intnroomid=datacenter()::instance()-&gt;getroomid();

本实施例会将直播间相关信息存储到数据中心datacenter中,当链接弹幕服务器发现房间号错误时,则可以再次从信息中心获取房间号来进行重连。

获取都后再次的进行重连;

intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));

继续使用步骤1的代码来进行网络链接;

}elseif(errorcode==900){

其中错误码900表示当前链接的token失效了,此时正确的做法则是重新请求token然后再进行重连,那么此时客户端则会使用http请求来获取新的token,获取到新的token后再来进行弹幕链接。

token=http.get()

本实施例通过http的get函数来获取token数据,得到token后,则再次的进行重连。

}

}else{说明找到了,说明不需要进行重连;

本实施例则会调用之前的pipe对象的break函数来断开当前的网络链接;

pipe.break();

}

}else{说明找到了,则进行重连;

intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));继续使用步骤1的代码来进行网络链接。

}

本发明实施例,而基于移动终端在发现弹幕链接过程中的一些错误码,移动终端也可以使用自动重连技术重连弹幕,提高了链接弹幕的处理效率,使用户无感知进而提高用户体验。

实施例六

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,基于网络环境设计链接方案,具体实施例如下:

随着互联网协议6(internetprotocolversion6,ipv6)的普及,对于移动终端来说,可以使用互联网协议4(internetprotocolversion4,ipv4)的网络,也可以使用互联网协议6(internetprotocolversion6,ipv6)的网络,而如果弹幕服务器本身只支持ipv4的网络,则需要将移动终端的客户端进行ipv6的地址转换成ipv4的地址,通过ipv4的地址来链接服务器。需要确保支持链接弹幕时,移动终端在ipv6的环境下也可以链接上互联网协议4(internetprotocolversion4,ipv4)的弹幕服务器。当基于多个互联网协议(internetprotocol,ip)或者域名情况下,如何快速选择可用的ip,如何尽快的让用户看到弹幕成为首要的问题。本发明提供了一种解决方案,具体步骤如下:

步骤1、确定弹幕服务器所属的网络类型。

当客户端(安装在移动终端上)开始去链接弹幕服务器时,由于服务器的ip都是ipv4,所以不管本地是ipv4还是ipv6,都需要转换成ipv4的网络地址来进行链接,那么客户端则需要判断当前是ipv4的网络还是ipv6的网络,同时如果是ipv6的网络还要转换成ipv4的网络。

步骤2、判断当前网络环境是ipv4还是ipv6。

本实施例需要判断当前网络是不是ipv6的网络环境,如果是则说明是ipv6,如果不是则说明当前是ipv4。本实施例通过定义一个ipv6的网络地址去链接,如果成功说明当前支持的是ipv6网络。对于ipv4本实施例选择服务器的ip地址2000::0,而对于ipv6本实施例则使用2000::0做为服务器的地址去链接。具体实现如下:

定义一个socket的地址信息变量structsockaddr_inaddr;addr.sin_len=sizeof(sockaddr_in),addr.sin_family=af_inet6,其中此次表明是使用ipv6,然后对其进行赋值,其中端口号使用80端口,然后ip使用2000::是一个ipv6的地址,addr.sin_port=80,addr.sin_addr.s_addr={//2000::0x20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}。

接着创建一个socket,并且使用用户数据报协议(userdatagramprotocol,udp)协议,因为本实施例只是测试当前服务器的ip是否可以连上,所以本实施例采用udp,因为udp的速度比较快,ints=socket(pf,sock_dgram,ipproto_udp)。

然后调用系统函数connect进行链接,intret=connect(s,addr,addrlen);如果返回值ret为0说明链接成功,否则链接失败。如果链接成功说明当前支持ipv6,如果成功了说明当前是ipv6网络环境,否则是ipv4网络环境,如果是ipv4则可以直接进行链接弹幕服务器。

步骤3、将ipv6的网络地址转换成ipv4的网络地址。

如果是ipv6则还需要进行地址转换,通过系统函数socket_inet_ntop对ipv6的地址转换成ipv4的地址。具体实现如下:首先定义一个ipv4的变量用于存储转换后的ipv4地址,charipv4[100]={0};接下来则调用系统函数socket_inet_ntop进行转换,其中参数addr.sin6_family表示ipv6地址协议,其中参数addr.sin6_addr是输入的ipv6的地址;其中参数ipv4则会存储转换后的结果,socket_inet_ntop(addr.sin6_family,&(addr.sin6_addr),ipv4,sizeof(ipv4))。移动终端确定了当前网络环境是否是ipv4还是ipv6,如果是ipv6由于服务器使用的是ipv4的地址所以需要对地址进行转换,从而依据本实施例的移动终端应用在ipv6的网络环境下也可以链接到ipv4的弹幕服务器。

步骤4、基于转换后的ipv4地址连接弹幕服务器。

当将ipv6的网络地址转换成ipv4的网络地址后,则移动终端可以直接连接弹幕服务器。建立与服务器的连接,intsockfd=socket(af_inet,sock_stream,0)。地址族servaddr.sin_family=ip;端口号(主机字节序到网络字节序),servaddr.sin_port=htons(port);调用系统函数connect开始连接弹幕服务器,通过connect则可以直接进行连接弹幕服务器,intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr))。

本发明实施例,将ipv6的网络地址转换为ipv4的网络地址,确保移动终端在ipv6的环境下也可以链接上仅支持ipv4的弹幕服务器,提高了链接弹幕的处理效率。

实施例七

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,对当前网络环境的网络切换进行监控,基于监控结果来进行重连设计链接方案,具体实施例如下:

由于移动终端设备通常是移动的,和pc设备差异很大,进程可能会随着使用者进行位置移动,随着移动则会产生网络的切换。例如,在家使用wifi,则出门后会切换到4g,,也可能供信号很强的网络切换到信号弱的网络,而对于观看直播来说,需要尽量的察觉网络变换,从而及早的进行链接。具体的,从4g网络切换到了wifi网络,则需要监控当前网络发生了变换,从而断开当前的网络链接,重新使用新的网络进行链接,可以更快速的在新的网络中链接上弹幕服务器。而当从有网络或者强信号网络切换到无信号网络或者弱信号网络时,为了节省电量,在这种时候则需要使用网络底层的超时重连,并且设置为更为长时间的超时,让底层网络进行重连,以此来节省电量。本实施例则依据android系统来设计链接弹幕的方法,具体步骤如下:

步骤1、定义一个网络链接类型来存储当前的网络类型,并生成网络类型的状态码。

本实施例定义一个网络链接类型来存储当前的网络类型,确定一个枚举变量来存储网络类型,定义为netstatus枚举类型。

enumnetstatus{

knonet=1,

kwifi,

k4g,

};其中定义了knonet表示当前没有网络,并且赋值为1;定义了kwifi表示当前网络为wifi,则自动赋值为2;定义了k4g表示当前网络为4g网络,则自动赋值为3,定义了网络状态netstatuscurstatus。

步骤2、生成android系统中网络状态的监控类。

对于android系统来说,监控当前网络变化需要先编写一个子类继承于系统的broadcastreceiver类,并且对其中的网络事件进行监控。本实施例中定义了networkchange类,其继承于系统的broadcastreceiver,即生成了继承于系统的broadcastreceiver类的子类networkchange类。

publicclassnetworkchangeextendsbroadcastreceiver{

接下来则重载其接口来接收网络变化;

@override

publicvoidonreceive(contextcontext,intentintent){

判断当前是否连接上了网络;

if(connectivitymanager.connectivity_action.equals(intent.getaction())){

接着通过connectivitymanager属性来判断当前的网络状态;

接着定义一个networkinfo对象info,networkinfoinfo;

然后获取当前的网络信息。具体则可以通过系统函数getparcelableextra来获取当前网络信息,并且传入参数为connectivitymanager.extra_network_info。

info=intent.getparcelableextra(

connectivitymanager.extra_network_info);

得到info信息后,判断当前是wifi,4g还是没有网络;

if(info.gettype()==connectivitymanager.type_wifi){

通过info的接口gettype可以得到当前的网络类型;

如果网络类型是connectivitymanager.type_wifi,表示当前链接的是wifi,并将网络状态类型设置为wifi网络;curstatus=kwifi;

}elseif(info.gettype()==connectivitymanager.type_mobile){

如果当前链接的不是wifi,则继续判断其类型是不是connectivitymanager.type_mobile,如果是,表明当前链接的是手机的4g网络。

curstatus=k4g;

}else{curstatus=knonet;

即表示当前没有网络可以用,则处于网络断开状态。

}

}

}

}

步骤3、对监控类进行实例化和系统注册。

要想监控当前网络还需要调用系统函数来进行注册。

首先需要创建一个intentfilterfilter=newintentfilter();然后通过调用intentfilter的addaction函数来加入对网络的监听,filter.addaction(connectivitymanager.connectivity_action);其中传入参数为connectivitymanager.connectivity_action。接着实例化一个networkchange类的对象,networkchangenet=newnetworkchange();接下来调用系统的registerreceiver函数来注册网络监控,registerreceiver(net,filter);从而实现了对网络的监控。

步骤4、通过registerreceiver函数对网络类型的状态码进行监控,并根据状态码的变化情况链接弹幕服务器。

接下来需要依据当前网络变化来对弹幕链接做一些调整。我们监控网络获取了当前的网络状态,那么本实施例会与之前的网络状态进行比较,如果网络状态没有发生变化,则不对当前弹幕链接进行操作。如果网络状态发生了变化则需要依据当前状态变化来对网络进行变化。在程序启动时定义变量netstatusprevstatus=knonet;并且将其值初始化为无网络的状态。当检查了一次网络状态后,则会将当前的网络赋值给它来存储之前的网络状态。prevstatus=curstatus。对于网络切换时,我们会依据之前的网络和现在的网络状态来对弹幕链接是否进行操作来判断。

if(prevstatus==curstatus){

如果当前的网络状态和之前的网络状态是一样的,则说明网络没有发生变化,则弹幕链接不发生变化。

}else{if(prevstatus==knonet){

如果之前是没有网络,现在有网络了,则需要进行弹幕链接。

继续使用步骤1的代码来进行网络链接,intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));

}elseif(prevstatus==kwifi){

如果之前是有wifi的状态,而现在的网络状态发生了变化,如果现在是4g网络则需要尽快的断开现有的弹幕链接,使用新的网络重新进行链接。本实施例则会调用之前的pipe对象的break函数来断开当前的网络链接。

断开网络后,pipe.break();需要再次的进行重连操作,继续使用步骤1的代码来进行网络链接,intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));

}elseif(prevstatus==k4g){

如果之前是属于4g网络,而现在是wifi了,则也需要马上进行网络断开,并且重新链接新的网络。本实施例则会调用之前的pipe对象的break函数来断开当前的网络链接。

断开网络后,pipe.break();需要再次的进行重连操作,继续使用步骤1的代码来进行网络链接,intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));

}

}

}

本发明实施例,当从有网络或者强信号网络切换到无信号网络或者弱信号网络时,使用网络底层的超时重连,并且设置为更为长时间的超时,让底层网络进行重连,以此来节省电量,提高了链接弹幕的处理效率。

实施例八

需要说明的是,在上述实施例的基础上,本发明还可以有其他的实现方式,例如,基于快速的切换或者挑选可以链接的ip设计链接方案,具体实施例如下:

为了保障移动终端上的客户端可以链接上弹幕服务器(服务器),通常服务器会给出多个服务器的ip列表供客户端选择使用,客户端可以任意选择一个服务器的ip做为自己的链接服务器的ip。现有做法则是从ip列表中挑选一个ip来进行链接,但是当一个ip的服务器存在故障或者该条网络线路出现故障,则需要客户端快速的切换或者挑选可以链接的ip。而对于服务器还存在有域名的情况下,需要解析域名得到更多的ip,在链接上需要挑选出更合适的ip进行链接,以提高整个的链接速度。

步骤1、定义ip和端口的数据结构。

假设现有ip列表如下:ip1:119.23.45.6port:4002ip2:118.34.32.7port:4002ip3:120.45.33.1port:4008ip4:121.55.23.8port:2009ip5:130.55.23.8port:4009;本实施例定义了一个ip和port的结构,strcuntipportitem{stringip;intport;}结构中包含ip数据和端口数据,那么每一个ip和端口都可以通过这个ipportitem来存储。

步骤2、确定弹幕服务器域名。

在提供弹幕服务器ip的同时,为了提高用户连接的成功率,已经链接速度,域名具有依据用户当前地理位置进行优选的最近网络的功能。同时也为了防止链接不上,提供了一个域名danmu.dy.com客户端,需要快速的从这些ip中挑选出可以连上的ip,从而后续可以比较快速的连上弹幕服务器。

步骤3、选择支持弹幕服务器负载的均衡的ip。

为了保障服务器给出的ip客户端可以进行一定的随机,从而使得所有客户端不会选择到同一个ip而造成服务器因负载过高而崩溃。同时也存在一些可能,客户端有些端口被封禁而不能链接上服务器。因此随机打乱需要挑选不同的端口进行尝试的链接。

步骤4、根据弹幕服务器域名解析ip地址。

首先客户端需要对域名进行解析,解析出更多的ip地址。然后将域名解析的ip和服务器下发的ip一起进行随机打乱,系统提供了getaddrinfo函数来将域名转换成ip地址。本实施例则可以调用系统函数来实现域名的转换。对于域名转换函数的结果则会存储到structaddrinfo结构中,所以需要定义对应的结构。structaddrinfohints,*single,result;本实施例定义了3个这样的结构变量。需要将变量清空,memset(&hints,0,sizeof(hints));hints.ai_family=pf_inet;hints.ai_socktype=sock_stream;然后对hints信息进行赋值,本实施例是获取域名对应的ip,所以将socktype赋值为sock_stream。接下来调用系统函数getaddrinfo来实现域名的解析。getaddrinfo(host_name,null,&hints,&result);其中需要传入参数host_name则是域名,对应本实施例中danmu.dy.com函数调用完成后,结果则会存储到hints和result中;并且一个域名或解析出多个ip,那么以链表的形式存储到result中。接下来编写for循环来遍历解析出来的ip列表。for(single=result;single;single=single-&gt;ai_next){结果存储在result中,下一个结果则在ai_next结构中,本实施例通过遍历来获取。constcharip=single-&gt;ai_addr.ip;在每个链表结构中,数据存储在ip这个属性中。

步骤5、生成一个容器对象ipportitemitem来存储获取到的多个ip。

本实施例设计一个vector&lt;ipportitem&gt;vecip;本实施例定义一个容器对象来存储获取到的ip,ipportitemitem;

首先创建一个item,然后对item进行赋值,item.ip=ip;item.port=port;vecip.push_back(item);通过调用容器的push_back函数则将ip存储到了容器中,同时也需要将上面的ip和port也存储进来。ipportitemitem1;item1.ip=119.23.45.6;item1.port=4002;vecip.push_back(item1);ipportitemitem2;item2.ip=118.34.32.7;item2.port=4002;vecip.push_back(item2);ipportitemitem3;item3.ip=120.45.33.1;item3.port=4008;vecip.push_back(item3);ipportitemitem4;item4.ip=121.55.23.8;item4.port=2009;vecip.push_back(item4);ipportitemitem5;item5.ip=121.55.23.8;item5.port=4009;vecip.push_back(item5);}到此已经将所有的ip和端口都存储到了vecip容器中。

步骤6、对获取到的多个ip进行随机和打乱。

接下来需要对vecip中的数据进行随机打乱。对于stl容器提供了相应的接口random_shuffle来对stl容器中的数据进行随机打乱。具体实现如下:random_shuffle(vecip.begin(),vecip.end());通过调用完成此函数后,容器中的所有ip已经被打乱,在打乱后,本实施例后续策略是每次挑选3个ip进行链接,因此最好一组ip中不要有相同的端口号,如果有相同的则可以与后续的ip进行位置的调换。

步骤7、根据随机和打乱后的ip链接弹幕服务器。

现有方法是通过一个个ip和port进行尝试去链接服务器。此种情况可能有些时候由于超时导致的链不上时间消耗比较长。而本实施例则会依次的从容器中挑选3个ip来进行尝试链接,每个ip设置4秒的超时,如果3个ip中有一个连上了,则马上断开其他2个ip的链接。首先本实施例在开始链接时,发起第一个ip进行链接。创建网络套接字socket;intsockfd=socket(af_inet,sock_stream,0)建立与服务器的连接;创建地址族,servaddr.sin_family=vecip[0].ip;创建端口号(主机字节序到网络字节序),servaddr.sin_port=htons(vecip[0].port);然后进行链接,intnret=connect(sockfd,(structsockaddr*)&servaddr,sizeof(servaddr));在4秒钟内,如果链接返回了,则说明连接成功了,说明此ip链接速度比较快,可以使用,后续则基于此ip进行链接;而如果从开始链接到过了4秒钟,此链接没有返回成功还是失败,本实施例则会从vecip中选择第二个ip进行链接。intsockfd1=socket(af_inet,sock_stream,0)建立与服务器的连接;创建地址族,servaddr1.sin_family=vecip[1].ip;创建端口号(主机字节序到网络字节序),servaddr1.sin_port=htons(vecip[1].port);然后进行链接,intnret=connect(sockfd1,(structsockaddr*)&servaddr1,sizeof(servaddr1));同样的如果过了4秒没有返回开启第三个ip的链接;intsockfd2=socket(af_inet,sock_stream,0)建立与服务器的连接;创建地址族,servaddr2.sin_family=vecip[2].ip;创建端口号(主机字节序到网络字节序),servaddr2.sin_port=htons(vecip[2].port);那么在12秒钟内本实施例开启了3组ip的链接,intnret=connect(sockfd1,(structsockaddr*)&servaddr2,sizeof(servaddr2));如果有任何一个ip的链接返回了,本实施例都会断开其他ip的链接。从而也说明了先返回的ip链接线路更好,链接速度更快。

本实施例则会依次的从容器中挑选3个ip来进行尝试链接,比传统方式要更快速的连上弹幕服务器。提高了链接弹幕的处理效率。

上面从链接弹幕的方法的角度对本发明实施例进行了描述,下面从移动终端的角度对本发明实施例进行描述。

请参阅图2,图2为本发明实施例提供的一种可能的移动终端的实施例示意图,包括:

第一确定单元201,用于确定基于c++语言的消息订阅者抽象接口;

第二确定单元202,用于确定所述抽象接口的防止对象拷贝功能;

生成单元203,用于生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理;

订阅单元204,用于通过所述订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息;

解析分发单元205,用于对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;

第三确定单元206,用于确定每一个消息的订阅者。

可选的,在一些可能的实现方式中,第一确定单元201具体用于:定义消息订阅者的抽象接口类messageobserver,classmessageobserver;定义所述抽象接口类的析构函数virtual~messageobserver();定义抽象接口getobservermessagetype;定义抽象接口onrecvpackmsg。

可选的,在一些可能的实现方式中,第二确定单元202具体用于:将拷贝构造函数定义成空实现的函数private:voidoperator=(constmessageobserver&);将所述拷贝构造函数定义成私有的空实现的函数messageobserver(constmessageobserver&)。

可选的,在一些可能的实现方式中,生成单元203具体用于:定义一个消息管理者类classobservermanager;生成std::multimap&lt;std::string,messageobserver*&gt;map_push_observer,并通过函数multimap对消息的订阅者进行管理;生成所述函数multimap的对象map_push_observer来存储所述订阅者。

可选的,在一些可能的实现方式中,订阅单元204具体用于:通过接口voidsetmessagetypeobserver(messageobserver*_taskobserver)提供给所有订阅者来往消息管理对象中注册的所有订阅者;通过抽象接口getobservermessagetype获取订阅者需要订阅的所有消息类型;获取返回值_cmdtypes,所述返回值存储了所有需要订阅的消息类型;定义一个迭代器对象std::set&lt;std::string&gt;::iteratoritr;初始化迭代器itr=_cmdtypes.begin();编写for循环来遍历得到每一个消息类型;调用multimap容器的insert接口向容器中插入所述订阅者。

可选的,在一些可能的实现方式中,解析分发单元205具体用于:创建一个消息解析器pdecoder;将消息存储到mq.read(buf,len)中的buf中;通过调用所述消息解析器来解析所述buf获取消息的类型type;创建一个接口函数来对所述消息进行分发给对应的订阅者。

可选的,在一些可能的实现方式中,解析分发单元205具体还用于:通过multimap容器的count函数获取所述消息的订阅者个数n;定义一个迭代器std::multimap&lt;std::string,messageobserver*&gt;::itratoritr;调用find函数来从迭代器中查找对应的订阅者;查找到后将所述订阅者存储到迭代器itr中;通过for循环将所述消息逐个的分发到每一个订阅者。

请参阅图3,图3为本发明实施例提供的电子设备的实施例示意图。

如图3所示,本发明实施例提供了一种电子设备,包括存储器310、处理器320及存储在存储器320上并可在处理器320上运行的计算机程序311,处理器320执行计算机程序311时实现以下步骤:确定基于c++语言的消息订阅者抽象接口;确定所述抽象接口的防止对象拷贝功能;生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理;通过所述订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息;对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;确定每一个消息的订阅者。

可选的,在一种可能的实施例中,所述处理器具体用于:定义消息订阅者的抽象接口类messageobserver,classmessageobserver;定义所述抽象接口类的析构函数virtual~messageobserver();定义抽象接口getobservermessagetype;定义抽象接口onrecvpackmsg。

可选的,在一种可能的实施例中,所述处理器具体用于:将拷贝构造函数定义成空实现的函数private:voidoperator=(constmessageobserver&);将所述拷贝构造函数定义成私有的空实现的函数messageobserver(constmessageobserver&)。

可选的,在一种可能的实施例中,所述处理器具体用于:定义一个消息管理者类classobservermanager;生成std::multimap&lt;std::string,messageobserver*&gt;map_push_observer,并通过函数multimap对消息的订阅者进行管理;生成所述函数multimap的对象map_push_observer来存储所述订阅者。

可选的,在一种可能的实施例中,所述处理器具体用于:通过接口voidsetmessagetypeobserver(messageobserver*_taskobserver)提供给所有订阅者来往消息管理对象中注册的所有订阅者;通过抽象接口getobservermessagetype获取订阅者需要订阅的所有消息类型;获取返回值_cmdtypes,所述返回值存储了所有需要订阅的消息类型;定义一个迭代器对象std::set&lt;std::string&gt;::iteratoritr;初始化迭代器itr=_cmdtypes.begin();编写for循环来遍历得到每一个消息类型;调用multimap容器的insert接口向容器中插入订阅者。

可选的,在一种可能的实施例中,所述处理器具体用于:创建一个消息解析器pdecoder;将消息存储到mq.read(buf,len)中的buf中;通过调用所述消息解析器来解析所述buf获取消息的类型type;创建一个接口函数来对所述消息进行分发给对应的订阅者。

在具体实施过程中,处理器320执行计算机程序311时,可以实现图1对应的实施例中任一实施方式。

本发明实施例,确定基于c++语言的消息订阅者抽象接口messageobserver;确定所述抽象接口的防止对象拷贝功能;生成消息订阅者管理类observermanager;通过所述订阅者管理类observermanager的接口向编写接口提供订阅者来进行订阅感兴趣的消息;对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;确定每一个消息的订阅者。本发明实施例,通过对消息类型的分类,再通过消息管理模块则对所有的消息订阅者进行管理,当接收到消息后,来分发给订阅者。消息管理者在不知道订阅者的情况下能够进行管理,提高了链接弹幕的处理效率,使用户无感知进而提高用户体验。

由于本实施例所介绍的电子设备为实施本发明实施例中移动终端所采用的设备,故而基于本发明实施例中所介绍的方法,本领域所属技术人员能够了解本实施例的电子设备的具体实施方式以及其各种变化形式,所以在此对于该电子设备如何实现本发明实施例中的方法不再详细介绍,只要本领域所属技术人员实施本发明实施例中的方法所采用的设备,都属于本发明所欲保护的范围。

请参阅图4,图4为本发明实施例提供的一种计算机可读存储介质的实施例示意图。

如图4所示,本实施例提供了一种计算机可读存储介质400,其上存储有计算机程序411,该计算机程序411被处理器执行时实现如下步骤:确定基于c++语言的消息订阅者抽象接口;确定所述抽象接口的防止对象拷贝功能;生成消息订阅者管理类,所述订阅者管理类用于对订阅者进行管理;通过所述订阅者管理类的接口向编写接口提供订阅者来进行订阅感兴趣的消息;对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;确定每一个消息的订阅者。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:定义消息订阅者的抽象接口类messageobserver,classmessageobserver;定义所述抽象接口类的析构函数virtual~messageobserver();定义抽象接口getobservermessagetype;定义抽象接口onrecvpackmsg。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:将拷贝构造函数定义成空实现的函数private:voidoperator=(constmessageobserver&);将所述拷贝构造函数定义成私有的空实现的函数messageobserver(constmessageobserver&)。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:定义一个消息管理者类classobservermanager;生成std::multimap&lt;std::string,messageobserver*&gt;map_push_observer,并通过函数multimap对消息的订阅者进行管理;生成所述函数multimap的对象map_push_observer来存储所述订阅者。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:

通过接口voidsetmessagetypeobserver(messageobserver*_taskobserver)提供给所有订阅者来往消息管理对象中注册的所有订阅者;通过抽象接口getobservermessagetype获取订阅者需要订阅的所有消息类型;获取返回值_cmdtypes,所述返回值存储了所有需要订阅的消息类型;定义一个迭代器对象std::set&lt;std::string&gt;::iteratoritr;初始化迭代器itr=_cmdtypes.begin();编写for循环来遍历得到每一个消息类型;调用multimap容器的insert接口向容器中插入订阅者。

可选的,在一种可能的实施例中,该计算机程序411被处理器执行时具体用于实现如下步骤:创建一个消息解析器pdecoder;将消息存储到mq.read(buf,len)中的buf中;通过调用所述消息解析器来解析所述buf获取消息的类型type;创建一个接口函数来对所述消息进行分发给对应的订阅者。

本发明实施例,确定基于c++语言的消息订阅者抽象接口messageobserver;确定所述抽象接口的防止对象拷贝功能;生成消息订阅者管理类observermanager;通过所述订阅者管理类observermanager的接口向编写接口提供订阅者来进行订阅感兴趣的消息;对所述消息订阅者管理类进行消息解析并将解析到的消息分发给所有的订阅者;确定每一个消息的订阅者。本发明实施例,通过对消息类型的分类,再通过消息管理模块则对所有的消息订阅者进行管理,当接收到消息后,来分发给订阅者。消息管理者在不知道订阅者的情况下能够进行管理,提高了链接弹幕的处理效率,使用户无感知进而提高用户体验。

本领域内的技术人员应明白,本发明的实施例可提供为方法、系统、或计算机程序产品。因此,本发明可采用完全硬件实施例、完全软件实施例、或结合软件和硬件方面的实施例的形式。而且,本发明可采用在一个或多个其中包含有计算机可用程序代码的计算机可用存储介质(包括但不限于磁盘存储器、cd-rom、光学存储器等)上实施的计算机程序产品的形式。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1