找回密码
 注册
搜索
查看: 5277|回复: 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++庞大的类库和模板。- ]& m4 T7 U% J
完整的windows版本socket握手实现:' F) k7 w' Z* ~" q: R5 J  [* i
2 w) F$ w8 Z1 S  K% v" `
bool WebSocket::handshake(const char* src, struct handshake* hs){6 B% j) k- W7 ]  P+ w' ~. N
        size_t src_len  = strlen(src), i = 0 ;2 H  j7 T; b% D# S1 m' s- i$ H
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前- j3 x) e: ~- d  e
        hs->host                = match_string(src, "Host: ", '\0');
4 E- Q6 ~. L3 h4 w3 D/ T9 o8 j        hs->origin              = match_string(src, "Origin: ", '\0');
$ H5 |. A$ D' J$ ^3 C: _) _; x        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
: }0 x' r  m1 |# h* P( w        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');7 S- W4 @2 V# I& l3 Q3 p
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); & }8 [! j) X: U1 D, T$ f: S- r2 K
        char key3[8]="\0"; // 获取 key3,即最后的8位字符. E: t+ ~; A, P& v! ~4 q( e; V
        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
' y1 z( J% V5 g( R: R        char digits1[64]="\0", digits2[64]="\0", c='\0';$ A  T# c& G$ t& Z* d( r
        size_t spaces1 = 0, spaces2 = 0;
4 H. _9 D7 [5 }7 k, v; G7 M3 v4 j( f        size_t key1_len = strlen(hs->key1);/ w" E# P4 C; v. H- z
        size_t key2_len = strlen(hs->key2);( D5 C9 ]1 g+ q" g
        short d1 = 0, d2 = 0;
# v' J4 j2 Z  u% m! D, f        unsigned int result1, result2;
" B9 [, k7 E+ ?/ M9 d6 h/ K        for (i = 0; i < key1_len; i++){
0 `: O; k3 V, X( x9 v% z& L, D                c = hs->key1[i];
* x* @# E7 J. @: G/ x, F( F                if (c == 0x20) spaces1++;
$ b7 b7 s1 w' V! j                else if(c>='0' && c<='9') digits1[d1++]=c; " t2 K  ]: t; v5 a
        }0 F8 w/ g" e# j) |. `- G: T; K
        for (i = 0; i < key2_len; i++){
7 M; [6 i; ?! t' a                c = hs->key2[i];7 s# W+ W3 e( ?1 w1 z0 z
                if (c == 0x20) spaces2++;
, _; o! z: x# H' F                else if(c>='0' && c<='9') digits2[d2++]=c; ) E8 w. L! ]0 @/ z$ y, n
        }2 P+ M1 T6 |2 t  w
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
$ K6 O) U; k+ R        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
1 Z: {1 S3 f* w5 r: ]        char chrkey1[4]="\0", chrkey2[4]="\0";) ]& {5 P4 ?" F# }! d2 r
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);0 R3 t  R7 N. B* E* w6 ~) n
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
8 h* h) y$ D3 q        unsigned char raw[16]="\0", dig[16]="\0";
1 v8 I% J- T/ g9 k( X: b        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,1 N/ I' Q( [5 M% {" V0 D4 e
        //  连接上key2的最后连接上头信息中的最后8位字符。
; p! V1 G" V5 F& J% A        memcpy(raw, chrkey1, 4);: w! q# S, ]" Y" |1 f" B& v4 ~4 w
        memcpy(&raw[4], chrkey2, 4);
# _$ h' Q/ x. D$ l3 G- m, \1 ]        memcpy(&raw[8], key3, 8);
! u* |: n2 C3 `7 c5 d        //计算的md5值
3 ^9 p! o* u" o( k  d3 O        md5_state_t state;
, R! L  {( k! c. ^- L; ]        md5_init(&state);, y! y& D# |" u* X& m; m
        md5_append(&state, raw, 16);; D4 ~3 z9 V/ b5 {) |" F
        md5_finish(&state, dig);
) p* K/ r9 \+ j: s        - L. N2 A/ j+ x0 d. U/ e2 D
        char handshake_str[BUFSIZ];  V  g0 I, a" ~/ |
        memset(handshake_str, 0x00, BUFSIZ);
# z6 n2 E# x. V9 ~3 ?        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"6 H0 m' k" U5 X& X
                "Upgrade: WebSocket\r\n"
8 \5 g+ A) A+ f+ m# K4 p                "Connection: Upgrade\r\n"
3 D( ?; e: U6 y% i+ z- J/ d+ u: k. J                "Sec-WebSocket-Origin: %s\r\n"
7 ?) u: ]: C8 ?3 z1 Z7 U$ D                "Sec-WebSocket-Location: ws://%s%s\r\n"
5 g' @1 ?0 @' z) K2 |                "Sec-WebSocket-Protocol: %s\r\n\r\n";
4 o7 N6 x1 ?* X        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);  S6 X9 t- J. {' f% a  n& P
        free_handshake(hs); // 释放handshake指针,已经不用了!, \, Y4 ?7 o% q" c# N
        char response[BUFSIZ];
  X" |0 K4 ?9 W* |! C% _: R4 {        memset(response,0,BUFSIZ);
) m0 e- r  |0 ^/ ]9 _6 ^& ^  z6 `        size_t j=0, handshake_len=strlen(handshake_str);' b  i/ \, O+ h  F# X3 h
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
( L6 g: S; j& @( y6 n) x; d' a        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
& E5 X' K9 \- M        // 这里的clientSocket就是连接好的socket对象了。  @4 r: v3 Y* T. {& `! g! k! U; t
        int sent = send(clientSocket, response, strlen(response), 0);
, Y6 k. x$ J8 B7 Y) @        return sent>0;, Q9 B/ I8 V3 s6 h7 l8 o; c
}8 |. A7 G4 I6 _$ W- |5 m4 }
目录结构:) H5 \/ H+ W/ z% I- D
socket/
8 T" B6 g2 _  K) [: M$ L      socket.c
, }) G7 k9 `- C3 ?      md5/: h# ]9 X# G# [5 c
        md5.h
3 K! L4 X  b1 I. D3 @7 M, v        md5.c9 S+ N. e( z: ?! u% O
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
' H% n5 ]3 _( v. _- Y! N" {编译命令:  gcc socket.c md5/md5.c -o socket.o  
  B1 W- Q2 T0 a$ l GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
; G2 j, t- z3 U1 V// socket.c 代码。* l8 `2 K# n) z
// web-socket example
# ^9 E- `9 l% R9 V# e! U3 b#include <stdio.h>* r, ~$ x8 z$ S# p7 g
#include <stdlib.h>- \% r( |. T) C
#include <string.h>; C, y/ b4 t' l6 X! O* c3 }/ w
#include <inttypes.h>8 R/ J+ o5 U! ^+ t
#include "md5/md5.h"1 `' a) s1 h3 _9 z. j
$ y9 A% t! M& F- Q
// #define BUFSIZ 512
  S- d, H& ~% m1 ]
( D7 x. ?3 h% f  R9 s$ [0 m% }//定义handshake结构体变量: _  t/ F9 _3 u( c4 z: m- Y! E4 b
struct handshake {
7 {! d0 k. N& q0 J5 j& R% H: t8 }2 E        char *resource;
, k5 T, s# {/ \4 O* W! h        char *host;
3 _2 q; Y, _4 h9 z4 Z7 H# @4 ]        char *origin;4 }3 J# |0 X0 x7 {
        char *protocol;2 l6 c, i/ {& E9 S8 a* a
        char *key1;& ?2 N  |+ z5 O; J7 B/ ?
        char *key2;. a/ y9 V2 ]* C' X. W
};. q% Z. D1 w/ V" K- m+ @
8 [( W. C# w3 m/ q' W

8 X3 @6 T5 @) e! F//释放握手后不再使用的部分变量* H! Y/ K, b+ r2 v" p. A: ?( k
void free_handshake(struct handshake* hs){% n; E  ]/ T1 }9 Y& I9 L
        if( hs->resource != NULL )      free(hs->resource);
& Z- p1 T  a7 g        if( hs->host != NULL )          free(hs->host);6 R- Q; J. _# G. ^
        if( hs->origin != NULL )        free(hs->origin);
& N7 S7 h) i+ l& h' v4 I        if( hs->protocol != NULL )      free(hs->protocol);3 l* w5 C0 M6 x* G) |2 V
        if( hs->key1 != NULL )          free(hs->key1);5 ~% h' R- Q! ], x3 T" C
        if( hs->key2 != NULL )          free(hs->key2);0 K4 I0 q, W1 `3 d: f7 G2 D( j
}" R! |2 A, F8 P7 \/ e4 f7 i; _

9 {+ U. r7 k; I+ W* u// 这里对上一篇的match_string做了点修改,
1 j! p* [* P- E" I// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理+ b& f) g3 L7 k7 _4 A
char* match_string(const char* src, const char* pattern, char end){
+ [% k$ k' |: n8 d        char buf[BUFSIZ];  M8 z4 E/ V% V6 Y: S
        memset(buf, 0, BUFSIZ);
' D* ?8 @: N+ j# C& B/ R2 Q2 L        size_t src_len = strlen(src);
! W! G5 |, F. C* v2 g        size_t ptn_len = strlen(pattern);
- Q$ ?* @. q8 p        unsigned short b=0, p=0, i=0;
- ?$ ~" L+ |1 y: N! y, w+ G) a  W        char c='\0';
8 s) D! o0 d7 Y- L0 X2 j9 I        for(i=0; i<src_len; i++){
8 w1 D  M, L- g. n2 G                c = src[i];
# O* j% U- M5 V" k* A$ Y4 |1 A3 C/ \                if(p==ptn_len){ // p==ptn_len 表示正在匹配中2 g: _; h6 f5 P( T
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束$ H4 Y/ Q+ z( k0 q- G! E7 W. v. L
                        else buf[b++]=c; // 匹配到的字符 * a* ^& O9 C/ E4 v( u' p3 d! x9 F
                }else if(p<ptn_len){ // 为达到匹配要求
& |* ^  q) X# a                        if(c==pattern[p]) p++;
( P& O- ~) p' X# F) H                        else p=0;
4 p4 s* M# T6 K# i6 R5 f, ^9 j                }
8 ]; p3 t9 K. \7 f( m: b0 g( F        }; v+ c, R& L9 L" j
        size_t ret_len = strlen(buf);
. x( l" ]$ E: X* S% f. r        char *ret_p;   X: _" v5 {$ n' j0 {
        if( ret_len>0 ){  F/ X& s6 x) g8 I# ?
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
/ p. ?9 j* n1 I* C/ g                memcpy(ret_p, buf, ret_len);
. H/ p& _- W; i+ D! l' |; Y2 A        }else ret_p = NULL;5 H7 c" j; {( H# y* e
        return ret_p;
; ^  @, k6 }8 y+ N: [}
. Q9 a5 H" B2 u3 g0 i. y; A) F3 x/ j( ~/ L# ~6 D
// md5 加密函数,用的是网上一个实现的比较通用的版本。1 _0 L, A( [+ |$ C! s) S
void md5(const char* src, size_t size, char* digest)
, i1 O9 H- d" H{( q5 d. U& O, j( R" I
        md5_state_t state;
* A* U" b7 a" w1 {* A2 ^        md5_init(&state);
/ u3 @& i; F0 P: ~, U% G        md5_append(&state,  src, size);
: Z3 A( {2 R, V- i6 ^( @) w        md5_finish(&state, digest);
7 h+ S' ?- R/ i8 W5 H; k. U( i}* l$ s. b0 i& n1 p
+ I5 @1 \' a0 [
void p(char*s, int len){! ?8 \# q- a5 y3 j7 T$ w$ v
        unsigned short i=0;9 C" @3 y. s. E! F' X, ~" i
        for(i=0; i<len; i++) printf("%c",s[i]);9 `( A" n/ A2 U0 X
        printf("%c", '\n');8 _" u" o: `- E
}
* j5 {. S/ [  U% a# ^' g/ V9 f( d. ?5 U! F/ Y
void handshake(const char* src, struct handshake* hs){
( z) D3 k! K* A  M8 [        size_t src_len  = strlen(src), i = 0 ;
  I* q* p" e4 T: ^; ?        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
3 Q2 ~9 e  g& j1 c5 l        hs->host                = match_string(src, "Host: ", '\0');
" k( q9 M, g+ m% T; {" t: }) _        hs->origin              = match_string(src, "Origin: ", '\0');
, h" d+ R; i0 a) G        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
% _/ I8 U: n1 ]/ B6 w( |        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');# S) }' F4 \/ K. k, c
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); 5 C* S0 g* ~' e4 b( ^
        // 获取 key3,即最后的8位字符* ]( h7 o6 b% s% ?* J
        char key3[8]="\0";1 l- F- K. X9 M# H% R, j
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
3 j. w% p+ b" w0 T8 V( y# R& c; s        char digits1[64]="\0", digits2[64]="\0", c='\0';
( f! o, O! N% x6 h        size_t spaces1 = 0, spaces2 = 0;/ T( A+ g$ @% w: u/ Y( O6 y
        size_t key1_len = strlen(hs->key1);
5 g9 m0 c% Z4 l        size_t key2_len = strlen(hs->key2);2 ]# j( l7 Z  k! j: R( V" b! G# c
        short d1 = 0, d2 = 0;) B) c: P- T# s: r2 S: Q: S/ l
        unsigned int result1, result2;
; H5 o0 `. C$ f/ P7 t3 M        for (i = 0; i < key1_len; i++){
' e4 T! d7 F! W# e                c = hs->key1[i];! a% Q& q  Y, G$ E% z$ V
                if (c == 0x20) spaces1++;3 u/ W5 z  H; l+ m3 |$ b
                else if(c>='0' && c<='9') digits1[d1++]=c;
3 T, w% |* X% W        }
1 k  V% L$ ~" H4 o, V        for (i = 0; i < key2_len; i++){ ! o6 s  ^1 ]  J0 K* Y
                c = hs->key2[i];
, Q- ?/ V% c4 `2 f* v2 e                if (c == 0x20) spaces2++;
8 U5 r! A7 E7 q: b6 k                else if(c>='0' && c<='9') digits2[d2++]=c;
: E. a; H2 }( m) k) `' @+ d' n9 I0 Y        }
3 b/ r: O- G8 w) h        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);7 j) m, r" P$ w5 y' b
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);1 ]6 ?( \9 J% C( q2 a4 f$ x
        printf("ch1:%s\nch2:%s\n",digits1, digits2);9 H) F0 E& }8 |1 ]) A5 v# c
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);; a0 A" e# i# F2 C2 f
        printf("d1:%d\nd2:%d\n"  ,result1, result2);
3 U3 G5 R% v9 N+ X        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";! h; x9 C' b; [1 g
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
* m2 k' {8 y9 v        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
3 u2 R$ C9 Q4 N4 T6 M% P) [8 N        printf("ch-key1:"); p(chrkey1,4);6 R9 T: i9 l" D4 \
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);% k* j* w& B+ {. @" ?) w( Y* o
        printf("ch-key2:"); p(chrkey2,4);
& o+ g$ e! x" i; C) }3 M8 x/ n        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
8 f" \% ]* A7 f& \        unsigned char raw[16]="\0", dig[16]="\0";5 H( w  K1 n: R  G/ X/ I1 V: o' s
        memcpy(raw, chrkey1, 4);& ?. a+ Z/ b- d7 ^
        memcpy(&raw[4], chrkey2, 4);
; N8 u. A5 R" c# F' f5 l1 A        memcpy(&raw[8], key3, 8);/ _1 \/ |2 }  J1 {3 L; d) _* x* d
        //计算的md5值# t6 U+ w1 [/ R2 ~- l  Q' r" I
        printf("\nraw:");0 N/ V" O* w8 W: ^
        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
; ]9 t) a: q  ?        md5(raw, 16, dig);! V- t$ X. H( A( i4 A4 T2 J
        printf("\nmd5:");0 M. I1 V% U5 X0 m' ]" N! p! _6 Z
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);) C( T9 r; n. r1 Z4 c
}6 A# \6 e) z, n* \% w5 n
/ i, n0 g" ?$ X- S& j* V
7 J4 _" |, P. u$ v1 G
int main()
; }& a( I& R  E1 G! x2 `+ t8 z{6 F' B- E3 e* U7 U; Z
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\4 l/ g: C1 o8 e( Z+ h
        Upgrade: WebSocket\r\n\
. O# h% W5 T. [- M- u9 i- h        Connection: Upgrade\r\n\
! {! ^9 b( |0 P9 |' f7 ^        Host: localhost:4400\r\n\! t7 K! Q( ?; X/ s
        Origin: null\r\n\
  V. a+ s0 ^$ l4 Z1 W        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\3 ]/ O& P1 W/ u! A" _3 u/ n
        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\$ Y- F. T7 ^: r" y7 C- |: E# N$ o; K
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";( t7 ^( y$ G# ^5 z& C3 l
        size_t len = strlen(msg);
9 ]2 A9 n0 m2 r! g4 Q( C% r* H0 e4 L        msg[len]  =0x1f;
3 q$ L9 t  T" F- E. ~/ W        msg[len+1]=0xf6;* u. v" O9 E- m9 {$ Q
        msg[len+2]=0xf3;
# y- S  P3 [& J" [        msg[len+3]=0x3f;
: S5 J! F  ~9 n8 B        msg[len+4]=0xc7;# g$ }# y8 v8 x( x% }4 l# M  [
        msg[len+5]=0x17;
- f& H) |# ^) H+ v# m8 D' }        msg[len+6]=0x20;9 T2 G$ z7 M( |$ J/ e' Y# B
        msg[len+7]=0x88;6 [' k! Y8 S! d9 t8 z4 A2 e
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
; E0 n! O! c& F  W: m9 {        handshake(msg, &hs);
* O. H: X' Z8 x5 z; h3 u        free_handshake(&hs);6 d1 g$ p; b# L1 F* o
        return 0;
# X, Q$ @4 J1 M8 T4 u0 ]) ~1 _}& A. L% Q: f/ x* ~4 ^, d

& V4 J" c- A! P2 B5 J' |" D( e0 `% L" ]# P
测试的结果:
. v) \! g" X$ L" R) |, `4 wraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
4 Z' C3 e: A& m/ A- R, R% kmd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 & N/ i3 w/ @$ R5 x. E
对比了nodejs的版本,握手部分生成没有错误。
2 c6 Z0 f! R. {" B' f9 t( q* s6 }
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-11-14 16:28 , Processed in 0.019774 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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