实现c++轻量级别websocket协议客户端

这篇具有很好参考价值的文章主要介绍了实现c++轻量级别websocket协议客户端。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 websocket 轻量客户端

因以前发过这个代码,但是一直没有整理,这次整理了一下,持续修改,主要是要使用在arm的linux上,发送接收的数据压缩成图片发送出去。

要达到轻量websocket 使用,必须要达到几个方面才能足够简单,
1、不用加入其他的库
2、只需要使用头文件包含就可以
3、跨平台
如果正常应用,可以使用websocketpp等库,问题就是比较麻烦,要使用boost或者asio库,当然asio也是足够简单,头文件包含,编译通过要设置参数,问题不大,不过不够简单

2 应用场景

1 windows 使用
2 linux使用
3 linux arm 板子上使用
在arm上编译的时候,就不用编译那么多的库文件了

3 原理

使用select模型 和原始操作系统的socket来直接编写代码,select模型比较简单,非长时间阻塞模式,以下是webscoket协议字节示意图
c++ websocket库,c++,c++高级技巧,网络,c++,websocket,开发语言
本文函数根据上图实现了websocket协议。定义的主要数据结构如下所示,websocket协议里面包含两种数据,一种是二进制,一种是文本,是可以指定的

	struct wsheader_type {
		unsigned header_size;
		bool fin;
		bool mask;
		enum opcode_type {
			CONTINUATION = 0x0,
			TEXT_FRAME = 0x1,
			BINARY_FRAME = 0x2,
			CLOSE = 8,
			PING = 9,
			PONG = 0xa,
		} opcode;
		int N0;
		uint64_t N;
		uint8_t masking_key[4];
	};

websocket链接

websocket链接使用的是http协议,所不同的是必须做upgrade

static const char* desthttp = "GET /%s HTTP/1.1\r\n"
			"Host: %s:%d\r\n"
			"Upgrade: websocket\r\n"
			"Connection: Upgrade\r\n"
			"Origin: %s\r\n"
			"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"
			"Sec-WebSocket-Version: 13\r\n\r\n";

上面把http协议升级为websocket协议的内容写出,把客户端的内容填充过去发送到服务端就行,从下面代码可以看出我们需要哪些内容

char line[256];
int status;
int i;
sprintf(line, desthttp, path, host, port, origin.c_str());
::send(sockfd, line, (int)strlen(line), 0);

发送和接收

使用select来做异步的模式,发送的时候指定参数
很简单,就如下所示

	fd_set wfds;
	timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
	FD_ZERO(&wfds);
	if (txbuf.size()) { FD_SET(sockfd, &wfds); }
	select((int)(sockfd + 1), NULL, &wfds, 0, timeout > 0 ? &tv : 0);

当然,如果要将socket置为非阻塞,开始的时候还是要设置的

#ifdef _WIN32
		u_long on = 1;
		ioctlsocket(sockfd, FIONBIO, &on);
#else
		fcntl(sockfd, F_SETFL, O_NONBLOCK);
#endif

下面是发送的函数,参数为毫秒

//参数是毫秒
void pollSend(int timeout)
	{
		if (v_state == CLOSED) {
			if (timeout > 0) {
				timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
				select(0, NULL, NULL, NULL, &tv);
			}
			return;
		}
		if (timeout != 0) {
			fd_set wfds;
			timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
			FD_ZERO(&wfds);

			if (txbuf.size()) { FD_SET(sockfd, &wfds); }
			select((int)(sockfd + 1), NULL, &wfds, 0, timeout > 0 ? &tv : 0);
		}
		while (txbuf.size()) {
			int ret = ::send(sockfd, (char*)&txbuf[0], (int)txbuf.size(), 0);
			if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) {
				break;
			}
			else if (ret <= 0) {
				closesocket(sockfd);
				v_state = CLOSED;
				fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr);
				break;
			}
			else {
				txbuf.erase(txbuf.begin(), txbuf.begin() + ret);
			}
		}
		if (!txbuf.size() && v_state == CLOSING) {
			closesocket(sockfd);
			v_state = CLOSED;
		}
	}

下面是接收函数

void pollRecv(int timeout)
	{
		if (v_state == CLOSED) {
			if (timeout > 0) {
				timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
				select(0, NULL, NULL, NULL, &tv);
			}
			return;
		}
		if (timeout != 0) {
			fd_set rfds;
			//fd_set wfds;
			timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
			FD_ZERO(&rfds);
			FD_SET(sockfd, &rfds);
		
			select((int)(sockfd + 1), &rfds, NULL, 0, timeout > 0 ? &tv : 0);
			if (!FD_ISSET(sockfd, &rfds))
			{
				printf("out of here ,no data\n");
				return;
			}
		}
		while (true) {
			// FD_ISSET(0, &rfds) will be true
			int N = (int)rxbuf.size();
			ssize_t ret;
			//钱波 64K 一个IP包长
			rxbuf.resize(N + 64000);
			ret = recv(sockfd, (char*)&rxbuf[0] + N, 64000, 0);
			if (ret < 0 && (socketerrno == SOCKET_EWOULDBLOCK || socketerrno == SOCKET_EAGAIN_EINPROGRESS)) {
				rxbuf.resize(N);
				break;
			}
			else if (ret <= 0) {
				rxbuf.resize(N);
				closesocket(sockfd);
				v_state = CLOSED;
				fputs(ret < 0 ? "Connection error!\n" : "Connection closed!\n", stderr);
				break;
			}
			else {//接收到的数据
				rxbuf.resize(N + ret);
			}
		}
	}

可以看出,我们使用select 仅仅是不阻塞,简单使用FD_ISSET宏去判决是否有数据达到,如果我们没有收到数据,我们就直接返回。

为了简单使用程序,我们封装一个class来使用接收和发送

class c_ws_class //:public TThreadRunable
{
	thread v_thread;
	std::mutex v_mutex;
	std::condition_variable v_cond;
	WebSocket v_ws;
	int v_stop = 1;
	string v_url;
	callback_message_recv v_recv = NULL;
	//已经
	//bool v_is_working = false;
public:

	static int InitSock()
	{
#ifdef _WIN32
		INT rc;
		WSADATA wsaData;

		rc = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (rc) {
			printf("WSAStartup Failed.\n");
			return -1;
		}
#endif
		return 0;
	}


	static void UnInitSock()
	{
		WSACleanup();
	}


	c_ws_class()
	{}
	~c_ws_class()
	{
		v_ws.close();
	}
	
public:


	void set_url(const char * url)
	{
		v_url = url;
	}
	int connect()
	{
		if (v_url.empty())
			return -1;
		return v_ws.connect(v_url);
	}
	void Start(callback_message_recv recv)
	{
		//because we will connect all over the time, so v_stop is zero
		v_stop = 0;
		v_ws.initSize(0, 0);
		v_recv = recv;
		v_thread = std::thread(std::bind(&c_ws_class::Run, this));
	}

	bool send(const char * str)
	{
		if (str != NULL)
		{
			if (v_ws.getReadyState() != CLOSED)
			{
				v_ws.send(str);
				v_ws.pollSend(10);
				return true;
			}
			return false;
		}
		return false;
	}

	void sendBinary(uint8_t *data, int len)
	{
		if (v_ws.getReadyState() != CLOSED)
		{
			v_ws.sendBinary(data, len);
			v_ws.pollSend(5);
		}
	}
	void Stop()
	{
		v_stop = 1;
	}
	int isStop()
	{
		return v_stop;
	}
	void Join()
	{
		if (v_thread.joinable())
			v_thread.join();
	}
	void Run()
	{
		
		while (v_stop == 0) {
			//WebSocket::pointer wsp = &*ws; // <-- because a unique_ptr cannot be copied into a lambda
			if (v_stop == 1)
				break;
			if (v_ws.getReadyState() == CLOSED)
			{
				//断线重连
				if (connect() != 0)
				{
					for (int i = 0; i < 20; i++)
					{
						std::this_thread::sleep_for(std::chrono::milliseconds(100));
						if (v_stop == 1)
							break;
					}
				}
			}
			else
			{
				v_ws.pollRecv(10);
				v_ws.dispatch(v_recv);
			}

		}
		v_ws.close();
		//std::cout << "server exit" << endl;
		v_stop = 1;
	}

	void WaitForSignal()
	{
		std::unique_lock<std::mutex> ul(v_mutex);
		v_cond.wait(ul);
	}
	void Notify()
	{
		v_cond.notify_one();
	}
};

以上为封装的外层线程代码,里面同时也封装了断线重连。

我们常常使用python或者使用nodejs来做测试,这里使用nodejs写一个简单的服务器程序,接收到数据以后发回。

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8000 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
    ws.send('recv:'+message);
  });//当收到消息时,在控制台打印出来,并回复一条信息
});

测试结果

服务端nodejs显示
c++ websocket库,c++,c++高级技巧,网络,c++,websocket,开发语言
客户端链接后显示
c++ websocket库,c++,c++高级技巧,网络,c++,websocket,开发语言

整个的头文件代码和测试代码在gitee上面
gitee地址文章来源地址https://www.toymoban.com/news/detail-524103.html

到了这里,关于实现c++轻量级别websocket协议客户端的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • c++: websocket 客户端与服务端之间的连接交互

    目录 socket 头文件 延迟时间 通信协议地址 TCP/IP 服务端 客户端 编程步骤 服务端 客户端 编程步骤 1. 初始化 WSAStartup 2. 创建 socket 2.1 协议族 2.2 socket 类型 2.3 协议 3. 绑定 bind (服务端) 4. 监听 listen(服务端) 5. 请求连接 connect(客户端) 6. 接收请求 accept(服务端) 7. 发送

    2024年02月14日
    浏览(29)
  • C++下轻量化websocket客户端库——easywsclient的使用

    1.1 WebSocket介绍 WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性

    2024年02月12日
    浏览(29)
  • websocket客户端实现(java)

    其中,headers 参数是一个键值对,表示需要设置的请求头。在构造函数中,我们首先创建了一个 ClientEndpointConfig.Configurator 对象,重写了其中的 beforeRequest() 方法,用于在请求之前设置请求头。然后,我们使用 ClientEndpointConfig.Builder.create() 方法创建一个 ClientEndpointConfig 对象,并

    2024年02月15日
    浏览(36)
  • Java实现websocket客户端

    常规情况下,大多数时候Java后台作为websocket服务端,实现方式也比较简单,网上很多案例代码。但是很多时候项目中服务与服务之间也需要使用websocket通信,此时项目就需要实现客户端功能。 步骤一:导入依赖: 步骤二:实现WebSocketClient抽象类: 该类中和websocket服务端接口

    2024年02月16日
    浏览(33)
  • SpringBoot+WebSocket实现服务端、客户端

    小编最近一直在使用springboot框架开发项目,毕竟现在很多公司都在采用此框架,之后小编也会陆续写关于springboot开发常用功能的文章。 什么场景下会要使用到websocket的呢? websocket主要功能就是实现网络通讯,比如说最经典的客服聊天窗口、您有新的消息通知,或者是项目与

    2024年02月13日
    浏览(37)
  • JAVA使用WebSocket实现多客户端请求

    工作前提:两个服务之间实现聊天通讯,因为介于两个服务,两个客户端 方案1:多个服务端,多个客户端,使用redis把用户数据ip进行存储,交互拿到redis数据进行推送 方案2: 一个服务端,多个客户端,拿到客户端的id和需要推送的id进行拼接存储 此文章使用的是方案2 1. 引

    2024年02月11日
    浏览(34)
  • WebSocket 实现长连接及通过WebSocket获取客户端IP

    WebSocket 是一种支持双向通讯的网络通信协议。 实现过程: 1 添加ServerEndpointExporter配置bean 2 实现过程 需求是通过WebSocket,建立长连接,并获取当前在线的人数。通过Websocket 不断发送消息,建立长连接,给Session续命。我是通过MAC地址,区分不同的设备,因为我的需求中需要一

    2024年02月09日
    浏览(42)
  • cpp-httplib: 轻量级、高性能的C++ HTTP/HTTPS客户端和服务器库

    cpp-httplib 是一个轻量级且高效的 C++ HTTP/HTTPS 客户端和服务器库。它由 Hideaki Sone(yhirose)开发,并在 MIT 许可下发布。该项目的主要目标是提供一种简单易用的方式,在 C++ 应用程序中实现 HTTP 和 HTTPS 功能。 项目仓库地址:https://gitcode.com/yhirose/cpp-httplib cpp-httplib 可用于以下场

    2024年04月09日
    浏览(59)
  • SpringBoot集成WebSocket实现客户端与服务端通信

    话不多说,直接上代码看效果! 一、服务端: 1、引用依赖 2、添加配置文件 WebSocketConfig 3、编写WebSocket服务端接收、发送功能   声明接口代码:   实现类代码: 4、如果不需要实现客户端功能,此处可选择前端调用,奉上代码 二、客户端: 1、引用依赖 2、自定义WebSocket客

    2024年01月23日
    浏览(40)
  • java实现WebSocket客户端&&断线重连机制

    1、引入maven依赖(注意版本) 2、代码

    2024年02月16日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包