Linux下编写C++服务器(Websocket服务器)

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为例子,
在这里插入图片描述

环境配置

  1. 两个库各复制一份到windows的共享目录下
cp -rf /usr/asio-1.12.2/ /mnt/hgfs/LinuxShare/
cp -rf /usr/websocketpp-master/ /mnt/hgfs/LinuxShare/
  1. 打开VS,创建一个名为websockettest的Makefile项目,和上一篇一样先创建main.cppMakefile
    在这里插入图片描述
  2. 把.\websocketpp-master\examples\broadcast_server.cpp的内容复制到main.cpp中;
  3. 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库。

  1. 右键项目,选择属性,在GeneralRemote 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

如图:
在这里插入图片描述

  1. 点击编译,运行成功了,在浏览器中打开websocket测试,在网页内的地址栏输入ws://0.0.0.0:9002,点击连接,发送hello,如下图显示即为测试成功。
    在这里插入图片描述

添加功能

  1. 创建websocketserver.hwebsocketserver.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
  1. 点击运行,我们可以开多个websocket客户端进行测试。
    在这里插入图片描述
发布了14 篇原创文章 · 获赞 6 · 访问量 3122

猜你喜欢

转载自blog.csdn.net/qq_39554698/article/details/98075499
今日推荐