找回密码
 注册
搜索
查看: 5350|回复: 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++庞大的类库和模板。
! T4 E9 a4 `5 n5 r3 H8 g4 g4 M+ z完整的windows版本socket握手实现:+ b/ e4 e* e/ t; z% N8 A2 \

1 K. O4 s( [4 ?) K8 K; xbool WebSocket::handshake(const char* src, struct handshake* hs){
0 y# A2 b* P  Z/ k! y! d' I        size_t src_len  = strlen(src), i = 0 ;2 a' f! u7 I- r9 D
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
0 {! z% |+ B; S7 N8 v% M        hs->host                = match_string(src, "Host: ", '\0');
6 J8 Z0 F' x7 }. A9 T- _  q' Z0 r        hs->origin              = match_string(src, "Origin: ", '\0');3 C/ ?, V# m. x$ k5 v5 c- b, @7 M
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');: O" d+ z& X  d/ ?
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');$ K! E7 A& y% x( j9 k9 Z/ S
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
3 n7 R+ P+ ]0 P& P0 D, M        char key3[8]="\0"; // 获取 key3,即最后的8位字符
( A! p& d( g, b+ F" k  Q( J        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
! j0 \/ B& a- X2 w, H' d        char digits1[64]="\0", digits2[64]="\0", c='\0';
; K5 @; R$ Z0 S' a+ i        size_t spaces1 = 0, spaces2 = 0;3 g2 G( r/ C. d; p) H/ y
        size_t key1_len = strlen(hs->key1);
: J* l1 B. l; _# L/ U) t        size_t key2_len = strlen(hs->key2);' i. p) h4 R) {4 |! I
        short d1 = 0, d2 = 0;. w5 [6 P9 @1 G8 u
        unsigned int result1, result2;% _  A1 i( M; \; b  f, i
        for (i = 0; i < key1_len; i++){ " b; B$ }! K8 z
                c = hs->key1[i];! Q  B2 |* U, |# j
                if (c == 0x20) spaces1++;% z8 d1 b8 M' F+ ?1 ~0 _# a; Z" S
                else if(c>='0' && c<='9') digits1[d1++]=c;
" m# S2 [  }3 G" {        }- J* V1 ?$ r/ r9 E
        for (i = 0; i < key2_len; i++){   \! K3 C( M% \4 Y
                c = hs->key2[i];/ ^1 ]$ U9 U2 a( h
                if (c == 0x20) spaces2++;
* E' g8 h0 t$ l1 ~                else if(c>='0' && c<='9') digits2[d2++]=c; 1 l2 w. Q. g( ~) R3 f$ @: A' E
        }; _* D! L$ d9 B/ y: B7 a/ I
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
( ?* |3 C( A. R/ m/ Y  |) B0 p        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);8 R6 {( R) s7 \1 F- D* i
        char chrkey1[4]="\0", chrkey2[4]="\0";
6 w6 K# D# }/ g7 h& h' L+ \( @        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
: K4 }) W  f7 s# C3 D: |        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);+ a  a2 [9 X5 O( f! l  Z
        unsigned char raw[16]="\0", dig[16]="\0";
' D$ K/ H, T& x' r0 l( z& B. X; \# g" O        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,. B) M# w! V' H# p, I+ `4 Q9 ?
        //  连接上key2的最后连接上头信息中的最后8位字符。, V! B3 X' E1 A6 Q
        memcpy(raw, chrkey1, 4);* D, R( A7 R4 o3 {2 b
        memcpy(&raw[4], chrkey2, 4);. ?% l/ H' q$ t
        memcpy(&raw[8], key3, 8);( |1 r" N, N: `3 g% S! {
        //计算的md5值7 S# c- J# V# \5 R5 I
        md5_state_t state;, W$ F* Q; w& w" b4 e" }
        md5_init(&state);9 q% I5 a: ~! {" ]
        md5_append(&state, raw, 16);
) M0 `( v9 b2 f! u+ G5 W        md5_finish(&state, dig);! R! e5 ?' y+ Q3 n- E) g# X
        
0 [9 d. ~: O/ Z3 M/ H        char handshake_str[BUFSIZ];. R2 I7 D' f* [8 ~0 Q: W  @
        memset(handshake_str, 0x00, BUFSIZ);9 u. o' n; Z3 t0 L1 m' `  W/ }
        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
$ V. M+ B; J+ Z- M' C( V! J                "Upgrade: WebSocket\r\n"4 e3 V6 S! ?- \+ b$ s
                "Connection: Upgrade\r\n"( V. r3 W/ }. p6 k
                "Sec-WebSocket-Origin: %s\r\n" 6 E/ u8 |1 j; q5 O0 S  w% _
                "Sec-WebSocket-Location: ws://%s%s\r\n"
) U( W* a2 I, M  {* I0 N                "Sec-WebSocket-Protocol: %s\r\n\r\n";4 W. x8 A: b' U- X- w4 O8 g
        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);% i: `# A4 I7 L
        free_handshake(hs); // 释放handshake指针,已经不用了!
, n( _& C. B4 N# u( d7 o        char response[BUFSIZ];
2 n9 f5 d3 G* L" G1 O. t        memset(response,0,BUFSIZ);
3 j' |+ n. C+ J+ Z* q1 E  @        size_t j=0, handshake_len=strlen(handshake_str);4 H6 V% A% ~9 g0 q  q
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];6 g& h6 k' T; h/ R9 L2 O
        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
. e" |) t1 {- a! S0 g0 N        // 这里的clientSocket就是连接好的socket对象了。% @* L' @% ^* O  L. {) U/ p
        int sent = send(clientSocket, response, strlen(response), 0);; ^0 R+ s! n* m+ a- a
        return sent>0;* N; \3 b$ E; o+ R3 I1 e
}% t( I- D* |  w
目录结构:) s1 A* ^7 n# A( K' L$ f4 Z
socket/
4 b1 ^# o( s7 {  \9 K1 ~      socket.c' ?' A: C; C# v4 n9 R; j
      md5/
% {4 }4 D7 i0 L. P& c8 g0 j( |        md5.h
, t" v' t! E- H! h. I) ~' Z$ T        md5.c
1 R  ]2 r# F; V库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
0 V( I0 x9 S# `' x; x8 S  d& L编译命令:  gcc socket.c md5/md5.c -o socket.o  ( B, i' p5 m: C
GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
0 R4 {, {3 i" C// socket.c 代码。
/ c: Q" {& p- _, g: s// web-socket example ; t8 I' j- d6 C3 _
#include <stdio.h>6 A2 E( C, H6 z# q" ^: K
#include <stdlib.h>
0 Q# |& f4 Z  u* q3 _6 Y#include <string.h>' M( B. ~# h$ [& G7 F
#include <inttypes.h>6 y! b8 E# Y! A. N# W
#include "md5/md5.h"
+ M: z+ t4 b" G! N( J0 H/ ~2 b5 X4 U- f# U- \
// #define BUFSIZ 512
& w) O- Y4 b& Y" u+ S5 m0 p: i" L
//定义handshake结构体变量: S. a  s- y  ^7 b% U1 ~
struct handshake {/ F! N1 ]! u: P
        char *resource;7 P( c2 l. L6 W2 |7 l0 P. d7 x& Z: ]
        char *host;
! w8 W4 I# F  Z" \/ M0 q4 y' @2 M% N1 K        char *origin;
/ e( i; F% H* D/ l" {8 v        char *protocol;
- y% A* f! z6 N% c        char *key1;/ @$ Y: f+ D( a7 ~
        char *key2;9 z" R* S& c- I3 ~6 N% n
};
, Q1 \6 T/ b5 ?% X. B- K$ C  H( l- \7 V2 B; }
, L$ A3 ^2 ^) V) u3 d4 ^: ~
//释放握手后不再使用的部分变量! L- e% q7 x& g) L. p
void free_handshake(struct handshake* hs){
6 k) V# j+ y+ V9 M7 ]* y6 Z! L( W        if( hs->resource != NULL )      free(hs->resource);+ M6 H2 D4 [% X5 d2 M$ ^8 [, E
        if( hs->host != NULL )          free(hs->host);' y& I2 P6 M! g  x
        if( hs->origin != NULL )        free(hs->origin);
+ Z/ c- e7 ], O4 a" q5 G' B0 m        if( hs->protocol != NULL )      free(hs->protocol);
" l  p! O, g4 h$ o  R" @& [        if( hs->key1 != NULL )          free(hs->key1);
6 j8 Y# A8 i8 r9 E$ O& P. [        if( hs->key2 != NULL )          free(hs->key2);- d/ C$ G& l0 B+ s& d! {& C% i
}
& n$ m1 P1 ?) K- f  m
+ t9 {( a, y. s0 ]/ K$ T// 这里对上一篇的match_string做了点修改,+ V' J$ R6 C" `, {2 B  {
// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
8 ?4 z# w! m6 L: }char* match_string(const char* src, const char* pattern, char end){1 s7 e6 D+ f7 @8 u" N8 V& x$ r
        char buf[BUFSIZ];. n* ^& ~! b# B( u. X
        memset(buf, 0, BUFSIZ);
. |( v9 Y- v; u4 b% G0 z1 Y& H        size_t src_len = strlen(src); " }, l; q" m: h* {/ S' r+ M) G
        size_t ptn_len = strlen(pattern);
, Y- e* r" o. u+ I/ ]        unsigned short b=0, p=0, i=0; 5 L9 }8 W; @7 A: R1 e9 v9 V1 F
        char c='\0';' w7 A. R+ e7 N1 h2 U( q
        for(i=0; i<src_len; i++){
! @7 Y0 O7 m8 i. `' \9 C& A4 A                c = src[i];
- ]( o% P4 q' h3 a                if(p==ptn_len){ // p==ptn_len 表示正在匹配中6 Z7 t) c. O4 s6 V- T$ h
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
- G; H  P& h$ h/ ~                        else buf[b++]=c; // 匹配到的字符
- f, ]% J" A; ~* G                }else if(p<ptn_len){ // 为达到匹配要求. u% v8 h% V* o9 I
                        if(c==pattern[p]) p++;
3 v" I+ A6 J# J6 x5 s/ q5 ]                        else p=0;
0 q6 }% e5 f8 w  j# a9 D                }3 t) X0 F6 z& X, K/ y2 f5 y
        }( W/ B& T3 i- r& S* n  V1 \. g
        size_t ret_len = strlen(buf);
2 a& v; q; {. o! o        char *ret_p; , S4 N; }& O+ Q1 m
        if( ret_len>0 ){+ f& Z2 `: ^" M- v
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
  H0 S6 U3 _8 T& S, V( {0 I' g# _                memcpy(ret_p, buf, ret_len);3 [7 Z6 O4 N" u* h' s
        }else ret_p = NULL;
/ R: g+ Q. d2 X) j# k0 ^; z- Z        return ret_p;
% n/ R" S" N3 j: `3 G}% c: }7 s5 m5 r3 h( _7 r, I
% C3 s8 x% w* I7 k/ h
// md5 加密函数,用的是网上一个实现的比较通用的版本。+ N0 Q- r) a5 ]/ C: W
void md5(const char* src, size_t size, char* digest)
9 i# {) G5 Y% R7 ]; Z" `{
+ C4 C; t1 ^" \/ R0 U* r; @; F+ x        md5_state_t state;# S4 l: m' u# O* I( _
        md5_init(&state);
$ h2 o7 d# R/ R) s& ^0 ~8 A6 a$ Q$ ]- g        md5_append(&state,  src, size);
) m' D1 l: X: z4 \        md5_finish(&state, digest);( ?/ j3 U3 I7 d5 n+ g) `+ F
}
8 [/ Q; w, ]9 W" f: s
* L- I# Z' m6 B# [# G, Lvoid p(char*s, int len){
  w+ k6 `+ C1 u; b+ A  \3 w: E  h% s        unsigned short i=0;
, a- Y3 m- e, Y: u- @        for(i=0; i<len; i++) printf("%c",s[i]);
- B8 l& Q* F8 I        printf("%c", '\n');+ {) P4 W; g. ^) R% T6 e' h4 i4 u
}
1 G( ~( A, n- M0 Y. k( m' D
6 i' Z' X) @/ Y' h* y$ ovoid handshake(const char* src, struct handshake* hs){, h$ t) n7 ?# ^+ J4 o! E. ^
        size_t src_len  = strlen(src), i = 0 ;3 K* y1 q5 t& k! T/ w' J1 b# B
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前6 P$ c( ]+ r; V! M5 t
        hs->host                = match_string(src, "Host: ", '\0');
7 q6 K4 m4 R& S- C$ g+ f        hs->origin              = match_string(src, "Origin: ", '\0');0 [2 C( O3 K6 t5 d
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');2 g6 F/ I3 Z5 {; N% L
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');3 K) S5 t3 j, h2 N2 S' I6 S
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
! T! c6 j" k: E/ W5 l; g6 h        // 获取 key3,即最后的8位字符
+ I  z4 m9 d0 I* w: [# ]* V* E        char key3[8]="\0";, h9 I5 h1 g) ~
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i]; # i( q% Z- T; [0 c. t: z
        char digits1[64]="\0", digits2[64]="\0", c='\0';9 n8 C* n. d9 T, {* C
        size_t spaces1 = 0, spaces2 = 0;0 X- S: Z& P" d4 e" L
        size_t key1_len = strlen(hs->key1);( X1 M- Y9 C. m) ^: j
        size_t key2_len = strlen(hs->key2);- N% m# S- e1 L( @# \" [6 E
        short d1 = 0, d2 = 0;1 o8 q' I8 P2 h- ?
        unsigned int result1, result2;
( `/ j, G0 D% L5 |7 V3 b) B        for (i = 0; i < key1_len; i++){
/ T: H3 j5 B# L* b/ t( F2 E                c = hs->key1[i];
. Z9 s& k" L2 m% v$ m6 J3 w+ g                if (c == 0x20) spaces1++;: E6 }0 h8 J# B7 i+ ~
                else if(c>='0' && c<='9') digits1[d1++]=c;
/ m% \4 a$ W9 C        }
/ m# n  T1 h0 }4 D) y$ T1 ~6 E        for (i = 0; i < key2_len; i++){ # v5 q, E. f) n
                c = hs->key2[i];( a7 Z1 F& M3 e
                if (c == 0x20) spaces2++;
& z) v) N$ u3 k" j                else if(c>='0' && c<='9') digits2[d2++]=c; ; \- @2 p8 T6 L
        }
- S# U  m( H8 f9 D" ?" ~5 v        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);2 \# H# i. d4 i5 e& X, k  p
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);0 d9 c. a# D" p$ u8 z( c
        printf("ch1:%s\nch2:%s\n",digits1, digits2);0 u& ^8 u! A$ I4 D% \9 z
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
+ i, B% v* \' I% T9 s( }        printf("d1:%d\nd2:%d\n"  ,result1, result2); ( W" ]+ c; p8 w' J7 k/ U! o: Q5 c
        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";8 X& Y( g$ h, ^4 f
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
9 I6 @5 V; o, c: a. i" G- Q, h        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
% K% m) }% y6 c1 W        printf("ch-key1:"); p(chrkey1,4);
9 ]- n- f7 B1 u8 L, X! x4 B        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);+ O* v/ {9 j) Z( _) d/ E
        printf("ch-key2:"); p(chrkey2,4);
& o/ O0 G3 j/ P% N: j8 u; j( f% V        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);- R, m4 W' L3 S" ?2 F
        unsigned char raw[16]="\0", dig[16]="\0";
1 O  ~. ]& ]1 l* K, Q+ R        memcpy(raw, chrkey1, 4);
- S# I  d4 e; R8 s        memcpy(&raw[4], chrkey2, 4);. g7 o8 x( U2 x* n* o
        memcpy(&raw[8], key3, 8);! t* J; I7 ?2 _: o
        //计算的md5值
1 a7 G' V* q1 e( s) I( y6 L        printf("\nraw:");
; i# ?$ f8 Y# V' ^! N: |        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
8 b" _1 S# ?, V4 n/ _% Q6 Z5 }        md5(raw, 16, dig);
9 p7 o8 K2 c. J* C4 }5 Q  M0 B        printf("\nmd5:");3 j; ^/ i& z: ~6 d: q
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);* o( g! l2 }, f9 I1 n; f
}) W: c( W( q% w/ b; k2 L

4 I$ i! e: G6 C: d8 H8 [1 H& F. C* G" z# h2 e$ C$ Y% |
int main()
+ u* g3 y0 e7 z{6 ]7 Q9 Z4 a7 E- F) H; z, O
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\: c" A6 t* O- ?' d$ U; I
        Upgrade: WebSocket\r\n\
( A& w; h) o+ j6 j" R        Connection: Upgrade\r\n\' {8 L. P( I) F# K6 l
        Host: localhost:4400\r\n\
) h% Q; D0 `$ i# p) N4 F; h        Origin: null\r\n\4 U; E& E6 a7 N, F8 b. N2 D# E. ?
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
) d1 L. U. Z3 J; \        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\( H: B/ ]5 J2 W" f
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";
1 x% V# \0 K& ^! E% o4 E        size_t len = strlen(msg);
8 E7 K& H6 c* m* Q! W        msg[len]  =0x1f;
9 r" ~6 y3 A5 i/ Q, Z        msg[len+1]=0xf6;
9 m: K% G7 N+ \        msg[len+2]=0xf3;
7 t" \3 o5 P8 ~/ @, S$ r# ]6 h        msg[len+3]=0x3f;
% A4 Y5 _* N# D: S; j" ?; E        msg[len+4]=0xc7;( K( h: G6 H: ~8 m, E( v
        msg[len+5]=0x17;
+ \7 r  B( a: h  m        msg[len+6]=0x20;) m- K- u" Z6 ^0 ?% @# d
        msg[len+7]=0x88;
1 R# Q/ r6 |( K" @4 O        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
: _0 a( J# r6 n8 C! x        handshake(msg, &hs);
  j- O2 C7 v3 P- ?0 b0 v* g8 m        free_handshake(&hs);
, d8 G8 N1 U9 e7 C        return 0; - b* }, Z6 i- E* G: z: [
}
6 `4 l- v" w+ Q! Q, h1 }( l) w5 @5 d! v+ V- P* `

5 Z; a( \" n* F6 |) |) x8 M8 t测试的结果:
* R  s% F! E2 [3 t! U: ?raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 ( O) A  h4 s, H) F. M
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68
6 p/ ?  Z9 j. E' {9 m对比了nodejs的版本,握手部分生成没有错误。
$ _& v$ f8 Y+ B" q
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-12-30 00:27 , Processed in 0.028617 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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