Linux下websocket协议实现

上篇文章讲述了http协议的一个简单业务流程实现,这个流程是浏览器通过tcp协议和服务器之间进行连接,然后浏览器向服务器发送http请求,服务器接收请求后给浏览器返回http响应的一个过程。

今天要讲的websocket协议和http协议不一样的是,只要客户端连接上了服务器,websocket协议并不需要浏览器向服务器发送请求,服务器才会返回响应,而是服务器可以主动向浏览器发送数据包。比如某些网页的消息推送,即时消息等,都是websocket协议实现的,当然 一样的是websocket和http一样,都是通过tcp协议来实现浏览器和服务器之间的连接的。

首先考虑四个问题:

1.websocket协议格式?

2.websocket如何验证客户端合法?

3.明文、密文如何传输?

4.websocket如何断开?

1.websocket协议格式,是有国际标准的websocket格式,其中对格式做了非常标准的规范。

引用协议中的原文如上图所示。

websocket协议主要分为两部分,一个是握手,一个是数据传输。上图也是给出了握手过程的请求和响应。

可以看出websocket其实也是一个http协议头,只是比http协议头还多了几个字段。 其中值得注意的一个字段为Sec-WebSocket-Key,这个字段就是解决第二个问题的。

2. 如何验证客户端的合法性?

协议里规定了一个唯一固定的GUID,"258EAFA5-E914-47DA- 95CA-C5AB0DC85B11"。

用上面Sec-WebSocket-Key和GUID相连接,即为:

"dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA- C5AB0DC85B11"

 原文The server would then take the SHA-1 hash of this, giving the value 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. This value is then base64-encoded (see Section 4 of [RFC4648]), to give the value "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".

也就是说对红色这个字符串做SHA1哈希,然后再对这个结果做一个base64编码得出最后的结果:

"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".

这个结果需要由服务器发给客户端,客户端对此进行校验。

下面是websocket握手过程对客户端的连接请求进行解析及回复响应的过程。


int readline(char *allbuf, int idx, char *linebuf) {

	int len = strlen(allbuf);

	for(;idx < len;idx ++) {
		if (allbuf[idx] == '\r' && allbuf[idx+1] == '\n') {
			return idx+2;
		} else {
			*(linebuf++) = allbuf[idx];
		}
	}

	return -1;
}


int base64_encode(char *in_str, int in_len, char *out_str) {    
	BIO *b64, *bio;    
	BUF_MEM *bptr = NULL;    
	size_t size = 0;    

	if (in_str == NULL || out_str == NULL)        
		return -1;    

	b64 = BIO_new(BIO_f_base64());    
	bio = BIO_new(BIO_s_mem());    
	bio = BIO_push(b64, bio);
	
	BIO_write(bio, in_str, in_len);    
	BIO_flush(bio);    

	BIO_get_mem_ptr(bio, &bptr);    
	memcpy(out_str, bptr->data, bptr->length);    
	out_str[bptr->length-1] = '\0';    
	size = bptr->length;    

	BIO_free_all(bio);    
	return size;
}


int handshark(struct ntyevent *ev) {

	//ev->buffer , ev->length

	char linebuf[1024] = {0};
	int idx = 0;
	char sec_data[128] = {0};
	char sec_accept[32] = {0};

	do {

		memset(linebuf, 0, 1024);
		idx = readline(ev->buffer, idx, linebuf);

		if (strstr(linebuf, "Sec-WebSocket-Key")) {

			//linebuf: Sec-WebSocket-Key: QWz1vB/77j8J8JcT/qtiLQ==
			strcat(linebuf, GUID);

			//linebuf: 
			//Sec-WebSocket-Key: QWz1vB/77j8J8JcT/qtiLQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

			
			SHA1(linebuf + WEBSOCK_KEY_LENGTH, strlen(linebuf + WEBSOCK_KEY_LENGTH), sec_data); // openssl

			base64_encode(sec_data, strlen(sec_data), sec_accept);

			memset(ev->buffer, 0, BUFFER_LENGTH); 

			ev->length = sprintf(ev->buffer, "HTTP/1.1 101 Switching Protocols\r\n"
					"Upgrade: websocket\r\n"
					"Connection: Upgrade\r\n"
					"Sec-WebSocket-Accept: %s\r\n\r\n", sec_accept);

			printf("ws response : %s\n", ev->buffer);

			break;
			
		}

	} while((ev->buffer[idx] != '\r' || ev->buffer[idx+1] != '\n') && idx != -1 );

	return 0;
}

握手过程结束后,下面再来介绍数据传输过程,这一过程也伴随着第三个问题,即如何进行明文、密文的传输?

3. 明文、密文的传输?

从协议中描述的数据帧格式如下:

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux C++中实现WebSocket客户端和服务端,需要使用第三方库来处理WebSocket协议的数据帧的解析和封装。以下是一些常用的WebSocket库: - libwebsockets:这是一个轻量级、高性能的C库,支持WebSocket客户端和服务端,以及HTTP客户端和服务端。它还支持SSL和TLS加密协议。 - WebSocket++:这是一个C++的WebSocket库,支持WebSocket客户端和服务端,以及TLS加密协议。它还提供了一些高级功能,如消息压缩和自定义协议扩展。 - Boost.Beast:这是Boost库中一个HTTP和WebSocket库,支持WebSocket客户端和服务端,以及SSL加密协议。它提供了一个简单的API来处理WebSocket协议的数据帧。 下面是一个简单的示例代码,演示如何使用libwebsockets库实现一个WebSocket客户端和服务端: ```c++ // WebSocket客户端 #include <libwebsockets.h> int main() { struct lws_context_creation_info info; struct lws_client_connect_info connect_info; struct lws *wsi = NULL; const char *address = "ws://localhost:8080"; memset(&info, 0, sizeof(info)); info.port = CONTEXT_PORT_NO_LISTEN; info.protocols = NULL; info.gid = -1; info.uid = -1; struct lws_context *context = lws_create_context(&info); if (context == NULL) { return -1; } memset(&connect_info, 0, sizeof(connect_info)); connect_info.context = context; connect_info.address = address; connect_info.port = 0; connect_info.path = "/"; connect_info.host = lws_canonical_hostname(context); connect_info.origin = connect_info.host; connect_info.protocol = "chat"; connect_info.ssl_connection = 0; wsi = lws_client_connect_via_info(&connect_info); if (wsi == NULL) { lws_context_destroy(context); return -1; } while (true) { lws_service(context, 50); } lws_context_destroy(context); return 0; } // WebSocket服务端 #include <libwebsockets.h> static int callback_echo(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { switch (reason) { case LWS_CALLBACK_ESTABLISHED: { printf("Connection established\n"); break; } case LWS_CALLBACK_RECEIVE: { printf("Received data: %s\n", (char *)in); lws_write(wsi, (unsigned char *)in, len, LWS_WRITE_TEXT); break; } default: break; } return 0; } int main() { struct lws_context_creation_info info; struct lws_protocols protocol; struct lws_context *context = NULL; memset(&info, 0, sizeof(info)); info.port = 8080; info.protocols = &protocol; info.gid = -1; info.uid = -1; memset(&protocol, 0, sizeof(protocol)); protocol.name = "chat"; protocol.callback = callback_echo; protocol.per_session_data_size = 0; protocol.rx_buffer_size = 0; context = lws_create_context(&info); if (context == NULL) { return -1; } while (true) { lws_service(context, 50); } lws_context_destroy(context); return 0; } ``` 这个示例代码使用libwebsockets库分别实现了一个WebSocket客户端和服务端。在客户端中,它创建了一个WebSocket连接,并发送和接收数据。在服务端中,它监听端口8080,并接收客户端的连接请求,收到数据后将其原样发送回去。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值