c語言websocket
① c++ websocketpp 怎麼用
首先:在我們是要使用C++搭配現有的函數庫來開發的,所以不太適合使用一般的網站伺服器方案;而在稍微評估了一下後,後來是決定使用「WebSocket++」這個函數庫,來做為C++環境的WebSocket Server開發方案。
WebSocket++的官方網站是:http://www.zaphoyd.com/websocketpp,他是採用BSD License的OpenSource、跨平台函數庫,文件則都放在Github上(網頁)。他目前最新的版本是0.3.x,在Github上要切換到「experimental」這個brahch;而這個版本的WebSocket++基本上是使用C++11以及Boost C++ Libraries里的ASIO(官網)來實作的Header -Only的函數庫,所以在使用前不需要特別去建置這個函數庫、只要在需要時去include他的Header檔就可以了,相當地方便。
然後在WebSocket 的功能方面,他除了有提供Server 端的功能外,也可以用來開發Client 端的程序,算是相當地完整;雖然他的板號還在0.3,好像還很新,不過實際上功能應該算是夠用了~
1.文件准備
如果要使用WebSocket++ 的話,基本上就是先到GitHub 上0.3.x 這個分支:https://github.com/zaphoyd/websocketpp/tree/experimental去把文件下載下來。而下載下來的文件裡面,「websocketpp」這個文件夾,就是要使用這個函數庫時,所有需要的文件了~而文件的部分,則是要連到他的網頁(鏈結)去看,內容不算很完整,Heresy算是看著范常式序和原始碼寫出來的;這點算是Heresy覺得這個函數庫做的比較差一點的地方,不過考慮到現在還是0.3版,也就不要要求太多了。
另外,由於他是基於Boost ASIO來開發網路的功能,所以也必須要下載Boost C++ Libraries來使用;Boost的官方網站是:http://www.boost.org/。
而如果有需要用到TLS的加密連線的話,應該是會需要使用OpenSSL這個函數庫(官網);如果不打算做加密連線的話,基本上是可以跳過這個函數庫的。(Heresy沒試過這部分)
2.基本概念
WebSocket++的基本使用說明,可以參考《Building a program with WebSocket++》這份文件。Heresy這邊算是整理一下,自己玩過後的想法。
首先,要使用WebSocket++ 來開發程序的時候,基本上要include 兩種文件,一種是用來做組態設置(config)的,一種則是用來決定要開發的程序的腳色類型(Role)的。
Role
在Role 的部分,主要就是分成Server 和Client 兩種;Server 就是用來開發WebSocket 伺服器的,而Client 則是可以用來開發C++ 的WebSocket 的用戶端程序、連線到其他的??WebSocket Server 做數據的存取。
如果要建立Server 端的程序的話,就是要include server 用的header 檔:
#include <websocketpp/server.hpp>
而之後則是就可以建立出websocketpp::server<>的物件,拿來做操作。
如果是要建立Client 端程序的話,則是要include client 的header 檔:
#include <websocketpp/client.hpp>
之後則是建立出websocketpp::client<>的物件來做連線。
而WebSocket++的server或client這兩種類別,都是template的class,在建立時也需要指定要使用的config才可以。
Config
Config 的部分,WebSocket++ 主要提供了三種類型:
config::core
config::asio
config::asio_tls
上面這三種類型,在WebSocket++是不同的結構,,config::core基本上是提供有限功能的設置,相對的他只會用到C++11的功能。而config::asio則是使用Boost ASIO做基礎來提供完整的功能;config::asio_tls則是config::asio再加上TLS的連線加密功能。
而根據組合的不同,不同的config也需要include websocketpp/config的目錄下、不同的header檔:
Server
Client
core
core.hpp
core_client.hpp
asio
asio_no_tls.hpp
asio_no_tls_client.hpp
asio_tls
asio.hpp
asio_client.hpp
而如果是以要建立一個使用Boost ASIO、沒有TLS加密的Server的話,基本上就是要include
asio_no_tls.hpp
#include <websocketpp/config/asio_no_tls.hpp>
其他的組合,也可以依此類頂。
Endpoint
在決定要include 哪兩個文件後,接下來就可以在程序裡面,建立出需要使用的WebSocket++ 物件了。
如果是要建立使用Boost ASIO、沒有TLS 加密的Server 的話,基本上要include 的文件就是:
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
而在控制用的物件的部分,則就是:
websocketpp:: server <websocketpp::config:: asio > mServer;
之後,所有的功能就都是針對mServer這個物件進行操作。而在WebSocket++裡面,則是把它稱為「endpoint」;通過組合出不同的endpoint,就可以實作不同的功能了。
Server 的範例
基本上,因為Heresy的目的是要建立一個WebSocket Server讓網頁來連線,所以這邊就只講Server的部分了~而實際上,在《Building a program with WebSocket++》里,官方就有提供一個很簡單的使用範例了~他的源代碼如下:
#include <iostream>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
typedef websocketpp:: server <websocketpp::config:: asio > server ;
void on_message(websocketpp:: connection_hdl hdl , server :: message_ptr msg )
{
std::cout << msg ->get_payload() << std::endl;
} int main()
{ server print_server;
print_server.set_message_handler(&on_message);
print_server.init_asio();
print_server. listen(9002);
print_server.start_accept(); print_server.run ();
}
在這個範例裡面,他是通過websocketpp:: server <websocketpp::config:: asio >這個Endpoint,來建立一個使用Boost ASIO、沒有TLS加密的WebSocket Server。這個server程序在執行後,會持續去監聽port 9002,當有信息傳遞進來的時候,就會觸發到on_message()這個函數、並把接到的信息輸出到命令提示字元的窗口上。
如果想要測試連線的話,可以考慮用 WebSocket.org 提供的
不過實際上,由於WebSocket++ 本身也有log 的功能,所以除了收到的信息會被輸出之外,還有很多紀錄用的信息,也都會被輸出在畫面上,看起來可能會有點雜亂就是了。
另外,由於這個范常式序,只會從client接收信息,並不會傳送數據給Server端,所以Echo Text的Log裡面,並不會像連到ws:// echo.websocket.org
在源代碼的地方,首先就是建立出一個endpoint的server物件print_server,用來做後續的操作。
而在建立出print_server後,接下來要做的事情,包括了:
初始化ASIO
調用init_asio()這個函數,初始化內部的Boost ASIO的io_service(官網),作為後續網路連線等功能之使用。
設置連接埠
調用listen()這個函數,指定要監聽的連接埠;這邊是設置成9002。
而如果系統上有多個網卡的話,默認會監聽所有的網路介面;如果需要的話也可以特別指定要針對哪個介面做監聽。
開始接受連線
調用start_accept()開始接受輸入。
進入主循環
調用run(),進入WebSocket++ Server的主循環。
之後程序就會進入主循環,直到Server 被停下來。
那要怎麼處理連線進來的信息呢?WebSocket++是通過提供各種「Handler」(callback function),來做事件的處理;在官方網站上,有列出可以使用的handler列表(頁面)。
而在這個範例里,則是通過set_message_handler(),來設置當Server收到信息時,要執行的callback function,這里就是on_message()這個函數;這也是一般來說,一定會用到的callback function 。
而message handler 的callback function,會收到兩個數據:
一個是websocketpp::connection_hdl型別的數據,是用來識別目前的連線用的;如果之後要傳送信息給client的話,就必須要通過這個物件,來設置要把信息傳送給誰。而如果有需要的話,也可以藉由server<>的get_con_fromhdl()來取得觸發這個 ??事件的連線、以及他的資訊。
第二個資訊,則是websocketpp::server<>::message_ptr,裡面儲存的是傳遞進來的信息。一般來說,這會通過他的get_payload()函數,來取得傳遞進來的信息,而得到的數據,會是const string&。不過由於WebSocket也有可能是傳遞非文字的binary數據,所以可能會需要通過 get_opcode()這個函數,來辨別傳遞進來的數據的形式。
而在這個範例裡面,on_message()這個函數,就是很單純地把街道的資訊,通過iostream做輸出了~
在網頁上的這個範例裡面,這個Server只有做接收的功能,並不會送信息給Client端。那如果要送信息給client端要怎麼做呢?基本上就是調用server<>的send()這個函數就可以了。
在官方的example文件夾里,有個echo_server的目錄,裡面的echo_server.cpp
而他送出數據的方法,就是:
s->send(hdl, msg->get_payload(), msg->get_opcode());
這邊可以看到,要調用sned()這個函數來傳遞數據,基本上是需要給他三個參數:
websocketpp::connection_hdl的物件,讓Server知道是要傳給哪個client。
要傳遞的數據,這邊就是直接把收到的信息(msg->get_payload())再傳出去;實際上send()有提供不同的介面,實際的數據型別可以是const void*或const string&。
最後,則是要有一個opcode,來指定要傳 ??遞的資訊的形式;如果是純文字的話,基本上可以直接指定websocketpp::frame::opcode::TEXT。
而這個范常式序在執行後,如果一樣使用 WebSocket.org 的 來測試的話,就可以發現他的功能和 WebSocket.org 測試用的 ws://echo.websocket.org
Windows / Visual Studio 上的問題
上面基本上就是WebSocket++ 使用上的基本用法。不過實際上,這樣的源代碼,在Heresy 這邊的Windows + Visual Studio 2010 / 2012,都是沒辦法正確建置的。
最主要的問題,基本上應該算是VC++ 本身對Boost C++ Library 的支援性問題吧…在Heresy 測試的結果是發現,如果希望在VisualStudio 2010 或2012 上使用的WebSocket++ 的話,有部分的功能??必須要強制讓WebSocket++ 去使用C++ 11 的內建函數庫,而不要去使用Boost 的版本。
設置的方法,可以參考官方的《C++11 Support》這頁。以Heresy這邊的測試來說,至少functional和memory兩個函數庫,是需要使用C++11 STL的版本才行的;也就是說,必須要加上_WEBSOCKETPP_CPP11_MEMORY_和_WEBSOCKETPP_CPP11_FUNCTIONAL_這兩個定義(因為MSVC不支援完整的C++11,所以不能直接用_WEBSOCKETPP_CPP11_STL_)。
但是,在加上這兩個定義後,實際上會產生另一個問題,那就是std::min()和巨集版本的min()沖到的問題(參考);這個問題,比較簡單的方法,就是在再額外定義一個NOMINMAX,來取消掉巨集版本的min()和max()了。
所以,實際上要讓上面的程序可以正常運作,一個方法就是在原始碼的一開始、include WebSocket++ 的Header 之前,先加上下面三行:
#define NOMINMAX
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_MEMORY_
或是在VC的專案屬性的「組態屬性」裡面,找到「C/C++」的「前置處理器」,在「前置處理氣定義」的欄位裡面,加上「NOMINMAX;_WEBSOCKETPP_CPP11_FUNCTIONAL_;_WEBSOCKETPP_CPP11_MEMORY_」了。
理論上,這兩種方法應該都可以讓MSVC可以正確地建置上面的范常式序。而這個問題Heresy也有回報給作者了(鏈結),就看之後有沒有辦法修正吧。
另外,Heresy在使用Visual Studio 2012的時候,雖然可以正確編譯了,可是在執行階段,則是會當掉。稍微追了一下源代碼後,發現應該是Visual Studio 2012的std::strftime()這個函數(MSDN)有問題所造成的。
主要的問題是發生在 logger/ basic.hpp這個文件,裡面定義的get_timestamp()這個函數裡面有透 ??過std::strftime()來列印出目前的時間,以做為紀錄之用;而他定義的輸出字串,則是一個長度30的C字串buffer。
由於他有試著輸出時區的資訊(%z),而在Visual Sutdio里,如果在台灣的環境的話,他會是一個「台北標准時間」這樣的文字;而這樣的文字,再加上前面的時間資訊的會,就導致整個結果會超過30個字元。而在這個狀況下,Visual Studio 2010隻是會無法輸出,但是在Visual Studio 2012,卻可能是讓程序整個當掉… orz
而解決方法呢?基本上應該是兩種,一個是把buffer的大小改大、例如把它改成40(要改兩個地方,一個是105行、一個是111行,參考),這樣可以讓字串夠長、不會出問題;另一種方法,則是把105行里定義的時間格式字串「"%Y-%m-%d %H:%M:%S%z"」,最後面的「%z」拿掉,這樣就不會去處理時區的資訊,也就比較不容易出問題了。
最後:這篇大概就這樣了。內容,算是對WebSocket++ 的極簡單介紹的~實際上,由於官方文件實在不足,所以學習起來有點累;不過,至少已經成功地用WebSocket++ 完成第一個WebSocket 的Server 端程序了~接下來,看看有什麼特殊的想法,會再做補充吧。