找回密码
 注册
搜索
查看: 5762|回复: 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++庞大的类库和模板。1 S. X8 y0 {. d) u& @4 z
完整的windows版本socket握手实现:/ ]7 Z5 r! X: p" q

0 s  b+ X4 f; obool WebSocket::handshake(const char* src, struct handshake* hs){: X& |& H4 ~4 z# ^; ^, j, ~
        size_t src_len  = strlen(src), i = 0 ;% Y) `, E. ?4 B$ h: S
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前. y  m% |! N, \8 i
        hs->host                = match_string(src, "Host: ", '\0');
6 `* ~7 i" a4 R  k# n        hs->origin              = match_string(src, "Origin: ", '\0');" V& m% ^/ a+ t
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
% T& ]0 B  L  V% d0 {. r$ j4 ^# l        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');# ]7 y, s2 K+ B' i9 p3 l9 b& A; h
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');   a: @- u; o- G6 q
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
: S8 s1 i+ b: A* N8 f; f) _        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
3 K8 _2 F1 ^7 C9 A9 ]        char digits1[64]="\0", digits2[64]="\0", c='\0';6 K  O# G6 U1 o  J0 }+ t
        size_t spaces1 = 0, spaces2 = 0;
) ?% j. f- l- k$ [0 X# j' g        size_t key1_len = strlen(hs->key1);6 ^9 T6 q" i8 ]/ k& f; \- G4 _
        size_t key2_len = strlen(hs->key2);
+ K/ y% e( w4 j1 I+ ~% M, R" ?: N" h        short d1 = 0, d2 = 0;
6 W% K/ @( r( R# J5 k( z) e        unsigned int result1, result2;
3 w! F: n! V  k        for (i = 0; i < key1_len; i++){
5 u* g# n' A0 N; P" ^                c = hs->key1[i];: d/ E0 U- `% o; @6 r4 N8 F
                if (c == 0x20) spaces1++;0 Q* I9 l7 r9 @2 ^5 N6 _
                else if(c>='0' && c<='9') digits1[d1++]=c; 8 e" o# x% M1 n, i$ B
        }
1 v1 V7 h5 s9 y+ {% C0 i! i        for (i = 0; i < key2_len; i++){
" l  d1 [8 |* ]. @. B" ^$ W  ^                c = hs->key2[i];
4 Y% y( s- k, }( T) w  @                if (c == 0x20) spaces2++;( m1 d6 }/ A" [" N
                else if(c>='0' && c<='9') digits2[d2++]=c; ; n  z! v; U+ V- X& k: n& _
        }, b6 J8 `. Y$ N# C1 {/ E! }- V6 p
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
: A. q1 Y7 v$ Y        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
$ U1 ]1 g( r. F3 i) V! @        char chrkey1[4]="\0", chrkey2[4]="\0";- ?7 B' E' h4 s/ R/ @
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
0 k+ G( y# }6 V        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
1 n% ?# ~7 Z9 C4 {5 I- B- J3 }        unsigned char raw[16]="\0", dig[16]="\0";$ e  n+ L0 |: D" ^2 j) u- d: F
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
. j8 Z8 h2 D8 e; P$ G5 I: D        //  连接上key2的最后连接上头信息中的最后8位字符。
7 ]/ k, O) V" b  d4 {        memcpy(raw, chrkey1, 4);
+ y0 J1 u9 H/ y) ^- @5 {        memcpy(&raw[4], chrkey2, 4);, K2 P9 q- Q0 j' T* E( _3 J
        memcpy(&raw[8], key3, 8);% G) K2 t4 t' ~/ ~# l0 M
        //计算的md5值6 y9 t. c6 [- h# A) y
        md5_state_t state;8 y: t4 M" l. W' _( b  v
        md5_init(&state);
% ?1 S, z! K5 e/ o; d) }1 N9 S        md5_append(&state, raw, 16);
7 n6 y  Q+ V9 x3 p" \0 ?5 y! @        md5_finish(&state, dig);
$ k3 ^- ?0 R$ G3 u; C        & Z6 C( _, X7 d& G* V- w
        char handshake_str[BUFSIZ];
$ |$ H) T, y* _8 a8 J        memset(handshake_str, 0x00, BUFSIZ);1 e! {# K8 e- }2 ~- f9 o
        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
, q3 D/ ~+ P8 V* `6 ^                "Upgrade: WebSocket\r\n"
. u! |6 q9 P- ^. l& y                "Connection: Upgrade\r\n"- V9 p7 k; I; a9 |& r5 k* `
                "Sec-WebSocket-Origin: %s\r\n"
; }7 c& R& E) \) ^                "Sec-WebSocket-Location: ws://%s%s\r\n"3 L( a% Z6 b2 ]' u# T, |! J2 k$ `; i
                "Sec-WebSocket-Protocol: %s\r\n\r\n";( J% b! ^' `3 w3 }; ^+ v% q
        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
. ^( U4 ]$ w2 a7 f4 v6 x        free_handshake(hs); // 释放handshake指针,已经不用了!
2 t9 j+ a  q4 ]        char response[BUFSIZ];
, A) t+ V+ W; d; D: j9 u        memset(response,0,BUFSIZ);
. I2 N* \6 x) @  o7 i( z        size_t j=0, handshake_len=strlen(handshake_str);
3 ^* t, F/ ]9 A+ m7 k7 w        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
% ~1 B& n; l0 r6 K; |# j4 E        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
) w7 s) S0 K% g, l, ?        // 这里的clientSocket就是连接好的socket对象了。* l% Y) l3 W, h; i6 a! G. ~
        int sent = send(clientSocket, response, strlen(response), 0);8 Y9 n7 i1 Y6 ^" l
        return sent>0;( C7 G5 d" b9 a1 c
}
8 }- C) a% n* R3 f目录结构:
! i4 n5 o. E9 P2 F6 Fsocket/& F% M# s0 ?# ]/ ?
      socket.c" h) _8 H9 Q9 e* w7 k
      md5/0 G9 P; A; O5 E0 t5 c1 D
        md5.h& E1 g! o9 t5 W% K7 G. q; B
        md5.c
4 b" l& J+ _& ^3 x: V库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,9 T( A3 u) d( c) Z$ |7 x
编译命令:  gcc socket.c md5/md5.c -o socket.o  : |" o' a4 U4 U: ]: V( q8 R
GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。8 p4 T+ U6 V) G& g0 o! Y
// socket.c 代码。; L' m  M3 p' |
// web-socket example + z- @9 R0 Z% Y2 X
#include <stdio.h>
$ o1 n3 k8 Z/ y! I0 B7 H#include <stdlib.h>& f( D0 R& |' y& G  e: ?4 y
#include <string.h>! ]4 J: [. ?) W8 C) w( n
#include <inttypes.h>1 j. b& o2 j3 w$ w/ ~
#include "md5/md5.h"
% y! Q6 M& Z/ I6 \8 w, y/ J; j! Q2 J/ e: b& I6 t" x, X3 Q- I
// #define BUFSIZ 512
$ r  ]2 F! U+ v( y  F
/ ?- ]% m0 e4 P1 n  p5 e- z//定义handshake结构体变量
# V8 s" g: q6 [! Q& Cstruct handshake {' _9 R! o6 P' |. g' s& x# s! K) _0 a
        char *resource;3 `# c& l& Z: B+ L0 }) |: T/ K
        char *host;3 L( L% O: R6 R0 l4 V
        char *origin;
8 N+ H4 e& \3 W7 c/ a, P        char *protocol;; F/ D0 Q' D  ]4 W/ X4 k( ^1 r
        char *key1;
: P* r7 n9 Q# ?0 f8 f        char *key2;
2 _2 U9 Y+ Q' q6 o( d4 c4 j};& w: ]) X' S# X4 y
7 |, }0 F; m; k$ m2 _: E
5 Y! r$ i0 f/ M, d, O7 ~0 g. d
//释放握手后不再使用的部分变量
$ f' ]6 V$ F' Z) |4 w: H( `( Ovoid free_handshake(struct handshake* hs){
( S6 o' x* z; q9 y; I. }        if( hs->resource != NULL )      free(hs->resource);$ A) Q/ Z* ^. D1 s& W1 z5 t3 e
        if( hs->host != NULL )          free(hs->host);/ x8 h8 w' \) [# {' \
        if( hs->origin != NULL )        free(hs->origin);
  _6 i! h3 K$ ]% u. ]7 f        if( hs->protocol != NULL )      free(hs->protocol);( C& z! k3 K+ B1 @7 H. W: X& v
        if( hs->key1 != NULL )          free(hs->key1);) y5 J8 u0 A+ V% |
        if( hs->key2 != NULL )          free(hs->key2);
( r+ I2 \% v$ w) E}6 F7 r- G: G$ ?: x5 C
- h" R# s$ D) K1 y
// 这里对上一篇的match_string做了点修改,
+ g! ~5 L3 u; ~9 B// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
' t4 {/ z- c0 Y; uchar* match_string(const char* src, const char* pattern, char end){# X& B. @/ e) _  f) u: ^$ U1 I- @
        char buf[BUFSIZ];
1 D1 C6 E+ R. B  [7 H  q        memset(buf, 0, BUFSIZ);
( t2 G& k$ T. P2 w- O/ }; D        size_t src_len = strlen(src);
! j. P0 {3 V8 [8 x) [: [6 i, Q        size_t ptn_len = strlen(pattern);& C) W4 U3 u1 H% Q
        unsigned short b=0, p=0, i=0;
4 A) ?( J! H) G5 c# w        char c='\0';+ [/ J8 ~! a9 m# ^8 s' t
        for(i=0; i<src_len; i++){8 D# }6 m5 ]* a2 x/ c/ {$ k" t' j* F4 ^
                c = src[i];
" C( M4 [  ?/ p4 V' G  f* @* R, [# \                if(p==ptn_len){ // p==ptn_len 表示正在匹配中4 ^# {1 y# v5 ]* ~& o! I
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
, u4 M3 |! Z5 ?7 [                        else buf[b++]=c; // 匹配到的字符 ! U4 c) Q8 m, B0 s4 f2 U
                }else if(p<ptn_len){ // 为达到匹配要求; n# F' P" c1 X* a& k+ H7 h$ C
                        if(c==pattern[p]) p++;- W( b! X1 t2 S) t( l9 J
                        else p=0;
+ T( J; V  e/ i                }0 `0 R) j8 R: v/ b5 M" y0 J' h
        }9 `/ p2 j) i! H8 x' f
        size_t ret_len = strlen(buf);
+ o" ]1 d# K6 N* b$ G        char *ret_p;
+ z; W0 L8 `5 T1 Q4 f        if( ret_len>0 ){6 o) I  I  d6 y* ?" F2 F3 }4 D/ B
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
! Q9 ^, w$ N, q5 e+ O* \                memcpy(ret_p, buf, ret_len);
/ ]4 |) i5 p2 {! c% Q        }else ret_p = NULL;# |" f$ t5 Z4 c% J
        return ret_p; # X. }: e0 a, g+ Z# }
}* c( X$ [+ Q' h! H) `
; u2 {7 f. H5 y4 }, ?
// md5 加密函数,用的是网上一个实现的比较通用的版本。
' [7 S2 C2 V: f; ]( lvoid md5(const char* src, size_t size, char* digest)8 H( V) M* ~5 s
{
: P1 `! ~# C( Q4 M        md5_state_t state;5 a/ D; n9 e  R
        md5_init(&state);! C0 ]. j+ k" H1 F! i: @5 X' }! l% L
        md5_append(&state,  src, size);
" y( c! b5 [0 h. z        md5_finish(&state, digest);+ P0 S/ r3 c' s  D" x
}
) J+ M. O9 L. ~- i. E
8 J& F3 s0 j7 p0 n  ~+ Gvoid p(char*s, int len){
9 p$ G. @8 w4 x) X  z- E& x+ l2 E        unsigned short i=0;
7 K5 e; z$ b! X1 w5 u        for(i=0; i<len; i++) printf("%c",s[i]);# C3 b- \" d% R2 u" p
        printf("%c", '\n');) x3 d! b0 D& V* z; T8 c' \
}' h; ]6 I5 @9 Q* f6 E' X# `2 V4 i

1 g' D& [9 G6 y: e  j% @" W, Yvoid handshake(const char* src, struct handshake* hs){
- [" t) H4 c$ ]) l        size_t src_len  = strlen(src), i = 0 ;
9 ~) h# F- w* A, M! P        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
. R6 Y# ?- z8 ~        hs->host                = match_string(src, "Host: ", '\0');6 u% @" m8 m7 K' s& g, _
        hs->origin              = match_string(src, "Origin: ", '\0');+ }* d: s9 ~% }8 e$ s
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');: x+ a; k- g7 L0 P3 k2 q4 w
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');6 H- @7 Q# }# _
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
$ I- ], H! j* {( ?) S        // 获取 key3,即最后的8位字符4 G4 {1 u/ I) v7 q9 b' L9 _3 ?
        char key3[8]="\0";( ]( Z2 b8 R7 L7 F) V7 J
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
, I- O# w8 @7 q        char digits1[64]="\0", digits2[64]="\0", c='\0';5 O# j& q- a8 f  ~+ {
        size_t spaces1 = 0, spaces2 = 0;
$ F) L6 @2 T! u" T& _7 x% c" t& X        size_t key1_len = strlen(hs->key1);$ n# U& t1 j  v: `: A: M
        size_t key2_len = strlen(hs->key2);: M7 ]) O; n9 e& Q& r8 v; X+ F
        short d1 = 0, d2 = 0;
' n$ V4 [4 T! H        unsigned int result1, result2;: ~/ d, x$ _; ?
        for (i = 0; i < key1_len; i++){
, W5 [  N, D0 l7 m                c = hs->key1[i];
+ L8 y* N" _. ]7 s" O! Z                if (c == 0x20) spaces1++;
* R: p9 B; w  \) p$ |% p! s$ h                else if(c>='0' && c<='9') digits1[d1++]=c;
: Y/ Z1 c. l6 M" A) Z6 q& M        }
6 Q, V+ A6 ?7 l$ e        for (i = 0; i < key2_len; i++){ 1 x+ i% P% \4 P; C. c. I% d$ M
                c = hs->key2[i];0 j( f8 p* K/ Z3 y- l1 I0 N2 T
                if (c == 0x20) spaces2++;) ?% f" M4 @0 J% j- y; M) a3 g
                else if(c>='0' && c<='9') digits2[d2++]=c; 3 L6 ]2 H6 N2 ?) v
        }5 X$ ^! `3 q; M" Y" _! |, S4 _" o
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);5 B, C0 }) h' L+ l) \, D3 o/ H
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
1 Z/ z+ T# Z% T# u$ y        printf("ch1:%s\nch2:%s\n",digits1, digits2);. X; }+ C2 W( }0 c
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);" |/ k$ D" L: X$ x! b: V+ }* u
        printf("d1:%d\nd2:%d\n"  ,result1, result2);
( p# P( l( o. X        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
" `" p2 \, ]! r9 Y$ {3 w        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);4 n" ~0 G, Q& x) h3 \- R
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
. u" j7 C. m) d! n8 G. _# M2 C        printf("ch-key1:"); p(chrkey1,4);
$ g, ]8 X7 q6 Y! ^4 |! ^: u: n) C* `7 T        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
$ K9 j4 r7 |5 \) Z; R        printf("ch-key2:"); p(chrkey2,4);4 L! ?$ H! b: l$ G
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
& Q+ z7 D# c9 L* q        unsigned char raw[16]="\0", dig[16]="\0";
4 Z, P; l! E# _& Q        memcpy(raw, chrkey1, 4);
( E) s3 L" ~( x' G! O# q- {$ U        memcpy(&raw[4], chrkey2, 4);' Z' L: O3 j7 H$ q  e
        memcpy(&raw[8], key3, 8);2 G2 M0 C7 j) D  R, s& @3 Z
        //计算的md5值
9 v+ a5 r9 H2 R+ S! {! g6 J1 X        printf("\nraw:");
3 ]; d$ Z3 _2 y( h/ e4 p        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);, o& S8 T& x$ l
        md5(raw, 16, dig);
: B! ?5 J* q9 b5 X8 Y        printf("\nmd5:");" {0 T1 x2 p6 ]; C. E! [
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);) i/ o& @3 s5 O/ v6 ^
}
4 u6 i% b- y! v9 F* R; W8 K# G1 Z* a! z& P

0 Q6 Y8 P5 R* o$ j5 Gint main()  ~  w4 E4 i' S1 m/ O
{
0 }" O4 Z% e# k) |9 j) B' }        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\, O2 j4 b8 W+ h
        Upgrade: WebSocket\r\n\
& m) W3 R  n5 c6 i! d. b( N2 k        Connection: Upgrade\r\n\
7 Y# Y2 |1 I% D( w/ I7 t        Host: localhost:4400\r\n\" ?; Y/ N% x$ W
        Origin: null\r\n\
8 b; z* K8 t5 ]8 @5 E: W" `        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\  X: M: H& X5 K# Q: k
        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
1 _) X0 Z4 m8 Z- L! W  e        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";/ _) f* ~. O/ Y5 E* {7 R) X+ V
        size_t len = strlen(msg);  O7 e4 Y; `" n( L2 ]# R4 W
        msg[len]  =0x1f;5 P' \# ^. u# n$ v+ b6 T" a, G
        msg[len+1]=0xf6;& H5 ~+ C! F: @2 X3 B
        msg[len+2]=0xf3;, E% W2 V0 n$ [1 ]1 g
        msg[len+3]=0x3f;
! `8 o3 O4 N2 v- S7 @        msg[len+4]=0xc7;$ t: ~! Y3 o9 c# _  D/ r
        msg[len+5]=0x17;
1 s& X+ X# a9 k        msg[len+6]=0x20;" A8 M: C7 S* o; O
        msg[len+7]=0x88;' C8 Y* Q8 `+ u, N# z& G4 j# l0 U
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
, k* D( q; U/ g' }( i! A, a        handshake(msg, &hs);6 E9 E* }* e; b
        free_handshake(&hs);
) D# Q* T2 H& D        return 0;
# \# D. s, d4 u. k/ K: P}% x8 n! W- x. L2 l! @% @0 c0 G6 |: A
" O. M* F5 }' _
  u# a6 m! J6 |7 m/ U( e# [
测试的结果:4 w' L1 |8 b% S' o; F9 J
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
( p6 i: }4 M- r! a* l) t  ]" Emd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 % z9 p& O+ f; K, F! E8 X
对比了nodejs的版本,握手部分生成没有错误。& E2 x2 {/ p3 H' p
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-8-14 00:59 , Processed in 0.028726 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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