1Websocket简介
1WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。
HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯
2Websocket原理
1他是基于TCP SOCKET上添加了一些上层协议。
2很多网站为了实现即时通信, 所用的技术都是轮询(polling), 轮询是在特定的时间间隔(比如1秒),
由浏览器对服务器发出HTTP request,然后由服务器返回最新数据给客户端浏览器,这种传统的HTTP
request的模式带来明显的缺点, 浏览器要不断向服务器发请求, 而HTTP request的header(头信息)非常长,
包含我们要用的数据却很小, 这样会占用很多宽带,
3Websocket API,浏览器和服务器只需要做一个握手的动作,然后浏览器和服务器之间就形成了一条
快速通道, 两者之间就直接可以数据互相传送。这样互相沟通的header还是很小的,大概是2Bytes
3长连接和短连接
长连接: 初始化的时候建立连接,一直不关闭
1服务器与客户端能以最快的速度发送数据
2服务器能主动的向客户端发送数据
3长连接长期占用网络资源
4长连接的服务器不方便直接重启
短连接: 发起连接-->发送数据-->获取数据-->关闭
1不会长期占用资源
2方便服务器直接重启
3每次请求与获取数据都要重新建立连接
4服务器不能像客户端主动发送数据
比如说正常访问百度,一旦连上百度,他就会get网页数据,然后就断开连接了
4websocket 握手连接
1 客户端向服务器发送请求websocket的http报文
然后服务器来验证这个http报文是不是符合websocket的协议
2如果符合,服务器返回握手报文,给客户端连接成功
5使用js写一个客户端
websocket test
服务器接到http报文
现在服务器要做的就是验证这个报文了,看他是否符合websocket规范
首先就是Upgrade:websocket 如果不是就要直接关掉这个请求
其中很重要的一点就是:Sec-WebSocket-key
这个key是客户端随机生成的。
6解析http报文
使用c语言的库,http_parser 这个库 百度一下就可下载到
github:https://github.com/nodejs/http-parser
我们最终要一部分就是要把Sec-WebSocket-key这个值取出来
我们用key+migic的方式,使用SHA-1加密,base-64加密编码
migic是一个固定的值258EAFA5-E914-47DA-95CA-C5AB0DC85B11
然后生成报文发送给客户端,这样他们就建立了websocket的握手连接了.
初始化
//第一步 借助http_parser,解析客户端给我们发送来的http报文头部 struct http_parser p; //初始化 第二个参数是解析类型 //HTTP_REQUEST请求,HTTP_RESPONSE响应,HTTP_BOTH都解析 http_parser_init(&p, HTTP_REQUEST);
解析http的时候 解析到头的时候 用这个结构体回调
http_parser_settings 这个结构体
struct http_parser_settings { http_cb on_message_begin; //消息开始的时候 http_data_cb on_url; //解析到url的时候回调 http_data_cb on_status; http_data_cb on_header_field; // key http_data_cb on_header_value; // 值 http_cb on_headers_complete; http_data_cb on_body; http_cb on_message_complete; /* When on_chunk_header is called, the current chunk length is stored * in parser->content_length. */ http_cb on_chunk_header; http_cb on_chunk_complete; };
要用到这个回调 on_header_field 就是解析key的时候
static int on_header_field(http_parser* p, const char* at, size_t length); static int on_header_value(http_parser* p, const char* at, size_t length); struct http_parser_settings s; http_parser_settings_init(&s); //初始化要用 s.on_header_field = on_header_field; s.on_header_value = on_header_value;
开始解析http报文 http_str就是你的报文
http_parser_execute(&p, &s, http_str, strlen(http_str));
//解析head报文 key回调函数
//http 解析回调函数 key static int on_header_field(http_parser* p, const char* at, size_t length) { strncpy(key_buffer,at,length); key_buffer[length] = 0; printf("%s:",key_buffer); return 0; } //http 解析回调函数 value static int on_header_value(http_parser* p, const char* at, size_t length) { char buffer[128]; strncpy(buffer, at, length); buffer[length] = 0; printf("%s\n",buffer); return 0; }
成功解析出来了 至于怎么拿到想要的 就不用多说了吧
通过加密后然后回给客户端报文
buffer 就是你通过 migic+key通过 sha1 + base64加密获得的
这两个加密 网上搜索一大堆 c语言写的就行了
//会给客户端的报文
char accept_str[256];
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-Location: ws://%s:%d\r\n" \
"WebSocket-Protocol:\r\n\r\n";
sprintf(accept_str, wb_accept, buffer,"127.0.0.1",8002);
printf(accept_str);
//发送给客户端
send(socket, accept_str,strlen(accept_str),0);
7websocket接收数据,
只需要在开始的时候握手,后面就不用去动了2
websocket接收数据的协议
1 固定字节(1000 0001 或 1000 0010) 也就是一个字节 十进制:129-130 十六进制:0x81-0x82
2包长度字节,第一位是1,他是固定的 也就是
剩下7位得到一个整数(0,127)数据范围(表示数据的长度);
也就是他125以内就用一个字节表示长度
如果是126就是2个字节表示长度
如果是127就是8个字节表示长度
就是最后面 数据的长度 不包含前面这些的长度
3mask掩码为包长之后的4个字节,掩码后面才是数据,
利用这个掩码,我们可以将这个数据,将他还原回来,
也就是服务器收到的数据实际上,还是要自己还原的.
4兄弟数据: 得到真实数据的方法:将兄弟数据的每一个字节x,和掩码的
的第i%4字节做异或运算,其中i是x在兄弟数据中的索引。
8websocket发送数据
static void on_ws_send_data(struct session*s, unsigned char* pkg_data, int pkg_len) { //printf("================send_ws_data============================\n"); // static unsigned char send_buffer[8096]; //固定的头 send_buffer[0] = 0x81; unsigned int ws_send_len; if (pkg_len <= 125){ //最高位是0 不用管 send_buffer[1] = pkg_len; ws_send_len = 2; } else if (pkg_len <= 0xffff){ //255 send_buffer[1] = 126; send_buffer[2] = (pkg_len & 0x000000ff); send_buffer[3] = (pkg_len & 0x0000ff00>>8); ws_send_len = 4; } else{ send_buffer[1] = 127; send_buffer[2] = (pkg_len & 0x000000ff); send_buffer[3] = ((pkg_len & 0x0000ff00) >> 8); send_buffer[4] = ((pkg_len & 0x00ff0000)>>16 ); send_buffer[5] = ((pkg_len & 0xff000000) >> 24); send_buffer[6] = 0; send_buffer[7] = 0; send_buffer[8] = 0; send_buffer[9] = 0; ws_send_len = 10; } //原始数据 memcpy(send_buffer + ws_send_len,pkg_data,pkg_len); ws_send_len += pkg_len; send(s->c_sock, send_buffer, ws_send_len,0); //printf("send_len%d\n", pkg_len); //printf("send_data%s\n", pkg_data); //printf("============================================\n"); }