找回密码
 注册
搜索
查看: 5676|回复: 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++庞大的类库和模板。
" R9 C; l! v3 z& f2 s完整的windows版本socket握手实现:5 n# Y" N& |" T

/ r( q5 P8 ?# b  m4 u, n; D  Obool WebSocket::handshake(const char* src, struct handshake* hs){
. m  b, f' q( q- }, |        size_t src_len  = strlen(src), i = 0 ;
& N7 A) a: w' D) N# @        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
; c* F  Q6 |+ `1 _        hs->host                = match_string(src, "Host: ", '\0');" J, C* @3 ^1 {5 d' U8 x
        hs->origin              = match_string(src, "Origin: ", '\0');
7 W" E, m$ K6 r' K" o9 x9 e1 M        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
! D# m) h, G+ x( u; M3 L        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');* w& |. T- X1 y9 z! U# \- E
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); $ [/ I- J* `9 h- {9 j& p
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
+ u- g3 S# Y& i4 L7 q$ ^# N        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; ( ^1 q- U' R' F% o; _! A9 i
        char digits1[64]="\0", digits2[64]="\0", c='\0';
3 }: C7 h9 E2 x        size_t spaces1 = 0, spaces2 = 0;1 Q. U1 ]% G4 X/ Q* s1 d" q5 }! c
        size_t key1_len = strlen(hs->key1);& {, y% X% ]; k: R' {
        size_t key2_len = strlen(hs->key2);6 o% _" ~3 I+ Z( J0 P
        short d1 = 0, d2 = 0;
; N4 O5 O$ P3 d  D        unsigned int result1, result2;" Z, D! e8 N1 c: ?: {/ t  f: G
        for (i = 0; i < key1_len; i++){ + p. z. B' q0 G/ c8 x
                c = hs->key1[i];9 F9 X4 U( b/ C# J5 O! @: I+ I
                if (c == 0x20) spaces1++;$ G8 L% l: r  y' ?+ D' D
                else if(c>='0' && c<='9') digits1[d1++]=c; 3 I: r8 p" |' Q1 {# o; p
        }& V- Y/ b! t# o
        for (i = 0; i < key2_len; i++){
0 w6 D: `9 ~) N/ w# ?' h                c = hs->key2[i];: j) d4 g) x! K/ u; |/ p% t
                if (c == 0x20) spaces2++;
5 a  I* K# l( _, M                else if(c>='0' && c<='9') digits2[d2++]=c; 1 `! ~3 {' n. p8 {/ |
        }2 w$ n: q3 x' s( x& D3 |1 \& w
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
% ]4 m% U* h# @( R        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
' k7 O1 S3 Y/ N0 T  n        char chrkey1[4]="\0", chrkey2[4]="\0";8 I" |5 _1 t3 J2 t. w* a
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
6 F, }$ ~- [" d, F: Q* Y        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);8 ?3 c% B: b, P/ c. B
        unsigned char raw[16]="\0", dig[16]="\0";
6 l2 V& D: Z% Q, l. P+ c        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,. B. z+ Q3 Z5 K6 p; P. B
        //  连接上key2的最后连接上头信息中的最后8位字符。
; |. f* j) F+ n3 i) b5 C) c+ ?        memcpy(raw, chrkey1, 4);
5 @/ S$ [" y5 q* a0 t7 [        memcpy(&raw[4], chrkey2, 4);4 Q% q, j) B2 H1 o4 E
        memcpy(&raw[8], key3, 8);# n) I0 R2 o: x
        //计算的md5值& `/ |3 M) s! I% d
        md5_state_t state;6 I/ I2 Q; s& I: d% F0 k
        md5_init(&state);
( m$ g( F" x9 @* j7 `7 X% \        md5_append(&state, raw, 16);7 S4 c: \3 S4 Q7 Q. S  V1 r6 p, Z
        md5_finish(&state, dig);
. S* `: I' y' J+ z- J  u        8 @9 c) M# H8 C* J
        char handshake_str[BUFSIZ];  f6 H4 i" N# Z7 R0 r
        memset(handshake_str, 0x00, BUFSIZ);
7 Q$ A* `5 l- ^$ v, y9 I        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"% q  {0 K3 G* j8 L( ~! S
                "Upgrade: WebSocket\r\n"2 T" t. c$ }5 f" j
                "Connection: Upgrade\r\n"
2 K$ ?5 U  e; v; y* I: Y                "Sec-WebSocket-Origin: %s\r\n" 3 S& B/ @# M6 r0 R
                "Sec-WebSocket-Location: ws://%s%s\r\n"0 y% _' K; F) S. d) d$ _
                "Sec-WebSocket-Protocol: %s\r\n\r\n";
; H5 T+ a) I; b1 Q' q  C        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
: z# O2 j9 l1 I" ], d& C        free_handshake(hs); // 释放handshake指针,已经不用了!8 ~$ f2 q) w- m: K; |9 N
        char response[BUFSIZ];
( U6 ]  T' h4 U" Y/ }7 n        memset(response,0,BUFSIZ);2 f9 {" P: ]4 a# |* E7 m
        size_t j=0, handshake_len=strlen(handshake_str);
$ g7 y( |' d) l$ K/ C# E- s        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
  e+ E0 Z$ H' E( K5 m        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
! R$ S; L6 R% T- I& z' e/ O$ B9 q  W        // 这里的clientSocket就是连接好的socket对象了。
, w% h* ?: [# D# u        int sent = send(clientSocket, response, strlen(response), 0);2 D: G$ X, Y; L
        return sent>0;
) `# X$ L1 W9 }6 W4 F; ^( Q' G}
2 U: n# b+ ]6 [) E: p3 G; l目录结构:& Y1 ^, [) N% Q. @4 [
socket/1 `5 P* G. }3 {# c
      socket.c
% A0 r1 \, x5 U+ t      md5/) p- w6 g8 k1 C- p2 g$ x
        md5.h
, v( O. g; _& X; R+ T        md5.c1 V, c5 N# y6 V
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,5 N, t8 t3 [/ u% d: V" [
编译命令:  gcc socket.c md5/md5.c -o socket.o  
) u( D: c  V# N* Y5 X- e7 v GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
$ E- `+ w: A% f9 m. b9 l// socket.c 代码。
) Z. [- @: i; V$ G/ E// web-socket example $ a$ [' B" c4 r
#include <stdio.h>" a$ P0 R8 g# W  B3 j$ j4 `
#include <stdlib.h>; `. t4 A/ |. Y
#include <string.h>
" ^7 c9 Z0 u' G! v# p9 @' z+ f#include <inttypes.h>
/ G2 B% H" w/ J" X' K- ], g#include "md5/md5.h"/ i" u# q' K9 z6 r3 F
0 U! C/ a6 A& \' L2 n
// #define BUFSIZ 512
( \/ }5 f+ l  w% y$ M0 f6 _. V7 x5 ^  ~0 l. E. ?
//定义handshake结构体变量$ o1 b7 a" Y- J( ^" u6 L+ ?. w! s
struct handshake {
' {' I- k- s  T8 R# |2 q        char *resource;
: V: B0 a, n  A" P        char *host;* d9 i! S8 y' x% H0 _1 |
        char *origin;( f# A% H% l7 B6 N% K
        char *protocol;
4 D4 _7 h- F/ e0 P" H        char *key1;
8 c$ w6 e: f2 E! x4 ]/ e, s5 `        char *key2;1 c/ d* p8 n6 o: o
};
- u2 b$ \9 `) X% K& d
7 s; X  y& A0 v  _! \& ^+ D3 Y& V# @6 l. s
//释放握手后不再使用的部分变量6 l+ N) Y2 I5 F
void free_handshake(struct handshake* hs){
- W2 H$ K, v# p* P7 W$ O        if( hs->resource != NULL )      free(hs->resource);  H) t/ n* L8 c3 j( [
        if( hs->host != NULL )          free(hs->host);
/ F* a- M9 A5 y' t# \        if( hs->origin != NULL )        free(hs->origin);
- a+ G( v$ c/ n2 d5 |- y        if( hs->protocol != NULL )      free(hs->protocol);
( a' b$ o- u8 I6 @; R$ i, w        if( hs->key1 != NULL )          free(hs->key1);
* ^! A3 M+ @1 b* e9 h8 o        if( hs->key2 != NULL )          free(hs->key2);
" Z: N# u/ _4 P. C1 v" [/ N2 m}5 X" F) P$ u0 f9 C2 I
$ w) w, C( m( Y0 Z
// 这里对上一篇的match_string做了点修改,7 U& `* k6 h- j5 Y
// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
  R2 R5 k) l" p3 |( c) A; G3 tchar* match_string(const char* src, const char* pattern, char end){$ Q; w3 B5 M, @4 a# x; s
        char buf[BUFSIZ];
  f8 _  _; `" {* |) j! _3 B        memset(buf, 0, BUFSIZ);
2 O4 r8 K% H& g& N0 N: M        size_t src_len = strlen(src);
/ ~; ?; Q/ z9 R' r8 C, Y9 T        size_t ptn_len = strlen(pattern);
1 M0 x9 l5 Q8 b5 k        unsigned short b=0, p=0, i=0; & D; t5 z( ^2 Y) n! _) P
        char c='\0';
2 p  p% v4 T: @' v  Q% A        for(i=0; i<src_len; i++){3 f; F9 z/ p( q7 l; r' W
                c = src[i];
/ {9 B$ s! {, @4 N0 g, ]' K                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
1 Q* G. {8 y0 u7 y0 c3 ]                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
0 o- g3 l1 \! \! z) e) F! Q0 H                        else buf[b++]=c; // 匹配到的字符
* g) d6 p, _8 v- R+ D/ v                }else if(p<ptn_len){ // 为达到匹配要求5 T) R7 w  C7 |) f; n+ ^
                        if(c==pattern[p]) p++;
- e- J$ `; C8 i9 w                        else p=0;
# o0 M* x0 k% i/ a  ]- @: N                }
+ [4 s9 U1 X$ A/ H2 o9 d        }
4 `5 |* n# I; P  D1 N/ C        size_t ret_len = strlen(buf);
! V! Z3 S5 g3 }# Z% |        char *ret_p;
& C7 I" |1 @) F9 J        if( ret_len>0 ){
4 I1 P6 B* v. S4 f' N                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'8 b# `; R( k" a. r9 T% |" e
                memcpy(ret_p, buf, ret_len);
( [; e5 }2 N. A% `" t; _* e  e6 W        }else ret_p = NULL;4 e2 p' o2 L' s) F) V2 _" a
        return ret_p; $ S6 T- }' C1 P; i; Z( p# [
}  L' e# G. Y7 \

. r* w( k  u& A// md5 加密函数,用的是网上一个实现的比较通用的版本。
: D' l+ e- q0 t5 c% F( s7 Nvoid md5(const char* src, size_t size, char* digest)" O( T8 O' d( k
{4 g% L/ {8 W3 r# {) U; M
        md5_state_t state;) b5 _$ w, Z2 b1 ~: X% ~
        md5_init(&state);2 c& [, p' B# }9 V! q- S8 X3 y
        md5_append(&state,  src, size);
. S) b& Y( k4 F  ^# C        md5_finish(&state, digest);
, ^: V$ Z/ S: R8 f4 P8 B}% x2 c/ L( f( l

, M; S, ~) _( z( D4 z, gvoid p(char*s, int len){- Q" t8 C. K4 k7 U" \3 p& ]- Q% K
        unsigned short i=0;
% ]- R9 W! g" h5 h4 j9 Y" c! w4 T        for(i=0; i<len; i++) printf("%c",s[i]);
& N+ @' k; n3 z) ]+ _        printf("%c", '\n');8 k1 ?! d. o' ]. w; H+ l2 `
}
& m7 f; O( f: z& ]6 V# z) [5 G0 b
: i' ^8 G6 @" @3 v' X6 qvoid handshake(const char* src, struct handshake* hs){
# c- V# \3 V2 r* P        size_t src_len  = strlen(src), i = 0 ;
) ^9 O0 O) O; Z        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
- w% X! W. i; e& y6 S        hs->host                = match_string(src, "Host: ", '\0');
/ p, u9 }8 N" F        hs->origin              = match_string(src, "Origin: ", '\0');
/ n2 y1 `$ _$ F8 M; P        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');$ z3 P0 t) `1 h6 m0 P; l  y/ ]# {
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');  m/ {# O/ x- a9 M! p' U# M
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
1 }, h: n# k0 G+ A+ K! q        // 获取 key3,即最后的8位字符
( `( F; U/ ]8 H( g0 N        char key3[8]="\0";8 N/ E( h  @7 s
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
* o) k4 [" X. J) {, f! s. h) I' i        char digits1[64]="\0", digits2[64]="\0", c='\0';  l$ r! w. A6 N, D
        size_t spaces1 = 0, spaces2 = 0;7 J  ~6 n- c( q2 K
        size_t key1_len = strlen(hs->key1);3 H. x, Q0 ~8 B* H
        size_t key2_len = strlen(hs->key2);& ~+ n: H6 a  i4 y) v) G# E
        short d1 = 0, d2 = 0;
; a1 i+ u. X" {5 h        unsigned int result1, result2;
0 A' |3 |3 a3 Y. u: o6 `2 T! a        for (i = 0; i < key1_len; i++){ ; l! f. f) i9 ]( B3 Z
                c = hs->key1[i];" e2 w) p. c4 M7 @
                if (c == 0x20) spaces1++;
# j& J' k6 P2 F( g! E                else if(c>='0' && c<='9') digits1[d1++]=c; ; J% m: v! Q) x$ H2 @: X8 p
        }
2 F( b/ K1 \- \# _' f        for (i = 0; i < key2_len; i++){
: b% {# R2 d/ K5 o8 v/ ~                c = hs->key2[i];+ w8 e( n# P+ b& I# W4 x
                if (c == 0x20) spaces2++;
4 K; ]+ m( L7 h: d                else if(c>='0' && c<='9') digits2[d2++]=c; $ c' ]. C6 l# M0 _
        }0 z/ ?* D0 ^$ p9 `
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
9 S# Y1 ]1 \: H        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
: u% m( S9 r3 A+ p% o( y        printf("ch1:%s\nch2:%s\n",digits1, digits2);
- k$ ~4 @; ^! V9 [" j- P* c        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
( y5 n/ ^) m: H        printf("d1:%d\nd2:%d\n"  ,result1, result2);
) O5 H' |+ D' W" m# {) z        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";" v! q5 k6 h" \3 \: ?4 G4 S
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
5 W& Z9 t6 s+ d& ^        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);5 z! C: r& L- b! o
        printf("ch-key1:"); p(chrkey1,4);
* l% _- H7 @8 W1 G9 k        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
) ^) Q  V, g" q: R        printf("ch-key2:"); p(chrkey2,4);
6 A: {  f9 r0 _7 j1 q% Z' W/ i' e        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);  t  z  z8 m' `. y% _. k7 w7 F; g
        unsigned char raw[16]="\0", dig[16]="\0";
! ?# _$ r/ V2 c' k2 W        memcpy(raw, chrkey1, 4);; |9 P$ B& R1 F  O" F% \$ v
        memcpy(&raw[4], chrkey2, 4);, V1 n& m+ S7 W. M1 j
        memcpy(&raw[8], key3, 8);
3 o5 g* t0 d/ k8 s3 \+ i: n        //计算的md5值2 G( @  o& X4 L% i* f! i
        printf("\nraw:");$ y# Y4 ]0 x$ ]8 n- y
        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);; R1 R: m8 S( L0 T2 T4 \
        md5(raw, 16, dig);! j1 J# P+ b( ], P% L1 T
        printf("\nmd5:");
7 ]) L# Q9 D7 i/ H. J        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
$ f5 e1 l+ |7 Q$ i4 w; W( p' ^}
. H3 ?. y. N' q, w/ A2 i4 r
: E2 Z% y7 R  i: I3 K
/ e0 T( }$ Q, D( Aint main()# O; I2 Z* J0 B# u, f) z
{" m' @) M* j6 P  ]$ D1 p8 }) D. _
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\
; H. h5 R1 {7 D        Upgrade: WebSocket\r\n\
1 h. V9 f" C9 q        Connection: Upgrade\r\n\
& a! D% }  P$ ^        Host: localhost:4400\r\n\1 f  X7 I2 W( F1 R- `3 c( O
        Origin: null\r\n\
1 V  R( n, v; C$ h        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
/ U8 r& t" `0 o! s. B( f        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\% B+ ?  i# o6 A8 X
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";
0 a8 \  t$ P: r- i# C  ^& U) a' W        size_t len = strlen(msg);/ y' C; I# `; f
        msg[len]  =0x1f;
; A" N6 D( ^$ A' U8 t        msg[len+1]=0xf6;
# Q1 \# I& V  o/ L% {5 X& p& `0 z; }        msg[len+2]=0xf3;
# B) y& j5 @. e' E# V' C        msg[len+3]=0x3f;
7 Z1 f  ]8 m+ W; G. V8 B  s/ F% ^        msg[len+4]=0xc7;+ o/ h! @0 e6 P) R, ~6 D, L
        msg[len+5]=0x17;
. e$ l7 m9 F# g2 a& T9 p        msg[len+6]=0x20;8 @2 s& D4 p2 M1 q4 P
        msg[len+7]=0x88;3 g0 p( H5 q' _7 @2 J1 N! c: f
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
9 P2 B$ f* Y$ m7 I4 Z        handshake(msg, &hs);! M0 h) h& n1 M* v* z. Z8 Q
        free_handshake(&hs);
7 h2 ?8 V4 o. I' I, H) o# i        return 0; ) V/ k, n8 ~& N: `
}; I& ~- N( t; V# i
+ K3 [( X8 @4 h4 B) Z+ a( k

8 A' ?. x" c; ~测试的结果:
. E+ u, i# p5 v& Q9 Jraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 3 W, U" t- r0 U+ }$ ]1 W4 j. R7 m
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68
& j  N/ ?; ?! f( i; m, i" V对比了nodejs的版本,握手部分生成没有错误。( F2 f' {% A! k4 B3 k
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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