1. 程式人生 > >一個用C++編寫的WebSocket客戶端示例程式碼

一個用C++編寫的WebSocket客戶端示例程式碼

  上次寫了WebSocket服務端,這次工作當中需要一個用C++實現的WebSocket客戶端,寫好後這裡記一下,免得以後忘記。

本示例共有三個檔案組成,依賴Websocket++第三方庫

其中main.cpp是使用示例

#include <iostream>
#include <string>
#include <sstream>

#include "websocket_endpoint.h"

int main(int argc, char **argv)
{
	bool done = false;
	std::string input;
	kagula::websocket_endpoint endpoint;

	endpoint.connect("ws://localhost:9002");

	while (!done) {
		std::cout << "Enter Command: ";
		std::getline(std::cin, input);

		if (input == "quit") {
			done = true;
		}
		else if (input.substr(0, 4) == "send") {
			std::stringstream ss(input);

			std::string cmd;
			std::string message;

			ss >> cmd;
			std::getline(ss, message);

			endpoint.send(message);
		}
		else if (input.substr(0, 4) == "show") {
			endpoint.show();
		}
		else {
			std::cout << "> Unrecognized Command" << std::endl;
		}
	}

	endpoint.close();

	return 0;
}

其它兩個檔案是封裝

websocket_endpoint.h檔案清單

#ifndef _WEBSOCKET_ENDPOINT_
#define _WEBSOCKET_ENDPOINT_

#include <string>

/*
Title: Web Socket Client
Author: kagula
Date: 2016-11-21
Dependencies: Websocket++、Boost::ASIO
Test Environment: VS2013 Update5, WebSocket++ 0.70, Boost 1.61
Description:
[1]Support connect a web socket server.
[2]If server is crash, client will not follow crash.
*/

namespace kagula
{
	class websocket_endpoint {
	public:
		websocket_endpoint();
		~websocket_endpoint();

		int connect(std::string const & uri);
		void close();

		void send(std::string message);
		void show();
	};
}

#endif

websocket_endpoint.cpp檔案清單

#include <websocketpp/config/asio_no_tls_client.hpp>
#include <websocketpp/client.hpp>

#include <websocketpp/common/thread.hpp>
#include <websocketpp/common/memory.hpp>

#include <cstdlib>
#include <iostream>
#include <map>
#include <string>
#include <sstream>

#include "websocket_endpoint.h"

typedef websocketpp::client<websocketpp::config::asio_client> ws_client;

namespace kagula
{

	class connection_metadata {
	public:
		typedef websocketpp::lib::shared_ptr<connection_metadata> ptr;

		connection_metadata(websocketpp::connection_hdl hdl, std::string uri)
			: m_hdl(hdl)
			, m_status("Connecting")
			, m_uri(uri)
			, m_server("N/A")
		{}

		void on_open(ws_client *client, websocketpp::connection_hdl hdl) {
			m_status = "Open";

			ws_client::connection_ptr con = client->get_con_from_hdl(hdl);
			m_server = con->get_response_header("Server");
		}

		// if connection failed, the function will be invoke.
		void on_fail(ws_client *client, websocketpp::connection_hdl hdl) {
			m_status = "Failed";

			ws_client::connection_ptr con = client->get_con_from_hdl(hdl);
			m_server = con->get_response_header("Server");
			m_error_reason = con->get_ec().message();
		}

		void on_close(ws_client *client, websocketpp::connection_hdl hdl) {
			m_status = "Closed";
			ws_client::connection_ptr con = client->get_con_from_hdl(hdl);
			std::stringstream s;
			s << "close code: " << con->get_remote_close_code() << " ("
				<< websocketpp::close::status::get_string(con->get_remote_close_code())
				<< "), close reason: " << con->get_remote_close_reason();
			m_error_reason = s.str();
		}

		void on_message(websocketpp::connection_hdl, ws_client::message_ptr msg) {
			if (msg->get_opcode() == websocketpp::frame::opcode::text) {
				m_messages.push_back("<< " + msg->get_payload());
			}
			else {
				m_messages.push_back("<< " + websocketpp::utility::to_hex(msg->get_payload()));
			}
		}

		websocketpp::connection_hdl get_hdl() const {
			return m_hdl;
		}

		std::string get_status() const {
			return m_status;
		}

		std::string get_uri() const {
			return m_uri;
		}

		void record_sent_message(std::string message) {
			m_messages.push_back(">> " + message);
		}

		friend std::ostream & operator<< (std::ostream & out, connection_metadata const & data);
	private:
		websocketpp::connection_hdl m_hdl;
		std::string m_status;
		std::string m_uri;
		std::string m_server;
		std::string m_error_reason;
		std::vector<std::string> m_messages;
	};

	std::ostream & operator<< (std::ostream & out, connection_metadata const & data) {
		out << "> URI: " << data.m_uri << "\n"
			<< "> Status: " << data.m_status << "\n"
			<< "> Remote Server: " << (data.m_server.empty() ? "None Specified" : data.m_server) << "\n"
			<< "> Error/close reason: " << (data.m_error_reason.empty() ? "N/A" : data.m_error_reason) << "\n";
		out << "> Messages Processed: (" << data.m_messages.size() << ") \n";

		std::vector<std::string>::const_iterator it;
		for (it = data.m_messages.begin(); it != data.m_messages.end(); ++it) {
			out << *it << "\n";
		}

		return out;
	}

	ws_client g_wsEndPoint;
	connection_metadata::ptr g_wsClientConnection;

	websocketpp::lib::shared_ptr<websocketpp::lib::thread> g_threadWS;

	websocket_endpoint::websocket_endpoint(){
			g_wsEndPoint.clear_access_channels(websocketpp::log::alevel::all);
			g_wsEndPoint.clear_error_channels(websocketpp::log::elevel::all);

			g_wsEndPoint.init_asio();
			g_wsEndPoint.start_perpetual();

			g_threadWS = websocketpp::lib::make_shared<websocketpp::lib::thread>(&ws_client::run, &g_wsEndPoint);
	}
	
	websocket_endpoint::~websocket_endpoint() {
		g_wsEndPoint.stop_perpetual();
		
		if (g_wsClientConnection->get_status() == "Open") {
			// Only close open connections
			websocketpp::lib::error_code ec;
			g_wsEndPoint.close(g_wsClientConnection->get_hdl(), websocketpp::close::status::going_away, "", ec);
			if (ec) {
				std::cout << "> Error closing ws connection " << g_wsClientConnection->get_uri() << " :" << ec.message() << std::endl;
			}
		}

		g_threadWS->join();
	}

	int websocket_endpoint::connect(std::string const & uri) {
		websocketpp::lib::error_code ec;

		ws_client::connection_ptr pConnection = g_wsEndPoint.get_connection(uri, ec);

		if (ec) {
			std::cout << "> Connect initialization error: " << ec.message() << std::endl;
			return -1;
		}

		g_wsClientConnection = websocketpp::lib::make_shared<connection_metadata>(pConnection->get_handle(), uri);

		pConnection->set_open_handler(websocketpp::lib::bind(
			&connection_metadata::on_open,
			g_wsClientConnection,
			&g_wsEndPoint,
			websocketpp::lib::placeholders::_1
			));
		pConnection->set_fail_handler(websocketpp::lib::bind(
			&connection_metadata::on_fail,
			g_wsClientConnection,
			&g_wsEndPoint,
			websocketpp::lib::placeholders::_1
			));
		pConnection->set_close_handler(websocketpp::lib::bind(
			&connection_metadata::on_close,
			g_wsClientConnection,
			&g_wsEndPoint,
			websocketpp::lib::placeholders::_1
			));
		pConnection->set_message_handler(websocketpp::lib::bind(
			&connection_metadata::on_message,
			g_wsClientConnection,
			websocketpp::lib::placeholders::_1,
			websocketpp::lib::placeholders::_2
			));

		g_wsEndPoint.connect(pConnection);

		return 0;
	}

	void close(websocketpp::close::status::value code, std::string reason) {
		websocketpp::lib::error_code ec;

		g_wsEndPoint.close(g_wsClientConnection->get_hdl(), code, reason, ec);
		if (ec) {
			std::cout << "> Error initiating close: " << ec.message() << std::endl;
		}
	}

	void websocket_endpoint::close()
	{
		if (g_wsClientConnection->get_status()=="Open")
		{
			int close_code = websocketpp::close::status::normal;
			kagula::close(close_code, "");
		}
	}	

	void websocket_endpoint::send(std::string message) {
		websocketpp::lib::error_code ec;

		g_wsEndPoint.send(g_wsClientConnection->get_hdl(), message, websocketpp::frame::opcode::text, ec);
		if (ec) {
			std::cout << "> Error sending message: " << ec.message() << std::endl;
			return;
		}

		g_wsClientConnection->record_sent_message(message);
	}	

	void websocket_endpoint::show()
	{
		std::cout << * g_wsClientConnection << std::endl;
	}
}