找回密码
 注册
查看: 5839|回复: 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++庞大的类库和模板。+ S( O+ G5 }3 [1 }5 M6 x$ O3 T( Z
完整的windows版本socket握手实现:
: e/ t  n# y  z: @3 M
! d5 D; _" q7 Jbool WebSocket::handshake(const char* src, struct handshake* hs){
6 ?# k* A$ h4 I" `0 y        size_t src_len  = strlen(src), i = 0 ;
* R( m; K9 d4 F% D% N9 P        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
- g! k/ [6 y8 E& z7 d        hs->host                = match_string(src, "Host: ", '\0');
3 T/ P) y' B. A+ N1 f2 e0 n        hs->origin              = match_string(src, "Origin: ", '\0');
% f, _# b* i: \! Z6 m        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
/ n* Q% j, Q8 w/ l" q0 L        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
8 d/ W6 Y2 s4 w6 h& P; f( z+ m" Q        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); 5 L2 f. s. o. B: ~# [+ x
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
" e% o% b! B$ O7 w! p( V9 U        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; + Y9 j0 s5 v3 ?4 ^$ E
        char digits1[64]="\0", digits2[64]="\0", c='\0';8 \% U  p$ v2 H
        size_t spaces1 = 0, spaces2 = 0;
: C, e1 k) S7 F7 Y4 t6 J$ `( y        size_t key1_len = strlen(hs->key1);
2 u+ r5 e3 E2 {/ ~" E& h4 i        size_t key2_len = strlen(hs->key2);
( U9 \* ^: T1 D) f        short d1 = 0, d2 = 0;3 R. P) j9 K3 ~
        unsigned int result1, result2;5 d7 s5 F! f% s( Y' ~  ^
        for (i = 0; i < key1_len; i++){
9 e# s, Q! x  {- D, f  I* b5 p                c = hs->key1[i];
2 S( @* \: Y# {5 d$ W/ a7 ?                if (c == 0x20) spaces1++;
0 c  w1 l/ F0 ^5 l+ \                else if(c>='0' && c<='9') digits1[d1++]=c; - J* G7 H$ ]# N
        }
! s/ E5 ^7 _3 N4 t/ Z; @        for (i = 0; i < key2_len; i++){
) y8 t* e$ o0 M( `4 `5 V- c* p7 _                c = hs->key2[i];8 Z+ y: C0 Q( E
                if (c == 0x20) spaces2++;
+ ]; E* S( m; N- B                else if(c>='0' && c<='9') digits2[d2++]=c; $ R1 w5 U$ @3 O2 F- f- }
        }
% D$ g) ^; M/ O" Z; c* g+ h3 W7 s        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
. n3 u6 M( |: R$ a/ M: z0 v9 V        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);) S9 W0 Y0 A4 {* ?
        char chrkey1[4]="\0", chrkey2[4]="\0";
" Y( L& ?+ R# Q. @        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);2 P# j: X3 ~" ^$ Q
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
: T7 ^8 i2 ^# r- V        unsigned char raw[16]="\0", dig[16]="\0";
1 m" o# V8 G8 h/ q% z0 N5 f% r4 ]        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
6 i# l+ P4 `7 O5 `0 b. y1 s* A        //  连接上key2的最后连接上头信息中的最后8位字符。( Y! v6 v/ ^& }0 x
        memcpy(raw, chrkey1, 4);
& t0 o$ h6 H) V+ Y% J9 E        memcpy(&raw[4], chrkey2, 4);" X- l" z/ l4 N& N
        memcpy(&raw[8], key3, 8);
( @% [9 q' Z# [  B) _* s        //计算的md5值. D/ a: y0 Q( t  ~0 P8 ?5 C8 R5 _
        md5_state_t state;
0 Z" W0 `# M5 I7 e* T' o3 l        md5_init(&state);
: \: n$ i- T: N" N  S        md5_append(&state, raw, 16);
5 U8 C# M' Z, i5 e3 e        md5_finish(&state, dig);
! e* Y/ W, y- e: b" b        
1 O* {9 t! B/ ~" H. j' M9 Y        char handshake_str[BUFSIZ];2 Y2 c* @3 p6 i% J- Y" v7 E- p
        memset(handshake_str, 0x00, BUFSIZ);
* j/ S2 g! _9 m0 O        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"( E6 B$ C6 g* m. i" s
                "Upgrade: WebSocket\r\n"
! p( ^$ q9 j3 _3 {) g                "Connection: Upgrade\r\n"
7 F" A7 A# n4 ]                "Sec-WebSocket-Origin: %s\r\n"
7 A) N. V) P) S. j                "Sec-WebSocket-Location: ws://%s%s\r\n"6 H/ G4 q* a/ m' V
                "Sec-WebSocket-Protocol: %s\r\n\r\n";8 L: _9 `8 Z0 U0 w5 Z
        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
# u. Y6 Y5 O6 g1 Q0 d# w& @        free_handshake(hs); // 释放handshake指针,已经不用了!
, M; S" }" x: n% ^6 L+ n        char response[BUFSIZ];
8 ~7 m* M2 r8 \        memset(response,0,BUFSIZ);7 h5 X' m$ b' r2 G5 ~5 ^
        size_t j=0, handshake_len=strlen(handshake_str);% A) M4 J/ P" ]5 L/ Q9 H
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
' `4 O7 F2 w' R0 [        for (j = 0; j < 16; i++, j++) response[i] = dig[j];( R, z# m. a  o5 Q
        // 这里的clientSocket就是连接好的socket对象了。
1 n) ^0 g5 J6 b1 a        int sent = send(clientSocket, response, strlen(response), 0);" F: |# g: h7 B8 v& F, b
        return sent>0;
9 P0 S4 g" \3 I' U) z& Q}
1 ?5 y2 M  r4 N9 z! l; X/ F目录结构:
/ v0 M! t8 q1 _; usocket/
: x9 N, P+ ~" v* W! }      socket.c5 `, K" a8 L  a) ?/ R
      md5/
; m8 L2 F2 d* a        md5.h& E9 q# A! _% ~2 ^9 {
        md5.c* ]3 R2 P, }0 }; ~6 a3 y9 Y, j/ K
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
6 L  b9 E6 F& N/ R4 M; `% e编译命令:  gcc socket.c md5/md5.c -o socket.o  
, i/ g7 ]  I5 P1 `2 m4 T GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。& a$ m; @; Q9 |
// socket.c 代码。- {5 r; A' s$ G3 X3 h0 R5 n% {4 {
// web-socket example % W1 U4 v4 q$ m# `* @( c; k
#include <stdio.h>0 D+ {3 k, g. h( h1 g$ C1 t. y3 s
#include <stdlib.h>3 ^- _( ?: E: b0 i4 b3 V# b
#include <string.h>% k2 U- f& v6 V+ ~2 E
#include <inttypes.h>
% B) j# A4 L4 v% Q; i5 x1 `#include "md5/md5.h"
. n3 I9 Q  E# z" I' G- ?1 \) M
+ U8 g$ `# E, @6 x5 v7 ]! h& t// #define BUFSIZ 512
3 N% K" R- \' y& s2 F0 |! v  A
7 z2 W$ o3 j/ q1 W//定义handshake结构体变量7 H8 u- ?3 w8 t
struct handshake {
% p/ r( z) j  Z& O+ }& Q0 B9 c9 w# d: p* {        char *resource;! i) z5 g- g- g( l6 K+ d
        char *host;
) U- F0 j0 M% O4 }7 P/ G        char *origin;
0 E4 m( b2 S" b, h) h, H" _        char *protocol;  S- A, h% X9 |
        char *key1;
' K% ~, h3 h4 ~        char *key2;
1 Q) F! j0 A3 K6 k) u! \};
) s- Q6 ]/ |: u8 F( f
: r8 {$ r" s9 V% j3 s8 Q; \, v( Y! V) P5 _/ O8 t; [, g% F
//释放握手后不再使用的部分变量0 |/ g5 S5 q- I9 c( k
void free_handshake(struct handshake* hs){! k8 p+ u* A: h  b1 ]2 [  L
        if( hs->resource != NULL )      free(hs->resource);
' M4 R0 J% t1 J2 }" Q, c        if( hs->host != NULL )          free(hs->host);
; h1 {6 _: ]. P, ?        if( hs->origin != NULL )        free(hs->origin);- u& [8 V3 n) T# Z
        if( hs->protocol != NULL )      free(hs->protocol);
& @' s5 x2 |7 w1 p        if( hs->key1 != NULL )          free(hs->key1);5 j' R6 F2 q. Y6 j  ^
        if( hs->key2 != NULL )          free(hs->key2);0 O1 A5 {. u5 p: q/ l
}! g% s% Y5 T# m- s% w* f& _
0 S! y3 U, \) ?0 M* p" v% N
// 这里对上一篇的match_string做了点修改,
$ L/ n+ j1 L0 @- K  }& a/ }! A// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理# \" C+ R& h1 S! V1 J; e
char* match_string(const char* src, const char* pattern, char end){7 ]1 ?! g/ H  Q
        char buf[BUFSIZ];
% x& z, X1 I/ ^& C9 J        memset(buf, 0, BUFSIZ);
  u8 W- V: w( Z- ?3 Y        size_t src_len = strlen(src); + i5 l" F# r4 d# b# g
        size_t ptn_len = strlen(pattern);. F! H; J$ J& c0 k, E' l9 D: x
        unsigned short b=0, p=0, i=0; 8 [" M+ ~* l+ m  G6 a" u: O' l$ U
        char c='\0';  H; `1 ~3 B5 _$ I
        for(i=0; i<src_len; i++){
4 f! L/ y, l, g; ]                c = src[i];
. A9 R  Z# K$ z1 }0 n- ?- [                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
) n, V5 U1 x! [% c" X) s                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
: d# N% {. T4 t# N; ?. O                        else buf[b++]=c; // 匹配到的字符
0 G1 c' y0 ~/ r                }else if(p<ptn_len){ // 为达到匹配要求
  F. `; v; s8 M5 o, e$ y5 C; U! _                        if(c==pattern[p]) p++;
. F8 N8 Y) s9 H* Y                        else p=0;8 [+ T0 m5 K! ?, J  c' M5 J
                }
% i) ^( |( F- M4 `, `7 l; y        }
- B6 N5 p: @0 h* h/ B        size_t ret_len = strlen(buf);
3 }# X; p. z- _        char *ret_p; ' P' A9 e. Z7 j; H4 C
        if( ret_len>0 ){
; f1 w: v( w& _2 c8 u                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
8 s/ X4 P+ y( ~# V8 R                memcpy(ret_p, buf, ret_len);: ~" G: y* J- S3 m8 c
        }else ret_p = NULL;1 L) q% @: z) s4 n% i; Q
        return ret_p;
. X2 y4 T" k/ q2 z: ^/ s3 j}& b/ b, Q) a7 {2 ]7 [

# _1 ?6 ~1 ]8 S: L2 Z0 ]// md5 加密函数,用的是网上一个实现的比较通用的版本。1 g# H: a& V5 M1 K, @2 w- f
void md5(const char* src, size_t size, char* digest)9 _; [3 [, _+ N. U2 l
{# h& a" _9 ?* M3 U
        md5_state_t state;
9 C( D2 `) r) E: h6 R* M        md5_init(&state);
1 j* l# c& }! \  S7 F        md5_append(&state,  src, size);
# @0 j" T# N% A! b. M  x        md5_finish(&state, digest);' h$ m: F" j- z- W$ L$ ^# t
}
1 g% x% Z/ [, ~& B
! O, M5 [/ `8 k' x) l9 z3 fvoid p(char*s, int len){! Y$ a2 m( ?) ^3 r! I0 M5 D
        unsigned short i=0;
8 u) d( @" h* D  X4 M$ @' i        for(i=0; i<len; i++) printf("%c",s[i]);( \$ ?% ^: e4 O$ d3 V* D
        printf("%c", '\n');
5 }( }1 O8 L+ w" d0 e$ @' T7 P2 S2 ^}
8 q( z! T& G( p  P+ U0 X( F# |9 b: Y* G, ^
void handshake(const char* src, struct handshake* hs){, O% W3 D' Y9 l" B6 a( a# I7 R
        size_t src_len  = strlen(src), i = 0 ;/ q2 v% U% a) Q: q- C/ `
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
9 ~& ?( w" `9 ?+ s* o; j- g! I        hs->host                = match_string(src, "Host: ", '\0');* H) I2 B  w) k' J- z+ O0 |" o
        hs->origin              = match_string(src, "Origin: ", '\0');9 t) L2 M) M& |# }6 x) J
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
( O! p2 d  D! U        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');/ B- l% j. I/ M( A3 a6 K' o) O$ u
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
0 [* \" q5 W/ _        // 获取 key3,即最后的8位字符% n: G7 w) \+ A; `
        char key3[8]="\0";  P7 M( w( T" B. H& L) h- j) E, l
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i]; ) G1 H' ~* C; [8 y- c
        char digits1[64]="\0", digits2[64]="\0", c='\0';
. s8 n8 R5 ~7 p* T( K9 O        size_t spaces1 = 0, spaces2 = 0;
0 |0 I3 ^: b8 F4 K$ W) N3 z) m/ O" I! }        size_t key1_len = strlen(hs->key1);9 m8 l* Y) Y5 @- _5 ]
        size_t key2_len = strlen(hs->key2);
" X& q, [) u; |1 @" p3 i        short d1 = 0, d2 = 0;
6 W" Y) x2 X/ K1 _7 t! [        unsigned int result1, result2;* `9 a1 ]/ h$ k: w9 R0 w% s8 I" v1 G
        for (i = 0; i < key1_len; i++){   A' J  Z2 d) M8 x1 X
                c = hs->key1[i];
- k1 A6 X9 I5 v1 N                if (c == 0x20) spaces1++;
4 U* v* o! u) [" Z3 {$ F! a4 `3 [8 D                else if(c>='0' && c<='9') digits1[d1++]=c; 2 t. \5 I7 z# M9 \, x2 R1 B
        }
# B& @2 {- J% L6 e2 |; R! }        for (i = 0; i < key2_len; i++){
6 M# r& {5 s, [                c = hs->key2[i];
% n* u7 q0 n# u8 j4 H1 C                if (c == 0x20) spaces2++;
( X, W' k& c0 ?* {: t) y) n                else if(c>='0' && c<='9') digits2[d2++]=c;   v" ]7 f5 ^" H  n+ l
        }* n* g. V" h4 H. |# E
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
9 I! r8 }& t+ e* j8 `        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
: U! N% B% _+ J        printf("ch1:%s\nch2:%s\n",digits1, digits2);
8 z& q% S0 X7 Y* x0 x4 T( w        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);+ v! P7 O2 A. y
        printf("d1:%d\nd2:%d\n"  ,result1, result2); + \, S' G* y0 j" T" f6 L# }2 Y3 ?6 t
        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
  e. B, y6 w, R3 M9 ^/ [, f        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
7 a( g2 B$ P0 P* l# H1 R" Y% g        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);( c" c8 }4 V8 q% L
        printf("ch-key1:"); p(chrkey1,4);  Q' Z1 p2 x5 a% V1 n8 m8 ~
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);9 i* Q: }" {9 u
        printf("ch-key2:"); p(chrkey2,4);; F& ]: X8 O0 w9 X( q
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
% x1 [8 `$ [9 q+ D) }5 P- z        unsigned char raw[16]="\0", dig[16]="\0";
8 I* c9 ^; W; H' [, P- `- }* B        memcpy(raw, chrkey1, 4);% h  b2 o0 E$ ?! H4 u+ \4 g- g
        memcpy(&raw[4], chrkey2, 4);& `' J9 U# @4 P
        memcpy(&raw[8], key3, 8);( a$ y: ]* }0 W- R' o  K2 _
        //计算的md5值5 j) r: w/ C6 r* S
        printf("\nraw:");0 t% V; v8 B; L, ?( d9 L( r
        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
% N7 D6 U; F5 V" E        md5(raw, 16, dig);5 p' b: z. `  r8 S: a
        printf("\nmd5:");9 ^! ?0 ?& X, j0 I) h- |. D4 {+ q
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);: Q) c) g6 |9 U7 z0 t# s, N7 a( {: T! [
}3 ~; r  D# k% [. d& m/ s2 r. d

3 z) V$ @$ e$ |7 }+ i7 g- h* V" x! N
int main()
- ^/ \7 d  Q" s5 E) @6 f{
3 I1 g! _, N" _        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\) b: w1 k2 P6 ~% z' {6 Z
        Upgrade: WebSocket\r\n\0 p' X5 w4 _, G  G+ A
        Connection: Upgrade\r\n\
0 i* e  j5 g5 Z& d        Host: localhost:4400\r\n\" \5 k: y3 s8 g) W3 Y) }
        Origin: null\r\n\
2 U: n7 m* \7 p; ^  T6 G' J8 h        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
' P2 v: R! Z- n+ L        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\1 B+ B' `4 d9 v3 a
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";6 S8 P: B( Y* \/ [6 X
        size_t len = strlen(msg);
- P7 J4 F! T* h+ ~: B9 q        msg[len]  =0x1f;
! W6 d# d5 t1 O& I        msg[len+1]=0xf6;
- i9 W; o6 l% c/ x& |) B9 M        msg[len+2]=0xf3;2 s5 R( V, g6 K4 e
        msg[len+3]=0x3f;
& `0 j6 @4 P' Z, n. I. N! i0 z* C        msg[len+4]=0xc7;
" L- f* {3 M% b6 z7 Z        msg[len+5]=0x17;
" A* @0 h. q1 e; {        msg[len+6]=0x20;- c( @) q: c0 z+ M/ g: N4 p
        msg[len+7]=0x88;$ _. C: @) N1 y8 O5 r* B8 V( D
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
' m# }& i/ n0 R( R        handshake(msg, &hs);
/ K  U* o3 c. w) f" s7 q        free_handshake(&hs);
; J8 n: t4 E* A/ s* J6 M        return 0;
' T6 K/ h) ^! f}
2 Q+ i8 ~3 |% `4 V; [! {# k! D9 g7 Z% @" _# Y& j& ^# y+ a

- Z$ N+ t# @  `. ^测试的结果:3 D( `% H% J+ f. b9 e+ d/ Y  F; \
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 % }) h4 P3 f' N4 L
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 + H- C$ T  F, _$ ^
对比了nodejs的版本,握手部分生成没有错误。
, N$ Q  \9 m9 t; n: `2 Y6 A6 _
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-10-2 03:03 , Processed in 0.023021 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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