由于从去年开始,Heresy就开始有在写网页的东西了,所以也开始玩HTML5新的WebSocket这项技术。而由于当初自己通过C++来建立WebScoket Server的需求,所以后来就去找了WebSocket++这个函数库(官网)来用。
当时也边学边写了几篇文章,算是自己的纪录。有兴趣的可以参考:
C++ WebSocket 函数库:WebSocket++
通过WebSocket 传送Binary 数据:ArrayBuffer
通过WebSocket 传送Binary 数据:ArrayBuffer(补充)
不过,当时Heresy 都只有把它拿来建立Server 过,并没有用来建立Client 端的经验;而由于最近要用C++ 写个WebSocket Client,所以就在来追加一下这系列的文章吧~
首先,过了一年后,WebSocket++在GitHub(链结)的主线,已经换成0.3了~所以不用像之前一样,还要自己切换到experimental的分支;而目前最新的版本,则是0.3.0 Alpha 4。而一样,由于他是基于Boost的ASIO来开发的,所以会需要额外准备Boost C++ Libraries。
要建立一个WebSocket++ 的client 端,和建立Server 端时相同,需要include 两个文件,一个是config 的hesrder 档、另一个则是client 的header 档。以一个使用Boost ASIO、不加密的连线的Client 来说,要include 的文件就是:
- #include <websocketpp/config/asio_client.hpp>
- #include <websocketpp/client.hpp>
而client 端控制的endpoint 物件型别,则是:
- websocketpp::client<websocketpp::config::asio_tls_client>
由于他满长的,所以会建议用typedef 来做缩短;例如:
- typedef websocketpp:: client <websocketpp::config:: asio_client > WSClient ;
要注意的是,endpoint的物件在client端基本上是用来控制client的连线用的,一个endpoint是可以建立多个连线的;而美建立一个连线,都会取得一个connection_hdl的物件,这个物件才是和server沟通时、用来操作的物件。
在源代码的部分,一般来说流程大致上是:
建立endpoint 的物件
初始化endpoint
设置对应的handler
取得连线、并且建立连线
写成最简单的程序的话,大概会像下面这样:
- // 1. cretae endpoint
- WSClient mEndPoint;
- // 2. initial endpoind
- mEndPoint.init_asio();
- mEndPoint.start_perpetual();
- boost:: thread wsThread( [&mEndPoint](){ mEndPoint.run (); } );
- // 3. set handler
- mEndPoint.set_message_handler(
- []( websocketpp:: connection_hdl hdl , WSClient :: message_ptr msg ){
- cout << msg ->get_payload() << endl;
- }
- );
- // 4. get connection and connect
- websocketpp::lib:: error_code ec;
- WSClient :: connection_ptr wsCon = mEndPoint.get_connection( sUrl, ec);
- mEndPoint.connect( wsCon );
首先,就是先建立出endpoint的物件、mEndpoint。而之后,则是要针对他进行初始化,这边包含了三个步骤,第一步是初始化ASIO,第二步则是将这个endpoint 设置为永远执行(perpetual),之后就建立另一个执行序、来执行endpoint 的主循环。这边应该也有其他的方法,像是有的方法就是不需要设置为永远执行(perpetual)就可以用的,不过执行的顺序会不太一样,在这边就先不提了(注一)。
而这边Heresy是用Boost的thread来建立新的执行序,如果是使用C++11的环境的话,也可以用STL的thread就好。接下来,则是需要设置需要的handler,指定在事件发生的时候,要做哪些事。在这个例子里面,只有通过set_message_handler()设置收到信息时要做的事;在这边就是单纯把收到的东西通过ostream输出而已。
而如果要设置其他事件的处理方式的话,则就是要使用其他的set_xxx_handler()的函数、来进行设置。到上面为止,基本上都还是针对endpoint进行设置、初始化。接下来,就是要建立连线了~要建立连线的话,基本上分成两步,第一步是要先通过get_connection()这个函数,来取得一个connection_ptr型别的连线指针wsCon,然后再通过connect()这个函数、来进行连线。
在这里,会有一个error_code的物件ec,他是用来做错误侦测之用的,有必要的话,可以通过判断他的值、以及他的信息(message())来判断错误的状态。之后如果要传送信息的话,则就可以通过wsCon的send()这个函数,来送出数据给server端;而收到来自Server端的数据的时候,则会去执行set_message_handler()所设置的函数。这边的处理方法,和Server端是完全相同的,所以就不多提了。
最后,当连线结束的时候,则是要调用wsCon的close()这个函数,来做中断连线的动作。在调用close()的时候,需要给两个参数,第一个是状态的值,详细的定义可以参考close.hpp这个文件;而第二个参数则是中断的理由,是一个字串,可以给最长123个字符。
而之后,要结束程序前,则是需要把mEndPoint停下来,这边可以通过调用stop_perpetual()来让它结束永远执行的模式,这样在没有连线的时候,他就会自动停止了。
程序的最后,则是再调用
- wsThread.join ();
等wsThread这个执行序结束就可以了。
下面就是一个应该可以在Visual C++ 2010 下运作的WebSocket++ client 的完整范例了~
- #define NOMINMAX
- #define _WEBSOCKETPP_CPP11_FUNCTIONAL_
- #define _WEBSOCKETPP_CPP11_MEMORY_
- #include <iostream>
- #include <string>
- #include <boost/thread.hpp>
- #include <websocketpp/config/asio_no_tls_client.hpp>
- #include <websocketpp/client.hpp>
- using namespace std;
- typedef websocketpp:: client <websocketpp::config:: asio_client > WSClient ;
- int main( int argc , char ** argv )
- {
- string sUrl = "ws:// echo.websocket.org " ;
- // cretae endpoint
- WSClient mEndPoint;
- // initial endpoind
- mEndPoint.init_asio();
- mEndPoint.start_perpetual();
- boost:: thread wsThread( [&mEndPoint](){ mEndPoint.run (); } );
- // set handler
- mEndPoint.set_message_handler(
- []( websocketpp:: connection_hdl hdl , WSClient :: message_ptr msg ){
- cout << msg ->get_payload() << endl;
- }
- );
- // get connection and connect
- websocketpp::lib:: error_code ec;
- WSClient :: connection_ptr wsCon = mEndPoint.get_connection( sUrl, ec);
- mEndPoint.connect( wsCon );
- string sCmd;
- while ( true )
- {
- cout << "Enter Command: " << flush;
- getline(cin, sCmd);
- if ( sCmd == "quit" )
- {
- wsCon->close( 0, "close" );
- break ;
- }
- auto ec = wsCon->send( sCmd );
- cout << ec.message() << endl;
- }
- mEndPoint.stop_perpetual();
- wsThread.join ();
- }
附注
如果不设置perpetual 的话,endpoint 会在没有连线时自动结束,而不会持续执行;所以如果需要用同一个endpoint 建立多个连线的话,应该是采取这样的写法比较合适。
但是如果这个endpoint只打算建立一个连线的话,其实可以不用做这个设置,但是相对的,执行endoinpt的run()的时间点,就需要放到建立连线(调用connect())之后才行。
- 评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)

asio_client的解释为:Client config with asio transport and TLS enabled 是会加密的
谢谢分享!
C++ WebSocket++ 的Client很好,很强大~