websocket的协议比较复杂,利用websocketpp库进行WebSocket服务器的开发会简单很多,websocketpp库依赖boost的asio库,但是在C++11中,asio可以独立出来使用。接下来先进行两个库的安装。
安装asio,websocketpp
下载asio
然后依次输入
#剪切压缩包到/usr
mv /home/aubin/下载/asio-1.12.2.tar.gz /usr
#进入/usr目录
cd /usr
#解压
tar -xzvf /usr/asio-1.12.2.tar.gz
下载websocketpp
然后依次输入
#剪切压缩包到/usr
mv /home/aubin/下载/websocketpp-master.zip /usr
#进入/usr目录
cd /usr
#解压
unzip /usr/websocketpp-master.zip
安装完成后,输入
ls /usr/websocketpp-master/examples/
我们可以看见有很多例子,这次我们就以broadcast_server为例子,
环境配置
- 两个库各复制一份到windows的共享目录下
cp -rf /usr/asio-1.12.2/ /mnt/hgfs/LinuxShare/
cp -rf /usr/websocketpp-master/ /mnt/hgfs/LinuxShare/
- 打开VS,创建一个名为websockettest的Makefile项目,和上一篇一样先创建main.cpp和Makefile。
- 把.\websocketpp-master\examples\broadcast_server.cpp的内容复制到main.cpp中;
- Makefile的内容如下:
ASIOPATH=/usr/asio-1.12.2/include
WEBSOCKETPATH=/usr/websocketpp-master
CC1=g++
build:
$(CC1) -o websockettest main.cpp -I $(WEBSOCKETPATH) -I $(ASIOPATH) -l pthread -std=c++11 -D _WEBSOCKETPP_CPP11_STL_ -D ASIO_STANDALONE
clean:
rm *.o websockettest
-std=c++11支持c++11,-D后面的宏定义用于独立使用asio库。
- 右键项目,选择属性,在General的Remote Build Root Directory中写入Linux上的项目目录,
在C++->Include Search Path添加E:\LinuxShare\include;E:\LinuxShare\include\c++\4.8.2;E:\LinuxShare\asio-1.12.2\include;E:\LinuxShare\websocketpp-master;
在Remote Build四项中分别写入
cd $(RemoteRootDir)/$(ProjectName); make build
cd $(RemoteRootDir)/$(ProjectName); make clean build
cd $(RemoteRootDir)/$(ProjectName); make clean
$(RemoteRootDir)/$(ProjectName)/websockettest
如图:
- 点击编译,运行成功了,在浏览器中打开websocket测试,在网页内的地址栏输入ws://0.0.0.0:9002,点击连接,发送hello,如下图显示即为测试成功。
添加功能
- 创建websocketserver.h和websocketserver.cpp,这两个文件编码要改为utf-8,否则中文传送时会发生错误,添加功能主要修改process_messages函数;
websocketserver.h
#pragma once
#include <stdio.h>
#include <sys/select.h>
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include <set>
/*#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>*/
#include <websocketpp/common/thread.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
using websocketpp::lib::thread;
using websocketpp::lib::mutex;
using websocketpp::lib::lock_guard;
using websocketpp::lib::unique_lock;
using websocketpp::lib::condition_variable;
/* on_open insert connection_hdl into channel
* on_close remove connection_hdl from channel
* on_message queue send to all channels
*/
enum action_type {
SUBSCRIBE,
UNSUBSCRIBE,
MESSAGE
};
struct action {
action(action_type t, connection_hdl h) : type(t), hdl(h) {}
action(action_type t, connection_hdl h, server::message_ptr m)
: type(t), hdl(h), msg(m) {}
action_type type;
websocketpp::connection_hdl hdl;
server::message_ptr msg;
};
static void sleep_ms(unsigned int secs);
class broadcast_server {
public:
broadcast_server() {
// Initialize Asio Transport
m_server.init_asio();
// Register handler callbacks
m_server.set_open_handler(bind(&broadcast_server::on_open, this, ::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close, this, ::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message, this, ::_1, ::_2));
}
virtual ~broadcast_server() {};
void run(uint16_t port) {
// listen on specified port
m_server.listen(port);
// Start the server accept loop
m_server.start_accept();
// Start the ASIO io_service run loop
try {
m_server.run();
}
catch (const std::exception & e) {
std::cout << e.what() << std::endl;
}
}
void on_open(connection_hdl hdl) {
{
lock_guard<mutex> guard(m_action_lock);
//std::cout << "on_open" << std::endl;
m_actions.push(action(SUBSCRIBE, hdl));
}
m_action_cond.notify_one();
}
void on_close(connection_hdl hdl) {
{
lock_guard<mutex> guard(m_action_lock);
//std::cout << "on_close" << std::endl;
m_actions.push(action(UNSUBSCRIBE, hdl));
}
m_action_cond.notify_one();
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
// queue message up for sending by processing thread
{
lock_guard<mutex> guard(m_action_lock);
//std::cout << "on_message" << std::endl;
m_actions.push(action(MESSAGE, hdl, msg));
}
m_action_cond.notify_one();
}
virtual void process_messages() {
while (1) {
unique_lock<mutex> lock(m_action_lock);
while (m_actions.empty()) {
m_action_cond.wait(lock);
}
action a = m_actions.front();
m_actions.pop();
lock.unlock();
if (a.type == SUBSCRIBE) {
lock_guard<mutex> guard(m_connection_lock);
m_connections.insert(a.hdl);
}
else if (a.type == UNSUBSCRIBE) {
lock_guard<mutex> guard(m_connection_lock);
m_connections.erase(a.hdl);
}
else if (a.type == MESSAGE) {
lock_guard<mutex> guard(m_connection_lock);
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
m_server.send(*it, a.msg);
}
}
else {
// undefined.
}
}
}
protected:
typedef std::set<connection_hdl, std::owner_less<connection_hdl> > con_list;
server m_server;
con_list m_connections;
std::queue<action> m_actions;
mutex m_action_lock;
mutex m_connection_lock;
condition_variable m_action_cond;
};
class Websocketserver:public broadcast_server
{
public:
Websocketserver();
~Websocketserver();
private:
char errmsg[512];
public:
bool start(uint16_t port);
void stop();
char* geterrmsg();
void process_messages() override;
};
websocketserver.cpp
#include "websocketserver.h"
Websocketserver::Websocketserver()
:broadcast_server()
{
}
Websocketserver::~Websocketserver()
{
}
bool Websocketserver::start(uint16_t port)
{
try {
// listen on specified port
m_server.listen(port);
// Start the server accept loop
m_server.start_accept();
// Start the ASIO io_service run loop
m_server.run();
return true;
}
catch (const std::exception & e) {
std::cout << e.what() << std::endl;
strcpy(errmsg, e.what());
}
return false;
}
void Websocketserver::stop()
{
try {
m_server.stop();
}
catch (const std::exception & e) {
std::cout << e.what() << std::endl;
}
}
void Websocketserver::process_messages()
{
while (1) {
sleep_ms(100);
while (m_actions.empty()) {
sleep_ms(100);
continue;
}
if (!m_actions.empty())
{
unique_lock<mutex> lock(m_action_lock);
action a = m_actions.front();
m_actions.pop();
lock.unlock();
if (a.type == SUBSCRIBE) {
lock_guard<mutex> guard(m_connection_lock);
m_connections.insert(a.hdl);
//群发
int num = m_connections.size();
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
m_server.send(*it, "有人上线", websocketpp::frame::opcode::value::text);
}
std::cout << "客户端连接" << std::endl;
}
else if (a.type == UNSUBSCRIBE) {
lock_guard<mutex> guard(m_connection_lock);
m_connections.erase(a.hdl);
//群发
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
m_server.send(*it, "有人下线", websocketpp::frame::opcode::value::text);
}
std::cout << "客户端断开" << std::endl;
}
else if (a.type == MESSAGE) {
lock_guard<mutex> guard(m_connection_lock);
std::string str = a.msg->get_payload();
std::cout << "客户端接收:" << str << std::endl;
if (!strcmp(str.c_str(), "bye"))
{
stop();
return;
}
if (str.find("/all", 0) != std::string::npos)
{
//回复所有
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
a.msg->set_payload("某人发了\"" + str.substr(4) + "\"了");
m_server.send(*it, a.msg);
}
continue;
}
//回复单个
a.msg->set_payload("我看到你发的\"" + str + "\"了");
m_server.send(a.hdl, a.msg);
std::cout << "服务器回应:"<< "我看到你发的了" <<std::endl;
}
else {
// undefined.
}
}
}
}
char* Websocketserver::geterrmsg()
{
return errmsg;
}
static void sleep_ms(unsigned int secs)
{
struct timeval tval;
tval.tv_sec = secs / 1000;
tval.tv_usec = (secs * 1000) % 1000000;
select(0, NULL, NULL, NULL, &tval);
}
main.cpp
#pragma once
#pragma execution_character_set("utf-8")
#include "websocketserver.h"
int main() {
try {
Websocketserver server_instance;
// Start a thread to run the processing loop
thread t(bind(&broadcast_server::process_messages, &server_instance));
// Run the asio loop with the main thread
if (server_instance.start(9002))
{
std::cout << server_instance.geterrmsg() << std::endl;
}
t.join();
}
catch (websocketpp::exception const & e) {
std::cout << e.what() << std::endl;
}
}
Makefile
ASIOPATH=/usr/asio-1.12.2/include
WEBSOCKETPATH=/usr/websocketpp-master
CC1=g++
build:main.o websocketserver.o
$(CC1) -gdwarf-2 -o websockettest main.o websocketserver.o -I $(WEBSOCKETPATH) -I $(ASIOPATH) -l pthread -std=c++11 -D _WEBSOCKETPP_CPP11_STL_ -D ASIO_STANDALONE
main.o:main.cpp websocketserver.h
$(CC1) -gdwarf-2 -c main.cpp -I $(WEBSOCKETPATH) -I $(ASIOPATH) -l pthread -std=c++11 -D _WEBSOCKETPP_CPP11_STL_ -D ASIO_STANDALONE
websocketserver.o:websocketserver.cpp websocketserver.h
$(CC1) -gdwarf-2 -c websocketserver.cpp -c websocketserver.h -I $(WEBSOCKETPATH) -I $(ASIOPATH) -l pthread -std=c++11 -D _WEBSOCKETPP_CPP11_STL_ -D ASIO_STANDALONE
clean:
rm *.o websockettest
- 点击运行,我们可以开多个websocket客户端进行测试。