深刻理解websocket为何是基于tcp的协议(migic是固定值!!!)

1)ws客户端其实是能直接连接tcp服务器的

但是可见连接后,直接error-->close了,这是因为没有握手协议的支持,所以失败,

2)下面添加了握手协议后,tcp服务器就可以升级为ws服务器

3)client.html

<!DOCTYPE html>
<html>
<head>
  <title>skynet WebSocket example</title>
</head>
<body>   
  <script>
    var ws = new WebSocket('ws://127.0.0.1:8001/ws');

    ws.onopen = function(){
     alert("open");
     ws.send('WebSocket'); 
    };
    ws.onmessage = function(ev){
     alert(ev.data);
    };
    ws.onclose = function(ev){
     alert("close");
    };
    ws.onerror = function(ev){
        console.log(ev);
     alert("error");
    };

  </script>
</body>
</html>

4)server.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "uv.h"

#include "../3rd/http_parser/http_parser.h"
#include "../3rd/crypto/sha1.h"
#include "../3rd/crypto/base64_decoder.h"
#include "../3rd/crypto/base64_encoder.h"

struct ws_context{
	int is_shake_hand;
	char* data; // 读取数据的buff
};

static uv_loop_t* loop = NULL;
static uv_tcp_t l_server;

static void 
uv_alloc_buf(uv_handle_t* handle, size_t suggested_size,uv_buf_t* buf) {

	struct ws_context* wc = handle->data;

	if (wc->data != NULL) {
		free(wc->data);
		wc->data = NULL;
	}

	buf->base = malloc(suggested_size + 1);
	buf->len = suggested_size;
	wc->data = buf->base;
}

static void
on_close(uv_handle_t* handle) {
	printf("client close!\n");
	if (handle->data) {
		struct ws_context* wc = handle->data;
		free(wc->data);
		wc->data = NULL;
		free(wc);

		handle->data = NULL;
	}

	free(handle);
}

static void
on_shutdown(uv_shutdown_t* req, int status) {
	uv_close((uv_handle_t*)req->handle, on_close);
	free(req);
}

static void
after_write(uv_write_t* req, int status) {
	if (status == 0) {
		printf("write success!\n");
	}

	uv_buf_t* w_buf = req->data;
	if (w_buf) {
		free(w_buf);
	}
	free(req);
}

static void
send_data(uv_stream_t* stream, unsigned char* send_data, int send_len) {

	uv_write_t* w_req = malloc(sizeof(uv_write_t));
	uv_buf_t* w_buf = malloc(sizeof(uv_buf_t));

	unsigned char* send_buf = malloc(send_len);
	memcpy(send_buf, send_data, send_len);

	w_buf->base = send_buf;
	w_buf->len = send_len;
	w_req->data = w_buf;

	uv_write(w_req, stream, w_buf, 1, after_write);
}

static char field_sec_key[512];
static char value_sec_key[512];
static int is_sec_key = 0;
static int has_sec_key = 0;

static int 
on_ws_header_field(http_parser* p, const char* at, size_t length) {
	if (strncmp(at, "Sec-WebSocket-Key", length) == 0) {
		is_sec_key = 1;
	}
	else
	{
		is_sec_key = 0;
	}

	return 0;
}

static int
on_ws_header_value(http_parser* p, const char* at, size_t length) {
	if (!is_sec_key) {
		return 0;
	}

	strncpy(value_sec_key, at, length);
	value_sec_key[length] = 0;
	has_sec_key = 1;
	return 0;
}

static char* wb_magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

static char* wb_accept = 
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade:websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"Websocket-Protocol:chat\r\n\r\n";

static void
ws_connect_shake_hand(uv_stream_t* stream, unsigned char* data, int data_len) {
	http_parser_settings settings;
	http_parser_settings_init(&settings);

	settings.on_header_field = on_ws_header_field;
	settings.on_header_value = on_ws_header_value;

	http_parser p;
	http_parser_init(&p, HTTP_REQUEST);
	is_sec_key = 0;
	has_sec_key = 0;
	http_parser_execute(&p, &settings, data, data_len);

	if (has_sec_key) { // 解析握手成功
		printf("Sec-Websocket-Key: %s\n", value_sec_key);

		static char key_magic[512]; // key+migic
		static char sha1_key_migic[SHA1_DIGEST_SIZE]; // 经过sha1加密后的 key+migic
		static char send_client[512]; // 发给客户端的握手数据

		int sha1_size;

		sprintf(key_magic, "%s%s", value_sec_key, wb_magic);

		// sha1的结果:sha1_key_migic  sha1后的长度:sha1_size
		crypt_sha1((unsigned char*)key_magic, strlen(key_magic), (unsigned char*)&sha1_key_migic, &sha1_size); 

		int base64_len;
		char* base_buf = base64_encode(sha1_key_migic, sha1_size, &base64_len); // 得到base64结果

		sprintf(send_client, wb_accept, base_buf); // 得到最终握手数据
		base64_encode_free(base_buf);

		send_data(stream, (unsigned char*)send_client, strlen(send_client));
	}
}

static void
ws_send_data(uv_stream_t* stream, unsigned char* data, unsigned int len) {
	int head_size = 2;
	if (len > 125 && len < 65536) {
		head_size += 2;
	}
	else if (len >= 65536) {
		head_size += 8;
	}

	unsigned char* data_buf = malloc(head_size + len);
	data_buf[0] = 0x81;
	if (len <= 125) {
		data_buf[1] = len;
	}
	else if (len > 125 && len < 65536) {
		data_buf[1] = 126;
		data_buf[2] = (len & 0x0000ff00) >> 8;
		data_buf[3] = (len & 0x000000ff);
	}
	else
	{
		return;
	}

	memcpy(data_buf + head_size, data, len);
	send_data(stream, data_buf, head_size + len);
	free(data_buf);
}

static void 
ws_on_recv_data(uv_stream_t* stream, unsigned char* data, unsigned int len) {
	if (data[0] != 0x81 && data[0] != 0x82) {
		return;
	}

	unsigned int data_len = data[1] & 0x0000007f;
	int head_size = 2;
	if (data_len == 126) {
		data_len = data[3] | (data[2] << 8);
		head_size += 2;
	}
	else if (data_len == 127) {
		unsigned int low = data[5] | (data[4] << 8) | (data[3] << 16) | (data[2] << 24);
		unsigned int height = data[9] | (data[8] << 8) | (data[7] << 16) | (data[6] << 24);
		data_len = low;
		head_size += 8;
	}

	unsigned char* mask = data + head_size;
	unsigned char* body = data + head_size + 4;

	for (unsigned int i = 0; i < data_len; i++) {
		body[i] = body[i] ^ mask[i % 4];
	}

	//test
	static char test_buf[4096];
	memcpy(test_buf, body, data_len);
	test_buf[data_len] = 0;
	printf("%s\n", test_buf);

	ws_send_data(stream, "Hello", strlen("Hello"));
}

static void 
after_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
	if (nread < 0) {
		uv_shutdown_t* reg = malloc(sizeof(uv_shutdown_t));
		memset(reg, 0, sizeof(uv_shutdown_t));
		uv_shutdown(reg, stream, on_shutdown);
		return;
	}

	printf("start websocket!!!\n");

	struct ws_context* wc = stream->data;
	if (wc->is_shake_hand == 0) {
		ws_connect_shake_hand(stream, buf->base, buf->len);
		wc->is_shake_hand = 1;
		return;
	}

	// 客户端主动关闭
	if ((unsigned char)(buf->base[0]) == 0x88) {
		printf("ws closing!!!\n");
		return;
	}

	// 正常的数据
	ws_on_recv_data(stream, buf->base, nread);
}

static void
uv_connection(uv_stream_t* server, int status) {
	printf("new client come in\n");
	uv_tcp_t* client = malloc(sizeof(uv_tcp_t));
	memset(client, 0, sizeof(uv_tcp_t));
	uv_tcp_init(loop, client);
	uv_accept(server, (uv_stream_t*)client);

	//让event loop管理读
	struct ws_context* wc = malloc(sizeof(struct ws_context));
	memset(wc, 0, sizeof(struct ws_context));
	client->data = wc;

	uv_read_start((uv_stream_t*)client, uv_alloc_buf, after_read);
}

int main(int argc, char** argv) {
	int ret;

	loop = uv_default_loop();

	uv_tcp_init(loop, &l_server);

	// 配置我想要eventloop帮我管理的类型
	struct sockaddr_in addr;
	uv_ip4_addr("0.0.0.0", 8001, &addr);
	ret = uv_tcp_bind(&l_server, (const struct sockaddr*)&addr, 0);
	if (ret != 0) {
		goto failed;
	}
	uv_listen((uv_stream_t*)&l_server, SOMAXCONN, uv_connection);

	uv_run(loop, UV_RUN_DEFAULT);

failed:
	printf("end\n");
	system("pause");
	return 0;
}

5)数据收发协议测试

服务器收到和客户端发来的WebSocket字符串,客户端也受到服务器发来的Hello字符串

握手过程:

1.客户端发送 一段 http报文

2.服务器收到报文,通过http_parser解析到 Sec-WebSocket-Accept 对应的 客户端握手value

3.migic是固定的 static char* wb_migic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

4.把客户端解析到的          base64(sha1( 客户端握手value +magic )), 也就是先sha1算法,在base64编码后,得到握手内容,发送给ws客户端,就完成了握手! 此时,ws客户端就可以连接上升级后的tcp服务器了。

总结:

ws协议等价于 = 实现了 握手、发送数据、接受数据、关闭数据 这4个协议的tcp协议

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值