找回密码
 注册
搜索
查看: 5677|回复: 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++庞大的类库和模板。
% b( Q* O$ v2 k# o  g$ l完整的windows版本socket握手实现:# P6 }+ z$ M/ x5 C1 t) J" d

- X& H6 K& W) tbool WebSocket::handshake(const char* src, struct handshake* hs){
5 d+ ?9 t0 L! X2 \0 d, l+ e        size_t src_len  = strlen(src), i = 0 ;" z! m4 e* s( W5 ^  l/ K" ~
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
$ X( E: \7 Y3 J        hs->host                = match_string(src, "Host: ", '\0');
" v( D& R0 O2 y0 k8 L# z2 Z9 U        hs->origin              = match_string(src, "Origin: ", '\0');
+ u0 z, Q# u) w- O) ]4 b$ r        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
+ `# T/ w% ~2 H        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');- D+ S- w) v0 d. B
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
0 P0 {- m, a. t3 {* ?        char key3[8]="\0"; // 获取 key3,即最后的8位字符9 K+ a4 j8 ?' b6 K
        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
" R6 H0 u, _5 `# F; m        char digits1[64]="\0", digits2[64]="\0", c='\0';: O* y* E7 c5 v: t
        size_t spaces1 = 0, spaces2 = 0;
) q0 L" j! V+ Q+ o        size_t key1_len = strlen(hs->key1);
% [$ E* e: ~. B$ I1 o6 u* q, J/ N        size_t key2_len = strlen(hs->key2);; r% z7 [. d/ I) l2 i3 b
        short d1 = 0, d2 = 0;- P- s/ V6 }) z) D  G
        unsigned int result1, result2;
  L! i/ O' r$ k        for (i = 0; i < key1_len; i++){ & f/ v/ ~3 P1 C! I" P: }
                c = hs->key1[i];
2 _& M& o7 s$ |& V. \                if (c == 0x20) spaces1++;& R6 z* X$ z  ^/ |0 X! g
                else if(c>='0' && c<='9') digits1[d1++]=c; 1 f1 Z1 l8 c3 q7 w( w* k7 U* N
        }! V7 L. V4 m: k6 L9 U
        for (i = 0; i < key2_len; i++){ 3 k9 f) E1 q0 i+ p$ c
                c = hs->key2[i];
) t8 D9 y! Z, S0 `                if (c == 0x20) spaces2++;
' t; t; ]; O- _, ]# @3 x6 z                else if(c>='0' && c<='9') digits2[d2++]=c; 2 q. ?3 T) G9 z5 Y
        }
& d2 y- z9 B; m( C* \! U& @        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
% E8 O3 C5 W, N* g6 b        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
& T# t, b: ~- j# E0 B4 p2 n, V6 I        char chrkey1[4]="\0", chrkey2[4]="\0";5 ~2 B1 \: Z% W9 l, I
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);! T8 e+ ]$ v& F" T$ t% V; H
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
" t% q+ P. N, F- r, |        unsigned char raw[16]="\0", dig[16]="\0";; v1 G  z, B) W; R6 e& }
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
- [8 m! O2 X5 d* q7 d2 N& C) H$ C        //  连接上key2的最后连接上头信息中的最后8位字符。0 D% a$ b) M( Z0 S- g
        memcpy(raw, chrkey1, 4);8 n% ?: }( z0 ^! @1 i- ?. ~! S
        memcpy(&raw[4], chrkey2, 4);
' B) P( c/ J/ c2 x; ]" s        memcpy(&raw[8], key3, 8);3 Y. O. R+ [  V, L
        //计算的md5值
; U: e) N* A6 q* g; }' G        md5_state_t state;
' c) l* d- c/ [2 s: ^9 v5 M  }$ u        md5_init(&state);
: \+ `$ l: j8 u7 S        md5_append(&state, raw, 16);" G  u$ @* y! D1 Z
        md5_finish(&state, dig);+ k0 T( l0 u+ E4 Z) l7 P5 W: b6 a
        ; C& t! a' E5 u" [3 v
        char handshake_str[BUFSIZ];' A% ~* g. ^/ p/ X
        memset(handshake_str, 0x00, BUFSIZ);
4 ]5 v# x: n$ k; h+ W" f        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"5 d, a) O9 K/ N, n8 J6 N
                "Upgrade: WebSocket\r\n"
' o* N+ D8 Q- o5 n9 i                "Connection: Upgrade\r\n"
2 U5 n' x5 l, a4 ~- m* R                "Sec-WebSocket-Origin: %s\r\n"
2 f' D! |1 o( w% ~- ~                "Sec-WebSocket-Location: ws://%s%s\r\n"
- t0 E+ ?) Z7 K% `+ u" N7 ~                "Sec-WebSocket-Protocol: %s\r\n\r\n";
& o/ ?' N: A1 \1 w9 I2 B( ^        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);; j# V7 H7 l8 S: E$ m
        free_handshake(hs); // 释放handshake指针,已经不用了!
! R$ ?3 i4 i, P3 k        char response[BUFSIZ];( {( Q! z, z9 x  E8 B& r" q/ c
        memset(response,0,BUFSIZ);
$ b8 L6 h$ u# P: z# m# w& w        size_t j=0, handshake_len=strlen(handshake_str);# v& k/ ]0 H9 H2 `  M( J9 ^
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
% z" L4 K8 w: F7 {' |        for (j = 0; j < 16; i++, j++) response[i] = dig[j];& U0 h4 n: W! H# T5 l6 S( c) n
        // 这里的clientSocket就是连接好的socket对象了。. {: u; K6 k: u% y" b
        int sent = send(clientSocket, response, strlen(response), 0);& \) g, c& N" n
        return sent>0;
" n, R  T% K7 ~1 ^- h}
+ x9 c9 j2 p; ^+ v$ l3 ^! U目录结构:/ U8 [- J: d* I
socket/3 L! u) m. K' v9 C& y
      socket.c# O/ \4 Q) h7 q$ f. R
      md5/0 [4 u+ A. s! j
        md5.h
0 e4 X7 x$ S, u0 q( [        md5.c/ X5 M2 V. F; F5 ^" F5 T! {
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,, A; W/ Q( @9 v! I4 S6 o9 r9 W
编译命令:  gcc socket.c md5/md5.c -o socket.o  
+ g' a3 h1 c: {$ ~- X4 E! ] GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
/ q0 N% K. k2 Q4 l" e3 {6 p5 l1 _1 L5 I8 R8 B// socket.c 代码。
2 O! W* Q( X1 L9 W  \// web-socket example ) C# P4 n$ I) f- T5 L9 ?
#include <stdio.h>
  X! ^' D0 s7 Y3 K# Q+ Y#include <stdlib.h>
& H  u( E' Z/ v3 n* J#include <string.h>
4 }9 ^0 ^5 `  |#include <inttypes.h>
4 l6 i) c3 g8 d#include "md5/md5.h"0 a, r/ M. y4 N# I& H# W2 r* M5 i

/ M1 F2 U6 \! h0 ]  K0 `, a9 x0 h// #define BUFSIZ 512+ G1 B# P: F# K4 W  U. k/ u9 I3 m, a9 w
/ N, w* f' Y% h' [
//定义handshake结构体变量3 R. P0 ]+ F( a8 B3 T& u
struct handshake {
4 Y' G2 G! e% _8 j, V. r5 P5 H5 e        char *resource;
( w8 e) n* q1 @9 D        char *host;
% u7 |, J& F' \        char *origin;+ J$ h. g, B6 |& }
        char *protocol;5 c$ n! L: n; D, D5 K
        char *key1;" y9 Y4 P2 ^+ k4 Z! U
        char *key2;
/ U* D6 T" S1 W) G$ s4 Q! X; ]};
3 F" b% N  N. c7 I9 d+ G, f7 W+ j% j# a5 C" u9 \, g

* r. I5 k0 S: H* n//释放握手后不再使用的部分变量
7 Q  R6 Z; E7 K& f" p  `# W7 \6 M! uvoid free_handshake(struct handshake* hs){
! x8 P# N1 W8 U) d        if( hs->resource != NULL )      free(hs->resource);
8 X7 ~- V* t# i* Y        if( hs->host != NULL )          free(hs->host);% K0 j5 A4 w; _2 W" `
        if( hs->origin != NULL )        free(hs->origin);
! ~# W$ M  O+ Y5 E" a        if( hs->protocol != NULL )      free(hs->protocol);' z* \; D1 w, m. _1 C0 E
        if( hs->key1 != NULL )          free(hs->key1);
, z! {5 B6 F, P1 @  l1 R) M        if( hs->key2 != NULL )          free(hs->key2);: O8 P, ?; _* w- ?; @: Y; N0 }
}
) u& q* Q. ~4 y) z/ F! a8 ^/ z9 g1 t! W' a& Y8 I
// 这里对上一篇的match_string做了点修改,
0 s& u7 _* ?0 @' h// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
4 B2 J4 i0 r- I3 pchar* match_string(const char* src, const char* pattern, char end){/ \! n7 B6 W$ [5 J* ~
        char buf[BUFSIZ];
( i2 m1 v# @) N6 ^( A        memset(buf, 0, BUFSIZ);2 |4 p. b7 {2 a- j
        size_t src_len = strlen(src); % U# g, u1 v3 ]6 k
        size_t ptn_len = strlen(pattern);2 \) N5 B+ M: N* p) J% M' M
        unsigned short b=0, p=0, i=0;
5 D, i, V& K- F2 X) g        char c='\0';
& t% o7 H" d. o        for(i=0; i<src_len; i++){
/ v! E. G' h# Y8 m% e( E                c = src[i];! m2 ]7 C. ~% R8 K. m' b% o7 I
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中0 J+ J7 ?4 H8 H, D6 u9 k
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
" \3 l) n( H& b6 _* j, Q9 a, z% U$ B) I  L                        else buf[b++]=c; // 匹配到的字符
- g$ W5 P( \( T  V. z                }else if(p<ptn_len){ // 为达到匹配要求
9 J) H& R! w0 t8 F                        if(c==pattern[p]) p++;+ d9 V( U6 I  g5 f, G0 |
                        else p=0;
! f( W+ N' S) w% e/ C                }" `  J5 U3 k' a$ b
        }6 |, W& T3 Y7 e4 C/ ~- w
        size_t ret_len = strlen(buf);
6 r8 K" H2 w; h! h+ K- [" w        char *ret_p; 1 [8 x4 P- c% ]4 A8 I
        if( ret_len>0 ){( u9 {* m' v9 v  P" c6 c
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'- U& Q6 |* E  k/ X' J+ n* v3 S
                memcpy(ret_p, buf, ret_len);, e- p) L& v9 v! Y
        }else ret_p = NULL;
2 B6 C. _6 ?, F+ T+ a& R  k        return ret_p; 6 Y% D# m& ^# ]' K* ]/ n
}+ ^" f  \* Q+ Z5 f; p/ C! h8 j

, T: |3 i" w9 ^( y+ V// md5 加密函数,用的是网上一个实现的比较通用的版本。
  f. }6 _( K  Y8 ^void md5(const char* src, size_t size, char* digest)
; u" e. ~# J5 o- p! p  Y{3 M6 u  K' o0 Z* A" f
        md5_state_t state;
$ ~3 G9 n. |: m/ ]! P5 _        md5_init(&state);
9 Y6 }  g' T8 c  r+ c. N1 N, H5 D        md5_append(&state,  src, size);$ H( K' q2 F2 r# F2 w# ^5 k' t
        md5_finish(&state, digest);. M, R' t- b" M2 r
}' z6 A& O3 m% i. g4 W  S
7 H8 X3 K5 I1 @3 t1 m- e
void p(char*s, int len){
& o, ~3 L+ d% D8 w        unsigned short i=0;9 t2 O: \* ?+ t" `/ i# J
        for(i=0; i<len; i++) printf("%c",s[i]);
* h: m8 s* d! ^, Z, U! S$ M; `1 m        printf("%c", '\n');" T8 M" _8 [( F7 u
}7 W  l+ r8 `" Z3 w3 J# P, H
6 w6 V& w( c5 i; f; y
void handshake(const char* src, struct handshake* hs){* D6 c1 y' {. j" E& T* w' ]
        size_t src_len  = strlen(src), i = 0 ;2 B4 R$ ]' j2 P# ]3 \" F# ~
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前8 A% C- [& T! O7 L2 A, z$ G
        hs->host                = match_string(src, "Host: ", '\0');0 ]" W) z: N, t. J( c
        hs->origin              = match_string(src, "Origin: ", '\0');2 A; f! P8 h+ f% l
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
( O# V6 D1 q; [) l( b7 G        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
' \" T$ i- V- o8 X+ t        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
/ k4 Q: l* g" d* x5 H0 U# e        // 获取 key3,即最后的8位字符
+ {/ N3 ?2 _/ D: F/ ~5 h9 R4 ?        char key3[8]="\0";9 G( [$ x+ E( @; E* F
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
7 H$ i" @' V6 o" d% O& Z        char digits1[64]="\0", digits2[64]="\0", c='\0';$ m7 @$ U. F" G! L) q2 ?! `4 w3 F/ r
        size_t spaces1 = 0, spaces2 = 0;% P; d( i# \/ n) Y/ R. H2 h- k
        size_t key1_len = strlen(hs->key1);
3 g; {0 ^4 l( ~+ t0 |9 g        size_t key2_len = strlen(hs->key2);
# Y1 T% }4 A! X7 \        short d1 = 0, d2 = 0;
) e+ {3 H8 j/ p6 t# L8 l        unsigned int result1, result2;' f8 Z1 O2 d  a+ H5 S9 u. _# N
        for (i = 0; i < key1_len; i++){
7 a; ]' |) L' B' M! R                c = hs->key1[i];
3 s% p/ s# w9 N( l7 G, I                if (c == 0x20) spaces1++;& s% g5 t. z9 z  H
                else if(c>='0' && c<='9') digits1[d1++]=c;
& d6 L5 F! m0 n  `/ j1 ?        }
$ F4 M- A2 _- s8 |" H& U6 z" O        for (i = 0; i < key2_len; i++){ + O( {8 R8 [# ~9 k+ v6 d
                c = hs->key2[i];" p/ p. t7 j9 T7 j3 j4 z
                if (c == 0x20) spaces2++;
$ J% L& f# W" u9 w6 J( O: V  o                else if(c>='0' && c<='9') digits2[d2++]=c; ' Y/ x& M5 _# y$ U2 z
        }& k' r3 J1 p$ V. i1 W; U
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);. j' z! g# z5 F
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);, w. n8 x3 S; p! r* ?& I
        printf("ch1:%s\nch2:%s\n",digits1, digits2);
6 B8 p1 k6 o( |+ ?+ M( [+ d7 d        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
! N' h# o$ p# f% G        printf("d1:%d\nd2:%d\n"  ,result1, result2);
$ m2 u" X, s( d6 ~2 c7 u        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";: R7 {, W+ s5 f- O
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
8 Q) H! E1 c( ]4 U/ W        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);" }" [( s6 |% W7 ]2 T* v3 `. k
        printf("ch-key1:"); p(chrkey1,4);
3 o& y2 O/ n2 @  a        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
0 B/ O+ i' B% ^9 G% G5 ^        printf("ch-key2:"); p(chrkey2,4);
' {% A9 a5 S$ |, n: W- n* K        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
4 L9 t( L- a0 v        unsigned char raw[16]="\0", dig[16]="\0";, A3 y) U2 r  n% f: v) |
        memcpy(raw, chrkey1, 4);
) f7 _* l4 e# u7 f& v/ @        memcpy(&raw[4], chrkey2, 4);! {( Y! D, N/ s$ P$ `) v6 w
        memcpy(&raw[8], key3, 8);. U8 s# @- z& Q/ ?9 a' H
        //计算的md5值6 `8 v* j8 W/ j4 i
        printf("\nraw:");( |3 L9 G$ m" t5 ~7 P$ R7 q
        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);8 B7 P$ M1 C; f0 ?" c5 {$ \
        md5(raw, 16, dig);( w; [6 C" h/ h& ?
        printf("\nmd5:");/ n1 m+ \+ I9 V; ]1 i
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);( P% ~. R9 H( W5 b
}8 }, h0 q. K' z* U& @/ J/ M

4 z* X' T* U6 y- L5 H
" h% |# k  q3 {4 ?+ P0 |- d, qint main()
7 g- q4 a$ {$ Y1 ?  K{
# p$ g' \. r$ x, k- S  c        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\0 b" j: s( x! w+ T+ b
        Upgrade: WebSocket\r\n\
" p! s& [8 h, I6 _* h        Connection: Upgrade\r\n\
' X3 y0 k, k# ]" ^1 t5 U        Host: localhost:4400\r\n\
+ @' e. F! p9 p6 B) O! u& w( J/ n        Origin: null\r\n\: m( W- d' g7 I, j  C
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\" P% O- d& B  |6 ~; |+ g3 M  O
        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
( \& x/ @! o( l  l        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";2 b8 X# h5 k6 E" ?4 e! r
        size_t len = strlen(msg);
8 M% U# I1 |, w: Q' z        msg[len]  =0x1f;0 g" r0 F" n) ^) ]% w
        msg[len+1]=0xf6;4 [' [" x# g" J8 w  A; _- v% E
        msg[len+2]=0xf3;
- x* f& w1 I+ Q6 S        msg[len+3]=0x3f;
3 L$ d8 {/ b) o. g: ~        msg[len+4]=0xc7;
, S5 `1 T- |$ V1 q% Q% @$ {6 C        msg[len+5]=0x17;- u& G2 J# y) m. _4 T
        msg[len+6]=0x20;
+ i9 [) W& {% F5 v6 ^! b        msg[len+7]=0x88;4 X3 Q$ E( \8 u
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
8 v$ g, q1 t+ D% M0 v$ ^: [        handshake(msg, &hs);! D9 r$ t; i" g1 Z
        free_handshake(&hs);6 d& z- Q" O8 ^+ W! n
        return 0;
# A, @$ e9 h( T6 `( |: X( q}
* p) q0 o  z* d; M4 D
* }' Q* C+ y" C" g6 K( r5 \7 u" |8 u% O8 b4 J! ~/ Y
测试的结果:
0 B: u: t: U3 [, ~/ Iraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 - p9 }. t1 |$ A) |, j5 r. a
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 & b5 X5 n- d" l$ D# @4 ~0 V% j
对比了nodejs的版本,握手部分生成没有错误。
! o: }; l6 I, m; {% l
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-18 06:47 , Processed in 0.017373 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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