找回密码
 注册
搜索
查看: 5200|回复: 0

VC++实现的WebSocket服务器端握手协议

[复制链接]
发表于 2012-7-22 18:23:54 | 显示全部楼层 |阅读模式
本程序是VC++实现windows上程序内嵌WebSocket的部分代码,因为想让浏览器和本地程序直接交互,最好的办法就是websocket,windows的exe程序内嵌一个websocket服务器端程序,浏览器访问localhost,建立交互,这种办法比做成插件更好,所以我采用这种办法来联通桌面程序和浏览器。VC++实现WebSocket的服务器代码,网上还是有示例的,不过基本上不能用,我找到的两个,一个是基于MinGW编译,还有一个是基于VC++2010编译的,还有一个libwebsocket,C语言实现的库,实在是大的惊人,最后,我决定自己实现,实现WebSocket其实不复杂,在普通的socket的服务器上添加一个握手协议,这个握手协议如果用脚本语言实现,非常简单,但用户C++实现就不容易了,我这里实现的基本上是C语言的版本,因为不想使用C++庞大的类库和模板。" g9 B4 \; Q$ x, y$ U2 y) N9 u! R
完整的windows版本socket握手实现:9 n: a) Y  ~# G1 Y2 W; q9 t- X, O2 Q
, {; X- ?+ }2 J
bool WebSocket::handshake(const char* src, struct handshake* hs){8 }, f8 m  \0 Q! B' B
        size_t src_len  = strlen(src), i = 0 ;
$ |* k  E* |; H) M        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前+ l- r! q% X! }# C
        hs->host                = match_string(src, "Host: ", '\0');
3 ~- O4 U3 u8 B% R' P7 \- F2 W' Q9 u        hs->origin              = match_string(src, "Origin: ", '\0');7 Q# A1 c* V9 P; Q' V2 ^
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
, q0 ^1 e- S) |; ]2 f1 v        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
& B6 o6 n( T" b+ f2 @" l8 X        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); * Z4 k3 Q% ^2 H: n( L$ M0 }
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
. L5 F( Z$ p) O) D5 V        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
1 O) I- i/ `0 v3 U9 t        char digits1[64]="\0", digits2[64]="\0", c='\0';9 i) @! z& W7 ]0 z
        size_t spaces1 = 0, spaces2 = 0;
) k% I' j# [0 v' V1 f, p        size_t key1_len = strlen(hs->key1);
; z5 ?0 h' c$ J; ?        size_t key2_len = strlen(hs->key2);0 w, x5 u) i; J1 m7 V
        short d1 = 0, d2 = 0;
$ q' g+ f& h! I        unsigned int result1, result2;
' I6 ]# R& s$ f: \7 |5 j        for (i = 0; i < key1_len; i++){
" m- u% f8 Z# j7 b$ _                c = hs->key1[i];
0 j1 c- a5 f  G! X. W# q' }7 ~8 [                if (c == 0x20) spaces1++;
3 F# `. ?" N$ k                else if(c>='0' && c<='9') digits1[d1++]=c;
( o# t4 C0 ^  e8 T( {        }: h2 a1 L1 ^2 D3 q4 i2 F8 n
        for (i = 0; i < key2_len; i++){
8 A. h( }% m+ V4 m* D: j                c = hs->key2[i];
% i4 k6 y  @! b9 ^; `# B3 h9 E                if (c == 0x20) spaces2++;
1 `; @: w$ q* K/ ^( o, V5 @                else if(c>='0' && c<='9') digits2[d2++]=c;
/ t! e% z7 t  A        }* ~3 u) @0 Q& A1 }' ^& a9 d% q
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
4 Y5 d* g& ^# I! e; B9 P7 e        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
' y* x6 {3 t7 P9 H4 C- P        char chrkey1[4]="\0", chrkey2[4]="\0";
* e0 X8 N+ |) Z0 B        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
/ J4 [/ r/ o! v5 q  \  M& H        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);6 ], _) e/ g( }: b* x, v% C. W
        unsigned char raw[16]="\0", dig[16]="\0";9 b% D8 l$ k, J  ]
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
' c$ q" ]& @4 }  Q6 c. N        //  连接上key2的最后连接上头信息中的最后8位字符。
9 P; ~$ \. D; z' Y0 ?/ T        memcpy(raw, chrkey1, 4);
6 k  Q  s7 O! x% x, W) u        memcpy(&raw[4], chrkey2, 4);( P$ o: z8 G9 j8 \& Z9 k& P2 D, y7 U
        memcpy(&raw[8], key3, 8);
! r8 _6 o: _4 R7 [        //计算的md5值
3 h8 B3 }# Z3 y) q/ H        md5_state_t state;
+ M1 l% S4 \3 [, X4 q7 t( F        md5_init(&state);
0 |) D$ p+ b0 o+ ?1 M" Z        md5_append(&state, raw, 16);
! u' X( Z( l! y- s* M        md5_finish(&state, dig);
% n9 I$ W9 e; K6 Q* c" v. N5 k        7 l, ~$ z" y0 x9 I# ~2 e; B8 X
        char handshake_str[BUFSIZ];
& N7 q* w* s+ G        memset(handshake_str, 0x00, BUFSIZ);
$ m, E8 O7 R& r% ~; ^- K- _4 ]        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
- D' E* |0 ?4 ]4 R- A( T. K                "Upgrade: WebSocket\r\n"
3 t, i6 I+ D" F) f1 E" F                "Connection: Upgrade\r\n": A# u5 m6 z& f1 i
                "Sec-WebSocket-Origin: %s\r\n"
; Y$ T! O; v) k; g6 Z; ~                "Sec-WebSocket-Location: ws://%s%s\r\n"1 S5 j  R/ L% q: V
                "Sec-WebSocket-Protocol: %s\r\n\r\n";
* [  O- _4 M, G: a        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);' |! i- r# {* s0 c) k# a* z
        free_handshake(hs); // 释放handshake指针,已经不用了!
5 |3 u8 P4 u! H        char response[BUFSIZ];
; ], {4 [& ]' t. ^1 v& T$ b* {        memset(response,0,BUFSIZ);
! w6 P' p# |# \        size_t j=0, handshake_len=strlen(handshake_str);
: r6 _- U, P8 x, X0 g+ \        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];% h' X% d8 {& Y/ m6 k  N# s: c- v
        for (j = 0; j < 16; i++, j++) response[i] = dig[j];4 u& d4 d1 |) N: ]+ M8 \
        // 这里的clientSocket就是连接好的socket对象了。6 @* Y6 s9 B+ W5 R+ }
        int sent = send(clientSocket, response, strlen(response), 0);
) i3 H4 Y2 f1 D0 o3 @4 Z        return sent>0;
/ `% T9 r% m* `. O' P; W7 H1 b}
# D/ a4 E6 C) w, \目录结构:
/ C$ }) p8 ]# B$ |& s7 U* Asocket/
& |5 v' v, I& Q, t$ V8 @      socket.c
6 W9 C+ k- [0 `      md5/; {# [# Y* ]5 [
        md5.h5 e' v2 e, a) R5 @' z
        md5.c. \/ x+ I; U0 M0 |
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
- C& W$ y: X5 I+ a: W6 H编译命令:  gcc socket.c md5/md5.c -o socket.o  8 x6 t; V6 f* l5 p* g, z8 ?, w/ M' j
GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
: A7 W' }0 i, P6 Y8 @8 w// socket.c 代码。: H' h, O% v. Q, |# \* T3 I
// web-socket example . C9 w8 V9 w5 h$ n
#include <stdio.h>
1 t- f+ |) G5 I4 y  t% C#include <stdlib.h>; J) E9 P- ?" W4 t! K
#include <string.h>
9 O' M1 }$ k! m#include <inttypes.h>( ]5 \# v% D" K$ [2 o2 s( T
#include "md5/md5.h"
" y6 i8 }2 c! b% |6 h% p; R6 L: C, V7 d  X9 j6 W2 ^' c1 s, Q
// #define BUFSIZ 512: G" E! H0 @0 t8 m- ^1 D

1 L; K3 _; D( B$ D) t8 S1 e/ A; ^//定义handshake结构体变量: N  @5 ?. e1 x
struct handshake {
; l; \* u+ L/ @  r- D: L( S: S+ a        char *resource;6 }, {, m5 ^9 q1 e" {8 ]7 U2 F
        char *host;
* [) x) x! S5 }+ B6 L1 H) e' F        char *origin;
7 Z, H( z! _* _        char *protocol;
7 N1 i( K* e1 e2 K; D2 X) l( \' Y! p# i        char *key1;- j8 i* r# }4 z7 |9 [
        char *key2;' l9 [  }: t! S: q9 z6 u& I
};( n3 n9 W! G: ~! p

2 z0 v+ m# x# [9 v$ E
: W( w* l6 V' A//释放握手后不再使用的部分变量6 ^' D4 e! K6 R7 |, _
void free_handshake(struct handshake* hs){$ U( d$ }  X6 `3 m
        if( hs->resource != NULL )      free(hs->resource);: D# L2 p- l3 X, i6 ]0 |
        if( hs->host != NULL )          free(hs->host);) E2 a+ @  ]( x- X
        if( hs->origin != NULL )        free(hs->origin);
: A% Z- s9 D7 y* U/ D        if( hs->protocol != NULL )      free(hs->protocol);& ?, ?# O1 z' q4 ^
        if( hs->key1 != NULL )          free(hs->key1);
6 P, ?2 q! `! V' c        if( hs->key2 != NULL )          free(hs->key2);
, F( w5 @# A% Y5 }}+ o$ e6 _" N( }* i

. E3 e- i' N0 k* y7 M1 c2 Q// 这里对上一篇的match_string做了点修改,
+ P& q- X0 ]; |// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
1 S4 O- C" x- c( w3 Qchar* match_string(const char* src, const char* pattern, char end){, D% F  W$ N3 m8 A
        char buf[BUFSIZ];
* M+ R+ Z7 O& D3 ]        memset(buf, 0, BUFSIZ);
, A0 g3 B  j5 X' S9 l: U3 _' B        size_t src_len = strlen(src);
7 }# q: k* W8 z- d, i        size_t ptn_len = strlen(pattern);
/ d% w% `% u: k6 ^        unsigned short b=0, p=0, i=0;
5 |- F2 V9 c( p; N* V2 d        char c='\0';8 @! j. i4 i2 {! Q2 H( c5 @
        for(i=0; i<src_len; i++){* s3 k0 W& F/ j" j/ s
                c = src[i];4 [+ [/ H9 b4 J; a2 g0 |9 k
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
2 J% Z6 Z, ~/ R5 J8 f( ]6 {                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
6 U' \- k( }+ K( j4 h9 E7 @! v                        else buf[b++]=c; // 匹配到的字符
0 y4 w: _9 H' X                }else if(p<ptn_len){ // 为达到匹配要求
' o( ^0 x6 ^! l7 _% c                        if(c==pattern[p]) p++;
- J2 {. `3 u1 e' ]" F4 m$ @                        else p=0;0 b' W! ~. X/ o5 S9 q
                }
: \2 y$ p% s. D        }
3 j( r+ q3 J! L5 g% v  V        size_t ret_len = strlen(buf);
* C( m2 i# T/ R+ x$ G. P4 H6 M        char *ret_p;
( N$ f/ ]6 N# f0 ?8 f$ f' \$ U$ Q        if( ret_len>0 ){
' |% [! |9 h/ |3 C                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
; @* j5 {, X/ ?. y* w                memcpy(ret_p, buf, ret_len);
, k; M% u6 \, u  Z. s        }else ret_p = NULL;+ }+ O! b- E* W5 g! D* W  A
        return ret_p;
8 ], U/ _' `! r8 D$ e. B}
* o, b7 \4 m) c  S3 c- \
& B3 Z! E: \& x+ C1 t// md5 加密函数,用的是网上一个实现的比较通用的版本。( g7 _3 x2 w  ~$ z- K
void md5(const char* src, size_t size, char* digest)% ]$ i2 P& U/ a6 ?2 O2 \6 b6 v
{# G- }  P8 Y- S
        md5_state_t state;
; n$ J5 R% H" H- i( t, s0 F        md5_init(&state);; e/ w5 w0 Q! ^' U
        md5_append(&state,  src, size);; y, g' g" A: S
        md5_finish(&state, digest);
# `& l& Q0 q) C- t1 }$ Y, N}
8 v: K/ W7 b) n; K) h) S' U; _, d3 {/ Z/ ?6 _* T1 B% \! b1 u5 m
void p(char*s, int len){( o* n8 ?% G, {8 U7 a* Y# x; V
        unsigned short i=0;
4 G; y- `1 `' L4 L( Y' k" J: C        for(i=0; i<len; i++) printf("%c",s[i]);" Y* E! u9 o# L( v; I% e3 B
        printf("%c", '\n');
! ?9 m. R4 f) U9 [- T. M}
* ]$ j4 k; f! ?, c4 N. V4 M2 t) b8 P: g
void handshake(const char* src, struct handshake* hs){$ G% Z5 b3 m8 C4 }5 W# x
        size_t src_len  = strlen(src), i = 0 ;) ]# w! {! m% X9 O, F: {5 W
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前# j1 g) B0 @' i, A0 W
        hs->host                = match_string(src, "Host: ", '\0');
" Y" g6 X) Z) H, i: I& H& H        hs->origin              = match_string(src, "Origin: ", '\0');4 }& P1 Q. E3 D( b0 f4 A7 h5 m
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
* C) n3 |% B4 @& ~        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');! F4 P$ b7 h3 t% t
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); $ j, D: ~* |! c6 K6 F- v
        // 获取 key3,即最后的8位字符$ N, Z/ V; t5 \2 e9 p
        char key3[8]="\0";
$ P2 a7 m+ }" G3 }        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i]; & `" w" ~+ y/ m, X" ]3 J4 w6 K3 A
        char digits1[64]="\0", digits2[64]="\0", c='\0';  `7 d6 D4 X8 a6 T* E+ Z
        size_t spaces1 = 0, spaces2 = 0;7 q- B' a6 n/ g* e5 u- i. h! q4 z
        size_t key1_len = strlen(hs->key1);; }' S; V7 |" M" }
        size_t key2_len = strlen(hs->key2);! R6 O: w) c+ E; I$ P
        short d1 = 0, d2 = 0;
4 y0 Y: E8 ^: M        unsigned int result1, result2;
* ~' L. e! A6 ]        for (i = 0; i < key1_len; i++){
1 k2 o: Y+ r% `5 z4 T# I' y6 D& h                c = hs->key1[i];
( ~( q* e! @* D+ {1 V# ]5 e                if (c == 0x20) spaces1++;* J# z: S) ?' t, G- h
                else if(c>='0' && c<='9') digits1[d1++]=c;
( ]0 k' s4 \) _" W4 I  q        }) J- f) a( A; N; k1 M
        for (i = 0; i < key2_len; i++){ 4 [/ x5 E* d! b+ ]3 K
                c = hs->key2[i];) n, Q' _! [/ q7 o. _1 M4 l
                if (c == 0x20) spaces2++;
. Y. P+ q' F6 H- @                else if(c>='0' && c<='9') digits2[d2++]=c;
) w/ _4 J! l0 e+ Z7 J        }
* F! j% Y. }+ x6 w( `        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
2 r7 u8 S" h0 a8 F) I        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
3 t3 a; d7 Z& P# a        printf("ch1:%s\nch2:%s\n",digits1, digits2);
# i7 F% O) i/ J        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
- E5 B$ y$ p1 q8 T$ w        printf("d1:%d\nd2:%d\n"  ,result1, result2); % B2 E2 M+ e5 H% `) L5 y
        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
$ H( ^/ I' M9 o' K4 h4 Z. v& l        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);0 V1 x0 w# @2 j" M$ O
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);6 Q: k! z. E& O; K5 C
        printf("ch-key1:"); p(chrkey1,4);
0 j9 N2 e7 X3 y( g: p, g+ ~        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
6 s- ]3 m8 T+ ~, w& t        printf("ch-key2:"); p(chrkey2,4);$ S; N, l( ^+ r* v3 a
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);/ }0 Y1 z9 U- V) |" Z
        unsigned char raw[16]="\0", dig[16]="\0";
$ \. B$ `9 c" q' }9 d1 i; N        memcpy(raw, chrkey1, 4);
: w" H1 ?$ m( \* M+ j: f        memcpy(&raw[4], chrkey2, 4);/ g( U% z1 P! ?5 d( w
        memcpy(&raw[8], key3, 8);! }+ k8 v' r3 w! s: r9 {
        //计算的md5值
$ P) }+ n& e( f        printf("\nraw:");
) ?/ t8 q: a; X, r2 k1 I        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
7 Y) N* o2 w) F/ V7 x2 \, C        md5(raw, 16, dig);
0 T1 E+ W" V* s        printf("\nmd5:");
! l' A6 h1 E9 H5 W        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);$ y/ R8 h7 Y. N8 h* o3 |& ?
}
$ P( Y) h% W) b: P! u7 U
, ^# K0 ~4 |. v- T7 }6 c3 F" p7 x# M7 H+ T8 J8 T/ R* ]- [5 f  l0 V6 @- l
int main()* I' `0 `3 \( g" R5 H4 S3 h
{
5 `5 ~: @/ U9 u* Q! n        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\
4 I0 d' S8 y" P* d( x3 z9 S3 O        Upgrade: WebSocket\r\n\  l% r+ p- y, l4 p9 [
        Connection: Upgrade\r\n\1 }2 p; M1 g, `5 H7 V5 T0 v
        Host: localhost:4400\r\n\
" s$ i& Y& `' ]+ D7 c        Origin: null\r\n\
$ W! e- S4 w% F3 C        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
7 M4 v$ w6 G& k/ H        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
5 V  d5 Z4 I. L0 f! H1 A        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";* V+ Q: p( y# ?5 H- b9 X' o4 Z
        size_t len = strlen(msg);( M  ]) x5 j" s8 d% z' J
        msg[len]  =0x1f;& s) O+ c' k0 V5 a: V* N
        msg[len+1]=0xf6;4 R) K( B4 n  I& t3 Z
        msg[len+2]=0xf3;
5 Z) F; h! L/ t- v9 ?: X0 M* o6 N        msg[len+3]=0x3f;
6 u3 d7 v' d0 U        msg[len+4]=0xc7;
/ K1 N) e: X5 w        msg[len+5]=0x17;8 }/ S. t$ ?" d, p
        msg[len+6]=0x20;. x. l* H5 Y( h' Q
        msg[len+7]=0x88;  s: s. l/ s. s6 ?, {
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};/ P* H) W  t- ^
        handshake(msg, &hs);; W5 ?) y8 f, B! g2 \: J! d+ g
        free_handshake(&hs);; b- l. [& V0 V
        return 0; + e. C: e: R$ Z. e/ s* N
}
6 i' w& K( |) R: I9 I& A
- c8 |; g( C! ]2 f5 E/ k  N2 S" m& w, m3 c
测试的结果:
5 Y, q" _$ M8 ^8 t) ?: Kraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
" ]# C" n( d' ~/ kmd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 , P* O* O- F$ c
对比了nodejs的版本,握手部分生成没有错误。
4 s% M* ^2 T0 p) ^& V, U; o4 Q
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|宁德市腾云网络科技有限公司 ( 闽ICP备2022007940号-5|闽公网安备 35092202000206号 )

GMT+8, 2025-9-30 06:31 , Processed in 0.037817 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表