找回密码
 注册
搜索
查看: 5202|回复: 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++庞大的类库和模板。
5 l& {' a$ V3 [4 r完整的windows版本socket握手实现:
( ]! s8 M  K# o3 z+ l% U  F$ T. \7 h
bool WebSocket::handshake(const char* src, struct handshake* hs){
2 a/ ~0 P1 X' T/ P! E        size_t src_len  = strlen(src), i = 0 ;; X1 N9 \3 A( `$ G2 d5 ?1 r. Z
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
- D0 o* Q+ r4 _! G! E        hs->host                = match_string(src, "Host: ", '\0');6 g  U% y0 h) }" h
        hs->origin              = match_string(src, "Origin: ", '\0');; ~5 v2 T. B( ]. y
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
5 s* z# n% }8 }# Z  B# [; T  H        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
: ?; {( F( y5 u+ u        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
% C$ T% c+ p4 q# H- t0 s        char key3[8]="\0"; // 获取 key3,即最后的8位字符! t, I( n  m# t8 D) K! k  t
        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; 0 g3 G# [; Q6 y
        char digits1[64]="\0", digits2[64]="\0", c='\0';5 [' j5 @) s# T6 Y0 j( n
        size_t spaces1 = 0, spaces2 = 0;
" d1 J, f* Z; c  T# t* A        size_t key1_len = strlen(hs->key1);
" s' y: u7 U9 X( _% \, W; T        size_t key2_len = strlen(hs->key2);5 y# N- b1 Q, U
        short d1 = 0, d2 = 0;2 X! X3 R. _' h
        unsigned int result1, result2;
$ @! l+ K6 V. i        for (i = 0; i < key1_len; i++){
; a+ J0 }, K9 ]3 r                c = hs->key1[i];& h: G, a5 g! U4 ~. S: W3 w
                if (c == 0x20) spaces1++;9 r6 H1 Z1 V0 z6 a9 u* b
                else if(c>='0' && c<='9') digits1[d1++]=c; , D" |% q# \0 S( V8 d% w8 v0 W5 @9 Z
        }- W+ Z. ?6 v' B2 ~3 Q
        for (i = 0; i < key2_len; i++){
+ e- ?7 V+ ]8 }& N# ^5 c                c = hs->key2[i];5 E+ v# C5 r& q% |4 H; H0 A
                if (c == 0x20) spaces2++;
1 z1 j8 A  {, N4 [9 T; i/ L                else if(c>='0' && c<='9') digits2[d2++]=c;
6 a  h0 |4 F9 W7 q, j        }
$ G% Z  I2 d1 x0 g- V9 J6 T        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
. C& b. h& |/ _; `( B: F        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
( N9 O9 S9 s+ U        char chrkey1[4]="\0", chrkey2[4]="\0";
8 C5 i& v' N2 }) S7 P" l        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);9 ]+ f% A; ], J" h2 {( {2 Q
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);8 f5 [" _/ c) m- u: b" T" N4 f
        unsigned char raw[16]="\0", dig[16]="\0";
, T4 T3 C; J/ d+ N1 Y        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
* \( Q/ Q2 a& {: ]8 {7 c        //  连接上key2的最后连接上头信息中的最后8位字符。
+ \* e, D$ I8 n* y        memcpy(raw, chrkey1, 4);
5 Z' H5 T$ m3 }2 j, ^9 N! N        memcpy(&raw[4], chrkey2, 4);
" F# Z8 }- o: N) u. s, K( \: [0 F( j        memcpy(&raw[8], key3, 8);
, d7 q9 H4 I0 H4 @        //计算的md5值1 G6 n# E3 ~$ T; ?7 F5 O
        md5_state_t state;- b2 Y& u. v& F+ n" x# A
        md5_init(&state);
* w* s& k% \; ~& O% |        md5_append(&state, raw, 16);
0 n1 J. P2 K/ s# Y! _! |        md5_finish(&state, dig);
& o/ H" E3 S% P( M2 ~        6 F: @3 I; w) v& Q# _/ p
        char handshake_str[BUFSIZ];
  Y+ ~. N) e6 {5 A- Y$ t        memset(handshake_str, 0x00, BUFSIZ);
' C$ j; o  _2 v2 S: F& H        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"7 I$ Q# e5 L6 \4 H7 K' S
                "Upgrade: WebSocket\r\n"
+ ?1 O6 G: n, k0 V. e! ^                "Connection: Upgrade\r\n"
/ G/ T( P7 y. B                "Sec-WebSocket-Origin: %s\r\n" / w; k! T) \1 f" l1 e
                "Sec-WebSocket-Location: ws://%s%s\r\n": W1 `& s# d0 E! q( \
                "Sec-WebSocket-Protocol: %s\r\n\r\n";0 f( w$ \* Z0 O/ a3 I3 |) p
        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
& }/ g# {& Y! F        free_handshake(hs); // 释放handshake指针,已经不用了!. [& G7 _4 |$ z
        char response[BUFSIZ];
' p9 l) m* F& @8 N( I' A        memset(response,0,BUFSIZ);
" Y% c  J# q1 r; X: P( s1 ~        size_t j=0, handshake_len=strlen(handshake_str);% Z/ k+ \4 B, B" N- ]5 M
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
- N" p7 e; n, }2 i        for (j = 0; j < 16; i++, j++) response[i] = dig[j];5 ~* w  C  I1 w$ \& Z0 E: R  q8 R
        // 这里的clientSocket就是连接好的socket对象了。
; R! }5 `& j1 |; B: d4 d! p% M        int sent = send(clientSocket, response, strlen(response), 0);
2 B: C0 e$ M6 F/ z        return sent>0;
3 Z" m. s/ A% p  T. [( D5 P) C; b}: V2 a2 x8 e* `9 s1 T8 W
目录结构:! o5 `& {% D2 R: b
socket/4 c. o; d- R, j0 [7 `. J5 |
      socket.c) W, C1 ^- i- c/ B9 y& l
      md5/
& v% t4 S2 }+ V+ _4 p1 M        md5.h1 E9 z! k4 c) P" w0 _
        md5.c% l/ O6 J4 [  K5 R8 o& @
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
1 \% s) q5 C0 Z8 T) f# A9 ^编译命令:  gcc socket.c md5/md5.c -o socket.o  * M+ E. z4 k9 ^2 r! ^
GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
& @$ }$ V6 Q8 ^1 O7 _// socket.c 代码。
6 e  s4 \: E. {// web-socket example
; D, G8 g  i( a#include <stdio.h>+ ^2 ~) U0 C/ J/ T5 n
#include <stdlib.h>
- X8 H) }% C' `0 f* N+ X% H#include <string.h>
: f, N6 M8 y' a" U& Z) g; t#include <inttypes.h>* d" X/ l8 i$ q& K
#include "md5/md5.h"" Q) Y+ O/ w& E- j5 G+ }

3 a2 N8 ~2 P7 ~, K9 \$ j3 H// #define BUFSIZ 512- K$ G2 t8 y  l' j

' C& R- y" f+ |3 R8 H0 ]//定义handshake结构体变量6 V( k( G( g2 T/ s" c4 w9 q
struct handshake {
7 q. q2 R  l2 f3 u) R: |( G1 e        char *resource;' l# k, N8 U2 \+ L. H. M3 w7 Z
        char *host;
! N5 b6 o+ ~2 K, A, }9 t+ q        char *origin;
9 p) z* S! s* Y* x        char *protocol;' Q5 U4 f4 d# [" O
        char *key1;% q5 a1 T& Q' v- z. N+ P3 S! P  L
        char *key2;
% R( S- n- K" W; S" H9 f6 _};
, I1 `3 Z- D1 i9 ~" k% U5 g8 ^
+ h2 T  p$ S* J) l9 \: h/ V) t. e, U; |- c8 Q
//释放握手后不再使用的部分变量
2 T& l: H0 u# s5 c$ j0 jvoid free_handshake(struct handshake* hs){$ o) r6 x4 W! h+ V8 u  c: F
        if( hs->resource != NULL )      free(hs->resource);
% a% e& Y0 P" l. s6 d* q1 C4 _        if( hs->host != NULL )          free(hs->host);; q, P* m( M9 h8 q/ @
        if( hs->origin != NULL )        free(hs->origin);0 D. m# Q: C# E* s( S9 _( o9 r
        if( hs->protocol != NULL )      free(hs->protocol);, [& T/ |5 j, i; z/ g
        if( hs->key1 != NULL )          free(hs->key1);# ]/ [3 ?4 ~, }7 @4 `9 W
        if( hs->key2 != NULL )          free(hs->key2);
( h9 P; y: a+ T0 a  M}# @% I/ n: l" @% n
/ k; n( G! V7 t# Y
// 这里对上一篇的match_string做了点修改,$ j4 V- j' ~# A8 `3 @" x/ P
// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理0 s- t1 e' g2 T
char* match_string(const char* src, const char* pattern, char end){) m2 n8 R, N7 V" y
        char buf[BUFSIZ];
# V& v0 q. i' i( M! q; ?. F, v" i        memset(buf, 0, BUFSIZ);! p' Y' Q) @, V4 b
        size_t src_len = strlen(src); 6 l( e- L- W) g% l) t, X
        size_t ptn_len = strlen(pattern);. b- X0 Q& H% @( F4 O
        unsigned short b=0, p=0, i=0; / X' Z0 I2 e  n0 g
        char c='\0';+ i/ L3 e; w% b" D" @$ x
        for(i=0; i<src_len; i++){
0 [; N3 W- L# M$ m7 h                c = src[i];- M8 E  h  r7 Q0 m4 x8 _
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
( k& h! y; t" R, |9 b9 |                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束: b7 d0 e8 |" M9 D0 L( v) n$ w
                        else buf[b++]=c; // 匹配到的字符
; y0 p; L8 A5 M                }else if(p<ptn_len){ // 为达到匹配要求5 l7 k% S' G0 F/ r
                        if(c==pattern[p]) p++;% Z' ?" K' s/ P" Y; V+ V
                        else p=0;* {( D3 }6 L7 A; |9 U0 V
                }: }/ N1 ~) j& E5 ?% R0 R$ S6 i5 c2 v
        }
& j2 H( e% i! e: f        size_t ret_len = strlen(buf);
' E, c+ j3 _1 I        char *ret_p; % t3 h* Q& u. k# o0 Z
        if( ret_len>0 ){
5 [9 K$ K% e. `+ M                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'; g$ s$ T. H# ~4 g, m4 z9 H
                memcpy(ret_p, buf, ret_len);5 [: F6 i4 {( @: x
        }else ret_p = NULL;/ }& W- u& x: I0 |7 x4 {, ^
        return ret_p; 5 u2 Y  z- f$ C7 |
}
2 C3 A. P+ `3 Y* C$ A( Q' S, C4 \/ I( Z+ L% U
// md5 加密函数,用的是网上一个实现的比较通用的版本。
% G, I) v0 i: tvoid md5(const char* src, size_t size, char* digest)* B0 t( h/ b& u( g: I6 n! S
{
/ {9 F- p0 {( O" e) M        md5_state_t state;
( Y  [) N. E% y- y8 s; f        md5_init(&state);- @4 w1 w  \& V0 F/ V1 j
        md5_append(&state,  src, size);' i2 j7 m9 `! V' w
        md5_finish(&state, digest);
3 f, }+ z  C5 E5 F/ X}
% {/ }( s/ J4 V9 O8 V( ?# L2 P
1 R5 }# ~8 i6 n* Kvoid p(char*s, int len){' o& h8 U( D. l3 d6 {+ e4 _2 C
        unsigned short i=0;
4 u6 Y! ~- p8 N8 f4 C        for(i=0; i<len; i++) printf("%c",s[i]);$ e/ w+ j; Q" ?# P6 U
        printf("%c", '\n');
  ?) g# P! F6 T& k/ O}0 f$ f4 ~; t+ d! y- e7 F
! L3 T( N" h0 I5 ?
void handshake(const char* src, struct handshake* hs){  b: W2 H8 v: S. }- E
        size_t src_len  = strlen(src), i = 0 ;9 ^4 q, O3 L9 |, l1 ^+ z  B
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
* p+ ^7 e* o- p0 x6 \* G        hs->host                = match_string(src, "Host: ", '\0');
% ~% p' P" w" d3 H4 G% H( N0 A        hs->origin              = match_string(src, "Origin: ", '\0');! z0 f1 l( X& z7 S  e3 r
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');5 N2 _; C( a" p+ ], Q; i+ b
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');5 g3 I; x$ L$ K& {
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); 5 H8 Y% D& ~/ J) S3 q+ M/ X- w
        // 获取 key3,即最后的8位字符1 [/ t. y; P' K. p
        char key3[8]="\0";
& r& ]' x' D" J0 h7 A; v2 ~        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i]; & h* M  z$ _. G3 o& m
        char digits1[64]="\0", digits2[64]="\0", c='\0';8 T0 K7 b! C- P: _! @
        size_t spaces1 = 0, spaces2 = 0;( p) f) y5 z. R  q  a: Q
        size_t key1_len = strlen(hs->key1);  m' i  @" H, f  i
        size_t key2_len = strlen(hs->key2);* ?3 d; G& E0 p* g
        short d1 = 0, d2 = 0;
+ [' D) ~4 O) C1 P        unsigned int result1, result2;4 y* e0 }4 H$ g' }& l6 i9 n
        for (i = 0; i < key1_len; i++){
5 R0 Y0 f& Y+ q' U" s. @  c                c = hs->key1[i];
- X6 ~% K/ t6 k' M                if (c == 0x20) spaces1++;) Z7 U" {8 J+ K0 u
                else if(c>='0' && c<='9') digits1[d1++]=c;
' K) v. H/ h/ I5 ^! g        }
2 r/ M  {! o" U' K6 `1 s1 x7 `# m8 F8 b        for (i = 0; i < key2_len; i++){
7 \9 L/ F% W; T  h7 Z# F                c = hs->key2[i];9 h* t. {0 h# E1 P8 m* ]: N- }8 b
                if (c == 0x20) spaces2++;7 m7 V" q# W8 z: d# |4 ~+ w1 s
                else if(c>='0' && c<='9') digits2[d2++]=c;
- M  N0 |* }2 O- z        }! l/ ~9 s2 P( f, r" o, Y
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);. b/ Y! @2 B9 e2 u! h2 K# U! ]
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);9 o4 z0 n+ H" B8 e
        printf("ch1:%s\nch2:%s\n",digits1, digits2);0 O& \7 B2 M0 K, Q7 \# M4 m. H
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);6 M5 m- Q0 j3 o. p3 H$ E9 B4 L
        printf("d1:%d\nd2:%d\n"  ,result1, result2);
. p1 h2 f. O! C! F        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
& Z( l7 G0 I3 Z/ B% ~        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);" }+ F% @5 h% K. K& \9 M9 n5 t4 x
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
; S' y) Z1 G* F8 ~( U: w        printf("ch-key1:"); p(chrkey1,4);  i' }" K' H* N8 t' D; {
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
) ~1 y9 _9 H- \" `* w7 y/ X+ T        printf("ch-key2:"); p(chrkey2,4);
9 w. I( Q! q3 F1 w- I        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
5 @1 @2 a" ~2 X  v. J" B        unsigned char raw[16]="\0", dig[16]="\0";( {* F0 C+ F$ V" L5 b3 F
        memcpy(raw, chrkey1, 4);7 P- J2 e2 k0 F" t
        memcpy(&raw[4], chrkey2, 4);
! ~  K3 r: P5 ]& \  f6 q        memcpy(&raw[8], key3, 8);, C. k8 b' |( Y6 l  {+ P3 P
        //计算的md5值
+ d! f& u* |$ C  t' f8 E        printf("\nraw:");
/ ?; L1 B8 b! F        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
' I0 l' [6 l6 U% d9 e- J* N        md5(raw, 16, dig);
# P$ q/ C, j# B" r) ^        printf("\nmd5:");% L1 p! X. e  J. n; v; g; w0 w+ {
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);3 _* v; B4 r# f; o
}+ z- Y- O# P4 n7 ^) V. ^) o% d' E

3 E; C5 a* C9 l: v- `% I+ M% h+ x8 {; J8 S* p# C
int main()- D- `6 \) Q6 {9 p! z' U
{
* T8 N3 R/ ^& H: b1 f  R        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\
' ?! ~( J7 B1 o9 j0 D  M7 ?$ p        Upgrade: WebSocket\r\n\$ t1 n' m( F! D7 c
        Connection: Upgrade\r\n\% v4 y& f. q& C: ?- n  {" q/ A
        Host: localhost:4400\r\n\
3 _) y3 n; c4 b5 ~' E        Origin: null\r\n\  Z4 ]# X( w, E  n4 g& ~# Y: Y( b
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
% m  |! V5 Z$ B$ E1 n% f        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\* `4 Y9 T$ M- O4 @6 {; E- Y
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";
6 E# x5 _6 N$ I( e+ ?        size_t len = strlen(msg);
6 ]' v+ I/ `$ O; j# Y: B4 p: T        msg[len]  =0x1f;1 O- h5 N2 Y+ w: i3 @8 J
        msg[len+1]=0xf6;
+ M2 M2 N! ]6 K2 ]  @/ K        msg[len+2]=0xf3;# B1 T# G. Z% n9 C, e% f
        msg[len+3]=0x3f;
7 r; K5 s$ `, R, q% P6 d9 b        msg[len+4]=0xc7;
2 R( S( i8 L5 b+ t& l2 }        msg[len+5]=0x17;
0 S/ X" N7 A  n: c& j( w/ D4 E        msg[len+6]=0x20;2 V! M8 ], |' K1 ]
        msg[len+7]=0x88;
& i1 V$ m2 ]) z1 S  U3 x; P8 i        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
# t! k& A1 q! p  U$ C0 N        handshake(msg, &hs);
& l  u" C  e. ~# q: }        free_handshake(&hs);
! N2 u( C. [1 |0 K/ }        return 0;
4 L) W  h- t% d$ u( l$ K+ \}" }) J( d0 v+ K3 q& R
4 T) L& I$ a# L9 k

0 ~, `: L5 o$ t# r# s! a2 t2 U) P测试的结果:2 J- M4 B) E: E: C, v! R' P& D# a
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 ; ~: c( Y9 @* f! y" z4 |2 }
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 . e( ^1 S! [8 d) W
对比了nodejs的版本,握手部分生成没有错误。
8 ~3 f8 d4 E& Z. q- f$ R
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-9-30 12:38 , Processed in 0.042420 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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