找回密码
 注册
搜索
查看: 5673|回复: 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++庞大的类库和模板。0 d8 E  M- I0 L: @
完整的windows版本socket握手实现:9 ^% d5 _9 b( W7 N7 h# j1 C

4 O% a  ^" R/ \/ T! i' ibool WebSocket::handshake(const char* src, struct handshake* hs){0 F( d4 u3 J8 r+ D! p( Q
        size_t src_len  = strlen(src), i = 0 ;6 N! ?2 _! W/ d1 a( A
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
$ P% @- E) G  j" k4 W        hs->host                = match_string(src, "Host: ", '\0');
. M2 m: D# @" d& ?6 y        hs->origin              = match_string(src, "Origin: ", '\0');1 M" K/ N. h6 `% j$ Q$ U
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
  X: q: e  f8 v2 e0 y        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');7 B* h, Y3 b& @6 Q9 q# _2 S
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); % Y' \" @# N( T$ w  ^3 |, R
        char key3[8]="\0"; // 获取 key3,即最后的8位字符5 F3 J4 B# A7 S) h0 ~
        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
  d% _  D, u+ a6 K( Q7 [        char digits1[64]="\0", digits2[64]="\0", c='\0';0 |% @. F" d2 L* i6 t
        size_t spaces1 = 0, spaces2 = 0;* Q9 v1 J5 x4 K) {6 Z% h
        size_t key1_len = strlen(hs->key1);2 R' E$ o1 I3 y5 {  t! t- {& A0 H
        size_t key2_len = strlen(hs->key2);9 Z, w, P+ b+ A* d
        short d1 = 0, d2 = 0;
( y* |+ A* Y! c        unsigned int result1, result2;
* K6 t. C4 @: R  `        for (i = 0; i < key1_len; i++){
' C2 ^9 C% q$ n% i                c = hs->key1[i];0 E7 A: {9 [. X0 A
                if (c == 0x20) spaces1++;
3 J* x5 O1 \" D1 X9 r                else if(c>='0' && c<='9') digits1[d1++]=c; 5 s: w& E# Y4 R2 ~. _5 ]
        }
/ E" P! `3 g; n* e9 l- d; ?        for (i = 0; i < key2_len; i++){ $ r9 |$ y0 t4 H2 d: i* F- d
                c = hs->key2[i];
& |! |9 d6 K3 [9 G; k3 K- u                if (c == 0x20) spaces2++;
6 \5 r, Q' r1 I& y( M7 J4 N, R                else if(c>='0' && c<='9') digits2[d2++]=c;
/ z# R  N& l  @        }# x/ L1 B9 {9 A( ?0 y. O! F) e# z
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
4 M) o$ p; w4 r* M) ]        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);0 L/ B0 h/ z) Y0 y
        char chrkey1[4]="\0", chrkey2[4]="\0";
% m9 N) P' m) R5 O. M( |5 ?        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);8 U% {, p& s0 }, \
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
' Y5 J) Z9 q" F& y$ g& C        unsigned char raw[16]="\0", dig[16]="\0";
& Q6 ]! ^8 u% @5 o1 B* w6 E0 w        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
4 R  W% \7 `) F. G        //  连接上key2的最后连接上头信息中的最后8位字符。
! h# t; Y% {1 ~& ]  i, i& Z        memcpy(raw, chrkey1, 4);
+ S3 X' l$ W; N: g3 ?; A% S; L4 ?" w        memcpy(&raw[4], chrkey2, 4);
- ?  ]% F# H$ e% P) _0 R        memcpy(&raw[8], key3, 8);! a0 m9 v2 h# t+ o- `8 Z- N" I# y6 ]
        //计算的md5值
) [" K& y3 L. I( m; l$ G+ [        md5_state_t state;( D0 ^1 r- g# d: Z4 y. X
        md5_init(&state);
! P" H" d" R- P9 |; d/ Z* a        md5_append(&state, raw, 16);
$ }; f7 N% q2 ^, J        md5_finish(&state, dig);, T, z. X; V3 U- d
        
9 b' V* }' M* B0 y% @9 P" f9 U        char handshake_str[BUFSIZ];
7 Q' M0 d  C# e7 k' x) D. |% M9 E9 a8 z        memset(handshake_str, 0x00, BUFSIZ);
, ?) l6 p6 u4 [        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
! _- R, ?1 t' p/ W2 C7 i( K2 p( n/ R                "Upgrade: WebSocket\r\n"
% i8 r+ A, U3 t                "Connection: Upgrade\r\n"
% {6 F; ~% z  [- [. C4 `$ i5 |                "Sec-WebSocket-Origin: %s\r\n" 9 |1 S$ H: w7 D$ j! L
                "Sec-WebSocket-Location: ws://%s%s\r\n"
1 G/ f, N4 T; ]* D5 `- ~                "Sec-WebSocket-Protocol: %s\r\n\r\n";( o7 A; }% ]' X" U9 J; r
        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
1 O% K  J& v( C        free_handshake(hs); // 释放handshake指针,已经不用了!
. o# l4 W( N3 C& M        char response[BUFSIZ];
4 Q% |5 Y( ^* b8 c0 ^5 G, L$ A8 g        memset(response,0,BUFSIZ);
1 P( ~  }3 m9 s( ^9 g" }        size_t j=0, handshake_len=strlen(handshake_str);
2 u3 p6 `. Z* i        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
# Y0 f! M$ _7 u& W3 |8 c, a        for (j = 0; j < 16; i++, j++) response[i] = dig[j];9 w/ H1 k* ?- o2 h. U9 v
        // 这里的clientSocket就是连接好的socket对象了。
' K8 a% B5 D% U+ I1 D! J7 x) G5 R        int sent = send(clientSocket, response, strlen(response), 0);
6 h; {6 t: K9 R4 ~        return sent>0;
: w4 o" M5 u$ _9 ^  X}. l6 H: p, O" }5 P6 D/ k5 d9 |9 W
目录结构:
2 g9 F, _. ?( {# o. jsocket/2 [, ~, R. }! |, Y6 x" {
      socket.c  p# A8 o$ @, C% A
      md5/
) Y# [3 A0 W: b+ t+ b        md5.h
: t( @# [' {* k4 \: T        md5.c
+ \! w, ^* C/ e) B) H  O" a库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,# }: G  k: V5 S9 L" Y( J
编译命令:  gcc socket.c md5/md5.c -o socket.o  
5 G4 `! [7 [! w, f. p9 Z, j. U GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
% r. g* [5 N$ e' ~# h3 G1 G" h7 N// socket.c 代码。/ Q& r$ R% b5 W4 O3 P
// web-socket example
8 x* k- X) @' o; g9 Z6 e* k#include <stdio.h>
+ i" I* v  B1 B. i# y! x#include <stdlib.h>' y' g' e9 `7 t' d6 L* s# H
#include <string.h>& e/ u+ z8 A/ W! n* `4 h; A
#include <inttypes.h>
  y! y! l  S% `7 c. a3 a#include "md5/md5.h"
9 W* u2 k" C. H) b- x! ]) p' s
7 ]: h2 L) v  F0 |// #define BUFSIZ 512
" _3 Q& G1 J+ v- ^, [$ Z
. E# J$ n1 U, d$ S) j//定义handshake结构体变量' m4 |) o6 b. G7 B
struct handshake {+ g6 h4 y  x# j+ P  t4 X# J, Z- v# q1 [6 w
        char *resource;7 Q" c2 {( ~/ e5 x7 m
        char *host;- h5 i5 @) r% M+ X1 L: @# p# h
        char *origin;
* ?  V/ t4 x# w1 k        char *protocol;& t) z, l& \, E- }& w0 |/ r; x5 B
        char *key1;3 u  w% H0 E7 U0 H1 |$ x/ s& Z
        char *key2;
9 x0 D! A( T: u0 g1 E, `1 e8 y};3 W& R. {1 e( C- H  U

; _% k. _- ]0 ~0 C* [7 t+ M/ O: C" Z" ]" B
//释放握手后不再使用的部分变量
8 p; J  [- h3 N6 b! y5 gvoid free_handshake(struct handshake* hs){
2 N! ], |/ u& k! P+ `        if( hs->resource != NULL )      free(hs->resource);& r. k/ Z; [7 T5 M
        if( hs->host != NULL )          free(hs->host);
. A* h; W" U& T4 Q        if( hs->origin != NULL )        free(hs->origin);9 p: T- ^( g  H- Z
        if( hs->protocol != NULL )      free(hs->protocol);
, e" I; f6 t- M. h4 Y        if( hs->key1 != NULL )          free(hs->key1);% \; ?9 N' \  E) i
        if( hs->key2 != NULL )          free(hs->key2);8 f9 {" A: o% q5 U3 W) i6 D; E
}& {* s+ w$ Y$ a- B. Y4 S# i
* I" C/ x# D, g/ [
// 这里对上一篇的match_string做了点修改,% T( S; G& G3 o2 l
// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理9 V; H& y; g. \
char* match_string(const char* src, const char* pattern, char end){
% w7 Q* [1 }  L" u2 b3 ]% ?        char buf[BUFSIZ];$ _& s1 Y. @/ ]& @
        memset(buf, 0, BUFSIZ);
' u3 G. r# T* I* ?7 ]* n        size_t src_len = strlen(src); / Z$ T8 ^2 J4 Y
        size_t ptn_len = strlen(pattern);0 Y4 W+ [: f+ `* c3 Z8 Q
        unsigned short b=0, p=0, i=0; ( k- g* x% q% s" e
        char c='\0';* X6 P; [- b, e, H" N; d
        for(i=0; i<src_len; i++){2 }( d/ a( t3 q" ]/ Z5 K
                c = src[i];
: ^: u4 B5 _9 e" y0 n                if(p==ptn_len){ // p==ptn_len 表示正在匹配中3 b- j$ C# b8 p7 [* \
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束! ^, ^7 L0 [# y* [. @
                        else buf[b++]=c; // 匹配到的字符 : W- Q2 E# _( T6 }5 g/ E) w
                }else if(p<ptn_len){ // 为达到匹配要求% b3 b( R  y: F: j) V$ n8 P! A
                        if(c==pattern[p]) p++;: k" J& i! B2 K6 y
                        else p=0;9 k1 E- m$ [4 U0 P4 S
                }0 n9 `) ~& G. q: s, \  `
        }& D" {5 a) b5 X- ~" ^2 g" B) z0 W2 j
        size_t ret_len = strlen(buf);7 v" o' w/ _$ a  k
        char *ret_p; 1 R, S, d3 C4 P$ }- x
        if( ret_len>0 ){  C* S; T& @. j
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
7 f6 y  k- h/ K3 `4 K, `8 E                memcpy(ret_p, buf, ret_len);
! B  Z: u/ Q- G3 n5 W        }else ret_p = NULL;
# {) y  I( D0 U& P5 @; @        return ret_p;
3 W! w7 l0 n- Z}) }4 b- K4 Z# g# }

7 q5 {- q: Q( w// md5 加密函数,用的是网上一个实现的比较通用的版本。
- p2 e- Y  E" a/ C8 _* ^9 ~void md5(const char* src, size_t size, char* digest)
  o+ j& ~0 b- v5 s8 z' D( ]0 w2 `{  X' P7 p% _1 `0 n# X0 A
        md5_state_t state;
0 b6 A9 ]" _' A8 X7 q        md5_init(&state);. s3 F3 l' p+ ]  K
        md5_append(&state,  src, size);/ Y' p! A+ _) g
        md5_finish(&state, digest);  d7 k: M7 h9 y8 L9 o
}' n2 o) K! ]( h# r1 H
! B3 L" X& U9 {3 m
void p(char*s, int len){
( t2 w. s/ M- b. m) _5 l1 `        unsigned short i=0;/ c5 A! D: A; O) q3 S; T( p/ D
        for(i=0; i<len; i++) printf("%c",s[i]);
7 O6 r- `5 n# c8 m% j" `6 }        printf("%c", '\n');! v. F% k, y" o# ~; A  J
}
$ l  E' C3 ~, v8 s
/ a- @6 R. d7 A3 _void handshake(const char* src, struct handshake* hs){
  H! d0 _( b- Z5 g1 N        size_t src_len  = strlen(src), i = 0 ;* Q3 m4 o- c! C3 `. x
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前9 l& C& V7 T* A$ [# x8 Q9 n
        hs->host                = match_string(src, "Host: ", '\0');
8 `* F. M1 k/ A2 I        hs->origin              = match_string(src, "Origin: ", '\0');
: X$ \7 |" S: x8 n' p- {        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');3 U3 j. @) h  T; e% j5 D! y1 z
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');3 M1 O2 w" T! u
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); ; N! n7 s$ P' `; M# `
        // 获取 key3,即最后的8位字符
3 O# w6 y* P1 Z+ ~, v2 G, O        char key3[8]="\0";4 O3 I9 r. \' J8 M$ M( ]# c, q: j
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i]; 5 m& O3 `( t$ [
        char digits1[64]="\0", digits2[64]="\0", c='\0';
+ x8 j: G, e% s9 X1 W5 _$ s        size_t spaces1 = 0, spaces2 = 0;
$ A; O0 J6 A% o, y        size_t key1_len = strlen(hs->key1);! N& Q8 x% L3 E: T
        size_t key2_len = strlen(hs->key2);
2 p9 [# Y; w1 V7 s( k4 N        short d1 = 0, d2 = 0;; G4 @0 g' i# @3 H1 Q
        unsigned int result1, result2;6 j( L4 k" J& U- k9 U4 ]
        for (i = 0; i < key1_len; i++){ 9 f- F( g8 A5 E, V
                c = hs->key1[i];
9 V- m- J/ |& b                if (c == 0x20) spaces1++;  i; y, r0 M4 V
                else if(c>='0' && c<='9') digits1[d1++]=c; # ?5 V# q: o7 R1 J) o7 X: E+ q2 ]
        }
7 P. n8 q$ H1 ?% ?9 E; g, D        for (i = 0; i < key2_len; i++){ 1 S/ }: T% y& t! x9 x) z& M
                c = hs->key2[i];8 l& e0 j- A5 d+ ^5 o
                if (c == 0x20) spaces2++;" e. ~% P. S5 q5 r1 {
                else if(c>='0' && c<='9') digits2[d2++]=c; & t' `& H  d8 S% |
        }
: c; {3 Q0 h( a' v. H* H/ m        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
5 C& Q# H/ V- a- k1 q9 {2 b* |        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);" X1 a- G2 g' q
        printf("ch1:%s\nch2:%s\n",digits1, digits2);/ E0 ]5 M$ R/ x6 N) r% i
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
5 @, `; o2 m, e0 u        printf("d1:%d\nd2:%d\n"  ,result1, result2);
; b$ x( F, r( b, D" j7 p. G5 R1 h, ]        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";, J+ f- s1 W: J8 Z; `
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);+ S3 C% p, Y( S  C+ \1 ~
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
8 ?# z. U  |8 x; {! V3 g0 i# d( }# C. G        printf("ch-key1:"); p(chrkey1,4);
! R# Q, L7 l. @1 G3 O. f: L        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
; I) L$ q+ v1 O& g% d; v        printf("ch-key2:"); p(chrkey2,4);. o! J( @- }% p, {2 z
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
0 J. Z  Y! M/ f6 @        unsigned char raw[16]="\0", dig[16]="\0";
8 S: ~4 p4 q) O! p% W1 ^        memcpy(raw, chrkey1, 4);: h( J* c  [0 N
        memcpy(&raw[4], chrkey2, 4);( R; F; [: h! y8 x
        memcpy(&raw[8], key3, 8);; A! ^$ O% H) X4 r% V8 w
        //计算的md5值8 y0 d% O% W- d2 L3 \6 x9 F4 {
        printf("\nraw:");7 B8 E* i( b1 A7 ?: N. T2 E7 ]
        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);$ }2 O0 C6 u6 j7 A
        md5(raw, 16, dig);! u' p, j: z) v' g3 w0 m
        printf("\nmd5:");; X$ \  O9 c* I! @, g
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
- F9 p( i) ?* _& n; j}
9 y% X8 ^: l* z6 A1 [1 |% }( Q
2 m7 u, O4 @; `8 ?' u$ f1 y; C& s/ ^$ ]0 r
int main()& M& B" q# i. h
{8 {) \4 `) V( U/ x0 l3 h
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\
4 x. d0 O* U" Y( S  I5 d        Upgrade: WebSocket\r\n\
; J" V  F8 u9 y4 I        Connection: Upgrade\r\n\" Q- n" U( x" w* t% c
        Host: localhost:4400\r\n\( |$ b: C; Q% @3 B" n0 x
        Origin: null\r\n\
6 A5 r" J; x; b& y5 v        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
6 W! _: ^& a; r( R6 y        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
% U2 k: Z0 @! F% F8 ]1 l6 i2 N3 s        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";; o* S# M! Y) L3 A  z
        size_t len = strlen(msg);5 {- N/ r& R8 p4 i
        msg[len]  =0x1f;. @5 [' D' A3 b6 X1 h/ J# i
        msg[len+1]=0xf6;+ n5 S( Y( x  P, q% e
        msg[len+2]=0xf3;
3 s6 d7 T; A! j/ i- k: H" ^        msg[len+3]=0x3f;5 E+ }1 M- ~% v8 Y* r. i
        msg[len+4]=0xc7;/ V" F7 |: N7 u, |
        msg[len+5]=0x17;
! f0 G$ i$ I4 K. A        msg[len+6]=0x20;
8 s9 D/ Y' E" w4 a7 A        msg[len+7]=0x88;/ ]( W& x( ^8 E# z) u7 W
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};- p! e0 t! C. y( A; G3 w
        handshake(msg, &hs);' C0 o7 j5 i. ^, F$ v
        free_handshake(&hs);7 r! f& y* E/ H; J* u, F( c8 g! L0 |4 s/ Z8 f
        return 0; " U  N& }+ ?: n" {  u0 S* c7 C
}
4 B; n: O; ]# ?8 x2 N6 x0 y
/ k# F7 P1 x. n1 c
# h7 P) D+ L  d% b. @" I测试的结果:6 N( m  ~5 L* U( O
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
5 G' U/ _. q# E: {6 n) Imd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 4 d9 ]$ N  p6 Y# y& ^7 k9 y  |
对比了nodejs的版本,握手部分生成没有错误。3 w: q* I2 x/ C% S- |' j2 q
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-18 04:48 , Processed in 0.023194 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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