找回密码
 注册
搜索
查看: 5674|回复: 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++庞大的类库和模板。' y2 G' G. [! z/ Z; C
完整的windows版本socket握手实现:
" \" F4 K+ a4 `# n& F- X
6 u: V) T1 Y/ ubool WebSocket::handshake(const char* src, struct handshake* hs){* W3 u7 |* x4 x7 s
        size_t src_len  = strlen(src), i = 0 ;; ^, R: o, Z6 t$ R4 B
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前/ K! G6 v" l& M, ^3 [" M
        hs->host                = match_string(src, "Host: ", '\0');2 m/ A1 V( h" d3 G- b) E( J1 l
        hs->origin              = match_string(src, "Origin: ", '\0');
5 n3 \  p+ \  s# D/ N        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');% J" y+ e7 E% c: v5 ]
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
! z4 K+ ^; s  j0 U  M        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); . [: H+ v" `2 r/ V
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
0 y4 t8 ~! ?% R3 `% A5 _        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; + W; V2 Q8 G; t2 t# ~
        char digits1[64]="\0", digits2[64]="\0", c='\0';
) x% S/ k% m0 ^4 Z! U4 l, ~& z+ w        size_t spaces1 = 0, spaces2 = 0;9 s5 I  y7 U) X$ O: f; B# R
        size_t key1_len = strlen(hs->key1);
) J2 A2 i( X4 s) j4 N        size_t key2_len = strlen(hs->key2);1 m4 U" n. j7 q
        short d1 = 0, d2 = 0;
4 G+ J# L" F6 X' L5 e1 k        unsigned int result1, result2;
1 ?; f% {4 ~! z/ K" ~5 w        for (i = 0; i < key1_len; i++){ ( [( q. e: R( r* J. \. H9 U( r
                c = hs->key1[i];
% `" G' L4 R) [9 j                if (c == 0x20) spaces1++;( e! ^  J+ J4 Q" w. T# K0 H
                else if(c>='0' && c<='9') digits1[d1++]=c;
2 t# c$ |( k. K, A  O- C' o        }
" P; ^! |7 T7 d8 V1 h' C        for (i = 0; i < key2_len; i++){ 0 h, g$ ?6 P5 ~6 ^' r: s8 d! i! v
                c = hs->key2[i];
; U1 [3 x) _9 Z6 L6 V7 q                if (c == 0x20) spaces2++;0 d! ~" S4 T' g. ?: p# N
                else if(c>='0' && c<='9') digits2[d2++]=c; $ x7 ~, s* u* p* A4 a% g" ?
        }1 w! O9 ~( |) _' \' y2 y$ h& i
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);6 |+ x3 V- [- \( i& o, [! ]
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);; `* s3 b8 m' g$ V; _* O7 D2 X
        char chrkey1[4]="\0", chrkey2[4]="\0";
0 f! J4 a- L0 C* O# V        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
/ P' J  y1 F* L) q        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);( o" Q4 X! R( m
        unsigned char raw[16]="\0", dig[16]="\0";
) {$ z5 p1 F+ I5 @4 j        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,2 f/ y8 m: G4 w
        //  连接上key2的最后连接上头信息中的最后8位字符。
& [" a; ~9 C  e6 y$ y* z  g        memcpy(raw, chrkey1, 4);: |/ I# L6 N( h5 f( Q# Z5 p
        memcpy(&raw[4], chrkey2, 4);. r7 k; c0 b) @" O( f- \1 l7 v
        memcpy(&raw[8], key3, 8);
' e+ q6 v, H& H7 l6 {6 t: m) C# F' H        //计算的md5值
0 I- l$ Y. x, \/ k/ e6 {% i# Z        md5_state_t state;$ y, s4 C$ V  C
        md5_init(&state);( I( }* D1 I! D4 a% a4 b
        md5_append(&state, raw, 16);2 ]2 ]- G9 e- L. c1 T* t3 V
        md5_finish(&state, dig);- v6 b( U+ l) c: A4 u
        
! s  s6 _4 b* a. _0 P6 Z9 o        char handshake_str[BUFSIZ];
9 e7 t4 T  A2 `        memset(handshake_str, 0x00, BUFSIZ);
/ T9 F9 m3 H- f! B        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"+ }9 C' c* t8 A$ y! C+ z# l' s# Z
                "Upgrade: WebSocket\r\n"+ h4 A- L7 }7 x! ?5 V9 L1 o9 o
                "Connection: Upgrade\r\n"
. E3 e+ w, h# M, |+ J! X" p                "Sec-WebSocket-Origin: %s\r\n" 6 O* h; R6 U+ K% l( i
                "Sec-WebSocket-Location: ws://%s%s\r\n"
6 Q; Q1 w! C* t                "Sec-WebSocket-Protocol: %s\r\n\r\n";- z" Z7 E, h% `% B5 r
        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
  \. s* T7 [5 F$ W3 d# D$ Z& C        free_handshake(hs); // 释放handshake指针,已经不用了!) \, }! l# Z# R/ B' k8 E. H
        char response[BUFSIZ];6 |& X/ w! i4 b1 z% B4 p" M
        memset(response,0,BUFSIZ);# `0 y* i( @; s3 y
        size_t j=0, handshake_len=strlen(handshake_str);, J8 z4 H7 N) J$ M& l
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];9 J7 M- V. l% B/ b3 `  a2 S
        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
# t( O5 Z+ B  z        // 这里的clientSocket就是连接好的socket对象了。
( F+ t' T1 j1 Q+ |, a/ ?8 V# _        int sent = send(clientSocket, response, strlen(response), 0);6 L& }1 B  k$ d
        return sent>0;
7 ?3 T  a* q* l" d- L}
! O! a: ^+ u. q8 L; g目录结构:
2 G0 C0 B/ n4 w3 O6 s4 G, k( u% Wsocket/& S* n1 p' o0 `9 ]( n
      socket.c" C: i. o) |, e3 P% A
      md5/
$ u, P' `5 b) D/ B" F; q        md5.h
5 M( m* a2 a5 c% n        md5.c; k1 X% ?  t  A: z% S* U
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
+ |- @$ o3 g* b1 E$ ?编译命令:  gcc socket.c md5/md5.c -o socket.o  + j$ Z0 D7 |5 l. y0 i# q8 ?
GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。3 e! s. M& Y. {
// socket.c 代码。
& N; h$ u1 m2 E! d) r: l. f// web-socket example
$ x, H9 z6 @& W/ f" f  c) N6 Z5 y, l#include <stdio.h>  T* @) q9 ]( N
#include <stdlib.h>
! d9 V$ u/ J6 V' N8 O- M#include <string.h>% u7 ^# i0 Z8 V- j, v8 x( ~
#include <inttypes.h>+ M0 I, L+ O( c( l9 z
#include "md5/md5.h"
; S" d8 p  G0 {% m4 X0 _; x, g$ A* ]7 E3 C
// #define BUFSIZ 512
+ S, k. N+ L/ A6 f. U3 V2 I' L8 O" b2 m& i" W' D! \
//定义handshake结构体变量
" O. d* I! L. O1 c' i( ]struct handshake {
8 m& {, Z2 x& y: i" t$ @) T        char *resource;# s* q# h, \$ B7 @3 ^9 L' {
        char *host;
. p" g- @! A" Z4 i9 X6 r        char *origin;
, `6 h# M! L1 N3 G) B4 U        char *protocol;
, F; L6 E" \$ \9 f% T* V        char *key1;6 u7 n# q- y" z2 A- X
        char *key2;
$ w3 V+ @) |$ |6 N, k* T" K};8 K$ {7 c: Z& K! _. o

7 M8 b$ T7 Y7 Q) U" J7 X8 b! f* d
//释放握手后不再使用的部分变量
9 ^/ l- P" s0 @# X9 fvoid free_handshake(struct handshake* hs){
# ?' U$ m$ i# s) Q9 k) m, k        if( hs->resource != NULL )      free(hs->resource);" u9 T8 b$ i5 B$ Z& l: ^
        if( hs->host != NULL )          free(hs->host);$ l+ g& E( p. `0 n+ j
        if( hs->origin != NULL )        free(hs->origin);4 L5 Q4 J; g. A; B
        if( hs->protocol != NULL )      free(hs->protocol);
' Z& k) r1 i$ z# R' T        if( hs->key1 != NULL )          free(hs->key1);
% |0 M' C; b' i5 w& }  u3 B9 F        if( hs->key2 != NULL )          free(hs->key2);3 P) S  C; z& J: P- C! Z5 Q
}# Z+ F! O0 E. X( p2 J: T; ?; F
- q0 [4 C7 Y7 x& V3 J7 {
// 这里对上一篇的match_string做了点修改,
8 `8 T% y  p$ a. k9 M' Z// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
$ Z# H1 ~7 j+ r9 E# V; _char* match_string(const char* src, const char* pattern, char end){
1 Q, C8 b/ W5 Y' ^        char buf[BUFSIZ];* c3 M. m2 R" J, `3 Q0 F/ |7 e! O; O
        memset(buf, 0, BUFSIZ);  F  l: ]# n/ O# z/ H4 ~; [
        size_t src_len = strlen(src);
; f% w$ O0 \% g8 t4 Q) z7 D        size_t ptn_len = strlen(pattern);
1 Q  @+ O! T# x        unsigned short b=0, p=0, i=0; 7 w& u+ G5 D( U5 i- C: S- t
        char c='\0';
9 f9 H* C9 Z+ r, M        for(i=0; i<src_len; i++){7 P' J" d4 I; [, Q, R
                c = src[i];2 N3 z+ j2 s8 S5 X& u
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中) `) e, N. s3 y! H$ F/ f
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束2 @. U* h; S9 d- ~
                        else buf[b++]=c; // 匹配到的字符
0 d7 p. \& `# n) {. O                }else if(p<ptn_len){ // 为达到匹配要求, D3 d$ t3 R) l$ {. ]! q
                        if(c==pattern[p]) p++;
1 i6 C# }; s/ k2 a8 C5 `                        else p=0;/ ?' G3 |1 C* i- e3 s
                }
& ^% z8 m. W# y  q& h0 G        }9 S% w; j" \9 |" T" H. _9 T) h5 U
        size_t ret_len = strlen(buf);0 c) A. X4 T* V6 @2 }& o0 r
        char *ret_p;
3 a2 M: h* O$ }6 c0 J( a- l        if( ret_len>0 ){
- r; `! e) q7 ~% W                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
& h5 }/ V1 o5 s' n3 D1 Z                memcpy(ret_p, buf, ret_len);( R: N; [" u# f9 ]; \* j
        }else ret_p = NULL;' m! J; u/ s# U/ R8 ?1 ~$ t
        return ret_p; 7 m: _4 z/ g% \3 c" P/ t
}
5 C/ k; B3 @& a' i" {  Y
2 l' W" l" m1 @# Q3 u// md5 加密函数,用的是网上一个实现的比较通用的版本。% S7 _$ Y/ I+ Z
void md5(const char* src, size_t size, char* digest)& y! c( w  x8 D: ?9 [
{: q$ A3 W' K! t
        md5_state_t state;
- c& ~- E8 Q/ v  e0 c8 O7 r5 l        md5_init(&state);+ g+ f2 E# [# S+ f
        md5_append(&state,  src, size);
" r8 j4 d3 z$ O9 b  i* t        md5_finish(&state, digest);+ t! E" @: A- d, m$ S
}
% o: r, u: L* r
% G* p& {9 v& h- E6 L$ Q5 s  }- F1 ?void p(char*s, int len){
: r: Y- w# S  K" w3 T        unsigned short i=0;/ K- L6 F# M& d  V; O1 C4 H
        for(i=0; i<len; i++) printf("%c",s[i]);
. R% D  Z/ L, ^        printf("%c", '\n');  Z& Y5 a% c+ B2 d
}4 Y/ ^5 o: R: d' b  i! n. G
+ v) _5 v- o, B% Z
void handshake(const char* src, struct handshake* hs){
2 F5 j7 }# C6 [0 j4 e. S: `! h# K        size_t src_len  = strlen(src), i = 0 ;$ v6 |! M6 m3 D( ~& {
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前  A( i- F, y3 l, ~' m/ u# W
        hs->host                = match_string(src, "Host: ", '\0');( m/ G% J" @# e" G
        hs->origin              = match_string(src, "Origin: ", '\0');: a8 l$ H! E$ Y3 W, Z3 F% B
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');: @0 s' L1 x* D8 A' j2 q
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');3 U2 W0 ?* m; }
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); % @5 f' d% S* D, o
        // 获取 key3,即最后的8位字符' `& T: Y0 Q9 T  V
        char key3[8]="\0";% J' \" E/ U: U5 L6 X+ ^. g) C
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
4 r- A# C. N. Y1 v$ ~( v! ~        char digits1[64]="\0", digits2[64]="\0", c='\0';
- J- b1 ~4 Q  k: k& e% {        size_t spaces1 = 0, spaces2 = 0;( w. c/ k$ N( r# F4 D, Z7 F
        size_t key1_len = strlen(hs->key1);- y1 r" p; \7 L; N* g! p
        size_t key2_len = strlen(hs->key2);8 p7 o6 }( e. z8 `0 c& N
        short d1 = 0, d2 = 0;
8 P9 i& O5 l+ G        unsigned int result1, result2;
$ R8 A8 c  V: n1 r9 f4 t, J        for (i = 0; i < key1_len; i++){
' C7 h' |1 B( d2 M6 @                c = hs->key1[i];8 e6 O# }% X  h5 |) Z" v
                if (c == 0x20) spaces1++;% v* [* N$ w& X7 [2 T
                else if(c>='0' && c<='9') digits1[d1++]=c;
6 W9 s$ _4 a& v/ @1 }' u" J        }0 z  B2 z$ k; k3 V6 d5 D2 D
        for (i = 0; i < key2_len; i++){
4 B5 t4 W3 ^. [& Y                c = hs->key2[i];* `3 G- d+ E# `  i4 |' n3 i
                if (c == 0x20) spaces2++;. X$ V- W8 m; c2 _
                else if(c>='0' && c<='9') digits2[d2++]=c; 9 m8 ^& l$ U% B! A% T, O& c
        }
7 f1 D$ `; @) N        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
+ F& V1 g# i. ?2 l        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);7 J$ a* M& y( |4 \8 w
        printf("ch1:%s\nch2:%s\n",digits1, digits2);: a' O, |( @! Z4 o) D
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);' T0 j- r- \1 |# C( K
        printf("d1:%d\nd2:%d\n"  ,result1, result2); & D7 b5 p. G! G# O' h1 ]$ B
        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
+ B0 S- g2 Q# M8 Y8 y        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
. ]2 X* x. ^! X5 ~& p* {0 s        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
0 @1 Y7 a" d: k% I        printf("ch-key1:"); p(chrkey1,4);
/ K5 E& ]9 E+ A& p% `% ~; E7 h- {        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);2 ^( \/ I5 K2 ]% p6 }) A( P
        printf("ch-key2:"); p(chrkey2,4);6 a8 r, g- b! P9 R& f
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
6 O* x/ e% d2 s1 U' u        unsigned char raw[16]="\0", dig[16]="\0";" K. J* x; f# R. o
        memcpy(raw, chrkey1, 4);
% D% o- f4 o! w1 G        memcpy(&raw[4], chrkey2, 4);
  D, P2 J3 f, m/ ^/ @0 l6 L7 W        memcpy(&raw[8], key3, 8);
6 o9 A; Z) [0 R9 `8 i0 ?# r        //计算的md5值) X( K* V8 I& M8 d
        printf("\nraw:");
. \' J# a# K3 y; b: U( b5 h, }& @: n7 }        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
9 a& N+ t0 s7 r" t' {        md5(raw, 16, dig);
4 ^, d: O$ j# `3 L        printf("\nmd5:");
  P2 Y* x6 d0 m, V, E3 G        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
0 G6 z7 k4 h" q% m}
1 Z7 V, n4 W* j& D% ^& j
* X8 N6 H  c/ \, Q8 n% W4 c) d% A3 ]6 |! C, ]( a: l0 F
int main()
8 Y- t# _. G- S5 V/ h* u' m0 b; u  a{& H: i0 n' z/ Z! S( f, i/ A8 c
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\
3 d# J  C! M4 h4 _0 T        Upgrade: WebSocket\r\n\' H$ Y6 n4 g$ [, m3 F' Z9 P
        Connection: Upgrade\r\n\
! C" R- h* `8 g/ M2 q5 x- A        Host: localhost:4400\r\n\
/ t: M7 l% T& q) ~/ x        Origin: null\r\n\& K# h; W- y. P3 D! p" C* s$ g
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
7 @/ u9 M+ m2 H4 N5 b1 _        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
% ?7 f7 x' D! t; S7 d. A        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";7 n6 D5 W& G9 [" [0 \5 h: o, R
        size_t len = strlen(msg);
$ H  o0 Q' h  A        msg[len]  =0x1f;
  w; x8 c  P9 X& @' K        msg[len+1]=0xf6;
/ b& m/ W4 R, c$ r; x+ u        msg[len+2]=0xf3;
, o/ L4 l% F- U. L* L        msg[len+3]=0x3f;) K5 z2 @9 C! N
        msg[len+4]=0xc7;/ }5 C5 l  ^! Z+ {0 E8 E
        msg[len+5]=0x17;
: J- F, {+ f  k0 ]; L; M1 l        msg[len+6]=0x20;
: E7 y6 V# ]2 i0 {        msg[len+7]=0x88;# \! w0 T* G& E$ Y7 l
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};+ E( o, k3 Y5 d9 f, @8 m
        handshake(msg, &hs);1 f& M  P/ h6 m- A- _' y: H
        free_handshake(&hs);9 R( @$ A& B8 i/ i9 J/ x1 x
        return 0; % x7 i# N+ {. _8 D% S4 ~
}; q! W+ h. ]2 h) b+ K
* @1 C* U/ A; W$ m' V& D

1 Q) m0 M+ U" v测试的结果:
  J1 X( u1 d" M' n+ w6 J  Vraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 8 b5 C; s0 s1 y2 D
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68
8 O% r( R0 ]- Y8 x- i* @对比了nodejs的版本,握手部分生成没有错误。
, }" l1 A8 A5 I9 t/ `6 K: c  _1 p( `
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-18 04:58 , Processed in 0.018414 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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