websocket tcpip_网络游戏是采用TCP还是UDP「建议收藏」

websocket tcpip_网络游戏是采用TCP还是UDP「建议收藏」1)ws客户端其实是能直接连接tcp服务器的但是可见连接后,直接errorclose了,这是因为没有握手协议的支持,所以失败,2)下面添加了握手协议后,tcp服务器就可以升级为ws服务器3)client.htm

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

websocket tcpip_网络游戏是采用TCP还是UDP「建议收藏」

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

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

websocket tcpip_网络游戏是采用TCP还是UDP「建议收藏」

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 tcpip_网络游戏是采用TCP还是UDP「建议收藏」

服务器收到和客户端发来的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协议

今天的文章websocket tcpip_网络游戏是采用TCP还是UDP「建议收藏」分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/87152.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注