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协议
今天的文章websocket tcpip_网络游戏是采用TCP还是UDP「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/87152.html