Libjingle的代码分析Libjingle的工作流程主要分为以下几个步骤:
登录Jabber服务器talk_base::InitializeSSL();初始化SSL链路,如果需要加密的话
XmppPump类;封装了XmppClient并用XmppClient来登陆到服务器上,它主要的函数和消息通知:
pump.client()->SignalStateChange.connect(&object, &msg_function); //接收通知消息
STATE_START
STATE_OPENING
STATE_OPEN //登陆成功后;发送自身的状态
STATE_CLOSED
pump.DoLogin(xcs, new XmppSocket(true), NULL); //登陆服务器
pump.DoDisconnect(); //断开服务器
批注:如何将来配合UC客户端使用的话,这个环节就刨去不考虑了。
发送自身的状态和获取好友的状态信息在登陆Jabber Server后,客户端需要将自身的状态发送给服务器,同时服务器会将好友的信息反馈回来;
状态信息包括:Jid,available,invisible,show,priority,know_capabilities,phone_capability,is_google_client,version
//1. 接收服务器发送过来的状态 buzz::PresencePushTask *presence_push_ = new buzz::PresencePushTask(xmpp_client_); presence_push_->SignalStatusUpdate.connect(this, &FileShareClient::OnStatusUpdate); presence_push_->Start();
//2. 设置自身的状态 buzz::Status my_status; my_status.set_....... my_status.set_show(buzz::Status::SHOW_ONLINE);
//3 发送自身的状态 buzz::PresenceOutTask* presence_out_ = new buzz::PresenceOutTask(xmpp_client_); presence_out_->Send(my_status); presence_out_->Start();
//4-------------收到好友状态,从服务器传递过来的、(在当前程序中可不用) void OnStatusUpdate(const buzz::Status& status) { if (status.available() && status.fileshare_capability()) //可以进行文件传输 if (status.available() && status.phone_capability()) //可以进行Call通信 }
批注:如果将来配合UC客户端的话,这个环节就刨去不考虑了。
设置会话管理在登录服务器且获得了好友的状态信息后,你需要设置会话管理管道,用来监视进来的连接请求和响应出去的连接请求。设置会话的步骤:
1. 实例化您的NetWorkManager, PortAllocator 子类,SessionManager对象;(注意:Libjingle的文件传输程序是采用传输完一个Session就结束程序,所以它采用的是单线程也是就是使用的主线程来运行Session;而Call通信过程就采用了多线程的模式,防止主线程被阻塞。这里的主线程也就是处理信息的线程,也叫做信号线程) talk_base::NetworkManager network_manager_; talk_base::scoped_ptr port_allocator_; talk_base::scoped_ptr session_manager_; port_allocator_.reset(new cricket::HttpPortAllocator(&network_manager_, "pcp")); session_manager_.reset(new cricket::SessionManager(port_allocator_.get(), NULL)); //使用单线程!对于我来说用什么? or worker_thread_ = new talk_base::Thread();//这个是语言通信的会话管理 session_manager_ = new cricket::SessionManager(port_allocator_, worker_thread_); session_manager_->SignalRequestSignaling.connect(this, &CallClient::OnRequestSignaling); session_manager_->OnSignalingReady(); worker_thread_->Start();
2. 创建一个用来发送和接受Session请求;xmpp_client_(能否手工初始化,而不是从服务中获取?) cricket::SessionManagerTask *session_manager_task = new cricket::SessionManagerTask(xmpp_client_, session_manager_); session_manager_task->EnableOutgoingMessages(); session_manager_task->Start();
3.查询stun和relay server。这是异步调用在OnJingleInfo中接受服务器反馈的信息 buzz::JingleInfoTask *jingle_info_task = new buzz::JingleInfoTask(xmpp_client_); jingle_info_task->RefreshJingleInfoNow(); jingle_info_task->SignalJingleInfo.connect(this, &FileShareClient::OnJingleInfo); jingle_info_task->Start();
4.文件传输Session开始;有点类似开始侦听 file_share_session_client_.reset(new cricket::FileShareSessionClient(session_manager_.get(), xmpp_client_->jid(), "pcp")); file_share_session_client_->SignalFileShareSessionCreate.connect(this, &FileShareClient::OnFileShareSessionCreate); session_manager_->AddClient(NS_GOOGLE_SHARE, file_share_session_client_.get()); 【or】 phone_client_ = new cricket::PhoneSessionClient(xmpp_client_->jid(),session_manager_); phone_client_->SignalCallCreate.connect(this, &CallClient::OnCallCreate); worker_thread_->Start();
// 接收服务器发来的stun server、relay server的信息 void OnJingleInfo(const std::string & relay_token,const std::vector<std::string> &relay_addresses,const std::vector<talk_base::SocketAddress> &stun_addresses) { port_allocator_->SetStunHosts(stun_addresses); port_allocator_->SetRelayHosts(relay_addresses); port_allocator_->SetRelayToken(relay_token); }
批注: 1.从第三步我们可以看出:stun server和relay server的配置信息在Jabber服务器上,而且不是一个; 公司的Jabber是否有这些信息?是否支持这种命令? 如果这三个信息不从服务器获得,relay_token又是什么东西? 2.NS_GOOGLE_SHARE="http://www.google.com/session/share"; session_manager_->AddClient(str, xmppclient)是注册一个客户端,其实就是将他们存入map中;而NS_GOOGLE_SHARE是key值,这有什么用呢? 文件传输只需要创建一个file_share_session_client_,每次传输文件是创建一个FileShareSession对象,有这个对象发送文件请求。 那么session_manager_为什么要将用map来存储file_share_session_client_呢?
发送文件请求、或连接某个用户cricket::FileShareSession* share = file_share_session_client_->CreateFileShareSession();
share->Share(remote_jid, manifest_);
批注:
1.例子中remote_jid是从服务器反馈的,如果我们用手工创建(username/region/passwd),这些信息是否够用?
2.从这句话中我们可以看出,协商链路和打洞都被封装起来了;
在这个打洞的流程中,Jabber服务器究竟启动什么作用?他都处理了那些命令?
在打洞的时候,究竟是Jabber还是relay起到了协助作用?
收到文件请求或连接回报出来
void OnSessionState(cricket::FileShareState state){
case cricket::FS_OFFER: //收到对方发来的文件信息;上报文件信息
case cricket::FS_TRANSFER: //开始传输
case cricket::FS_COMPLETE: //传输完毕
case cricket::FS_LOCAL_CANCEL: //本地取消
case cricket::FS_REMOTE_CANCEL://远程取消
case cricket::FS_FAILURE: //传输失败
}
void OnUpdateProgress(cricket::FileShareSession *sess) {
注:session_->GetProgress()是整体的进度;而不是单个文件的进度!
}
FileShareSession,就是用来收发一个文件(应该说是文件列表)
session_->Share(remote_jid, manifest_); //发送文件信息
session_->manifest() //得到文件清单
session_->GetTotalSize //得到总大小
session_->GetCurrentItemName() //得到当前文件名
session_->GetProgress() //得到当前文件的进度
session_->jid() //得到发送者的JID
session_->is_sender() //是否是发送者
session_->Accept(); //接收文件
session_->Cancel(); //取消文件
session_->SetLocalFolder(); //设置本地路径,这样相当于另存为么?
session_->disconnect_all(); //断开所有连接
void OnFileShareSessionCreate(cricket::FileShareSession *sess) { //在创建一个FileShareSession对象后,管理的消息处理函数
session_ = sess;
sess->SignalState.connect(this, &FileShareClient::OnSessionState);
sess->SignalNextFile.connect(this, &FileShareClient::OnUpdateProgress);
sess->SignalUpdateProgress.connect(this, &FileShareClient::OnUpdateProgress);
sess->SignalResampleImage.connect(this, &FileShareClient::OnResampleImage);//
sess->SetLocalFolder(root_dir_);
}
确定接收和通信,(指定文件路径)void OnSessionState(cricket::FileShareState state)函数中将文件信息显示出来;
用户单独调用接受accept(), Cancel(), Deline()
文件发送完毕后;结束连接通道 delete file_share_session_;
talk_base::Thread *thread = talk_base::ThreadManager::CurrentThread();
thread->Stop();
libjingle使用方法:1.线程处理:线程对象:thread类(从MessageQueue中继承)
重写run函数做处理函数;调用start运行调用stop停止!
线程互斥锁CriticalSection;使用方法CritScope cs(&CriticalSection);
2.范围指针scoped_ptr
使用reset切换指针的地址;使用get获得原始的指针;很适合在类中使用
3.消息处理类MessageHandler
该类必须重写OnMessage()函数;
通过thread的post方法可以给消息处理函数发送消息!
将来的Demo界面:
对方JID、文件名称、大小、进度、速度、状态、注释
.......、Session信息、总体大小、进度(传输过来的/总大小)、速度(当前速度)、状态(总状态)、注释信息
、真正的文件名、...........................
、真正的文件名、...........................
、真正的文件名、...........................
、真正的文件名、...........................
、真正的文件名、...........................
封装成一个类;
1.设置会话管理:实例化三个对象、查询stun/relay、文件开始侦听、启动主线程
2.发生文件请求
发送端创建一个FileShareSession
3.收到文件请求后,也要对应一个对象
服务端创建一个FileShareSession
libjingle使用的是标准xmpp协议,但是对里面一些内容进行了扩展:下表列出了谷歌Talk中使用的非标准XMPP协议的扩展;除此之外都是标准的xmpp协议:
但是对于文件传输和语音通信来说:Jingle Server Discovery是需要的!
|