找回密码
 注册
搜索
查看: 5348|回复: 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++庞大的类库和模板。
% H8 h+ f, m) b& R  y+ D6 {+ {完整的windows版本socket握手实现:$ e- a8 A) L( n& G3 [

; Y) X4 e$ o- p3 o( e8 b# i, L+ ^bool WebSocket::handshake(const char* src, struct handshake* hs){
6 F( {1 x4 g: w( e5 S& W        size_t src_len  = strlen(src), i = 0 ;" c* ?/ E  \4 [' ]
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
7 W7 |  K2 J' q        hs->host                = match_string(src, "Host: ", '\0');3 r, q2 P1 y/ D: o7 D! U
        hs->origin              = match_string(src, "Origin: ", '\0');# l5 r! ^9 {- U9 a+ g# |
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');5 d5 e$ E  i8 ^
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
* j9 D2 {, v' P* j$ G        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); " @2 t& R" D1 S. Y" b
        char key3[8]="\0"; // 获取 key3,即最后的8位字符  R  M" |2 }! ^) i$ R  O4 ]
        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; 9 M( [( g0 j: Z) w, }: ?; {
        char digits1[64]="\0", digits2[64]="\0", c='\0';; F/ k, y) X6 R# v0 ~
        size_t spaces1 = 0, spaces2 = 0;
8 J: {$ a! l1 k        size_t key1_len = strlen(hs->key1);
9 O" \/ k$ R/ R! X        size_t key2_len = strlen(hs->key2);: U+ C$ U# ^$ K$ @8 p
        short d1 = 0, d2 = 0;
( C' f4 `& y9 @+ p$ X        unsigned int result1, result2;
2 w" y/ f) e) e* n7 u        for (i = 0; i < key1_len; i++){
3 s# ~! T, m' {  P/ K) d$ u                c = hs->key1[i];/ N0 Z, t$ A7 g  F7 l
                if (c == 0x20) spaces1++;
8 ?2 P! w6 S5 n  [* ~                else if(c>='0' && c<='9') digits1[d1++]=c;
. H: J+ T, E7 L% P        }
& p# t+ n+ ^* p  l5 H% z2 M- g        for (i = 0; i < key2_len; i++){
- W) u" \' `+ j                c = hs->key2[i];
1 Z2 M# l: [& v3 C7 c                if (c == 0x20) spaces2++;2 r2 ]  Y/ J9 Y$ ]4 R
                else if(c>='0' && c<='9') digits2[d2++]=c;
- E. F/ `7 T+ q( m4 q0 i        }" _8 t' [  a% w0 N  @1 {# g6 X
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);' [0 O: s/ h' f3 O
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);7 ?" [3 N0 h5 w" [& ?- ^
        char chrkey1[4]="\0", chrkey2[4]="\0";
4 F, R& ?) |8 f( m        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
/ e- _; |, E! x3 M1 L7 W: @6 i. B        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);( q3 E# M! N3 w' }. N
        unsigned char raw[16]="\0", dig[16]="\0";6 `& _, q2 H% Z
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,0 y; z3 U6 B1 p- \# \3 A
        //  连接上key2的最后连接上头信息中的最后8位字符。
& D9 j  {  y8 J1 W$ D        memcpy(raw, chrkey1, 4);
3 N) V( N3 n9 F3 Z% H* q* ]- g        memcpy(&raw[4], chrkey2, 4);
$ Q( Z6 G. x% O  p. S/ |9 U2 U        memcpy(&raw[8], key3, 8);2 s* V" |# Z  r( Q4 u
        //计算的md5值
+ w# C: z# g7 E, Q1 m% v        md5_state_t state;
* E; s3 M) V) K6 d5 y% l+ [        md5_init(&state);; R1 s- p: A# W0 J
        md5_append(&state, raw, 16);  j6 M; p) Z* R/ U: t
        md5_finish(&state, dig);
# x* p0 v8 N7 t        ; n$ Q, y2 s* s
        char handshake_str[BUFSIZ];" i, l4 O( I- R" A  S! U* H, k
        memset(handshake_str, 0x00, BUFSIZ);  R6 J  m% Z: w% H8 g! E2 J! {
        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
$ l, `( b! F1 k+ L( o7 a+ C                "Upgrade: WebSocket\r\n") d4 B3 v# S4 @9 K9 H5 a' a+ Q2 P6 H
                "Connection: Upgrade\r\n"
: f. T0 l- W6 j# z3 ]0 y                "Sec-WebSocket-Origin: %s\r\n"
' h, X* r% Y0 |" |' j1 j9 H                "Sec-WebSocket-Location: ws://%s%s\r\n"0 c$ y) V5 M1 C6 c- b) b, y' T
                "Sec-WebSocket-Protocol: %s\r\n\r\n";
( i- z& W: c8 q. Y1 {/ E. X/ w        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
. w6 a: [7 V  v9 G2 R: ^        free_handshake(hs); // 释放handshake指针,已经不用了!7 Q, N/ `7 J; j7 N; Q
        char response[BUFSIZ];
# ?0 M7 `) g+ h8 L        memset(response,0,BUFSIZ);2 M1 W0 A$ H/ K
        size_t j=0, handshake_len=strlen(handshake_str);1 X9 N' _0 Y- {0 C& l7 r
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];3 A" I# F- x8 v3 h
        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
. e; I1 h. {/ R+ \8 n, T) p        // 这里的clientSocket就是连接好的socket对象了。
8 k) j0 Q6 U6 o' K' F        int sent = send(clientSocket, response, strlen(response), 0);! ^7 @+ H. d. R
        return sent>0;, R. ]" [, ]3 e. U0 @% Z8 |) V
}
; p2 _! l# l9 e目录结构:
7 a& r9 Q% G0 Q. q: ]socket// X6 S  F9 R# ^: a: k
      socket.c! Q0 Z8 t/ f9 d0 M  w) b) u6 F7 d
      md5/
) `; S4 I  g" B        md5.h: K, p, i, A# ?0 v; h( ^
        md5.c4 H- {- Z7 V' a" }3 J
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,8 ?* A, j* v; \( J2 P$ `2 z+ y
编译命令:  gcc socket.c md5/md5.c -o socket.o  
4 n4 F( G; Q5 M GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。! W! P- x$ `* }0 _  ], @9 S4 ?2 _
// socket.c 代码。% r! ~( \+ a& D
// web-socket example 8 g5 H1 X, r, J7 _3 ?, L
#include <stdio.h>! p( `# W* v$ A- Z, w7 l' {" }7 [
#include <stdlib.h>
! C6 K3 Q/ F5 u1 L" K- j#include <string.h>
$ x$ c# _" J( T0 D7 j#include <inttypes.h>
& y( W2 A* \: q#include "md5/md5.h"
1 V9 H/ v4 q7 R7 }% ^& [) ], j# B, O. s% ^; J, U  `: E9 u
// #define BUFSIZ 512; X1 Q$ _! V+ j: c

0 b4 k% `  V  r& |  I% L//定义handshake结构体变量
3 K" H8 w9 i' O# S- v! j4 dstruct handshake {. T5 f- Z/ V4 O  G6 G* O. T
        char *resource;
6 o4 K2 t! ~" s' `        char *host;
& ]/ \4 u# I. m/ J- j        char *origin;3 n. w- @/ ~! h2 o3 i1 R3 S) `
        char *protocol;  b! h$ \# f2 v4 ^7 V7 J& H
        char *key1;, c% K7 u" v8 v8 u7 \
        char *key2;
4 B  O! }6 Z- a};
/ T' P5 B1 q: @7 c9 e9 y. f/ \7 @9 W. p9 e$ m! S+ n1 d$ \8 v

+ z6 A( S/ {( G) K, Z1 i//释放握手后不再使用的部分变量) ?1 t- u6 u0 ?) ?8 }+ \
void free_handshake(struct handshake* hs){' L2 z- ~$ q5 w! [. [* r
        if( hs->resource != NULL )      free(hs->resource);
+ P; {3 g: V" M( a, W" k1 d        if( hs->host != NULL )          free(hs->host);( o/ Q7 E7 R% y2 z3 a. w4 A/ W
        if( hs->origin != NULL )        free(hs->origin);7 V3 a6 m$ x; p/ X
        if( hs->protocol != NULL )      free(hs->protocol);# z6 Y5 c( _) o2 b' D3 y  b
        if( hs->key1 != NULL )          free(hs->key1);
. z1 G7 M, U$ w' n$ T$ o        if( hs->key2 != NULL )          free(hs->key2);
! y+ t, k9 Q$ X; Q0 M}
# m" t- k* ^4 j7 u9 u5 x# K+ l
5 S; S3 o# p# m: i// 这里对上一篇的match_string做了点修改,% {3 K6 O- w1 s+ ~  M  k  i1 j/ g
// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
# r9 R$ s- `7 o: v$ Gchar* match_string(const char* src, const char* pattern, char end){  Q9 G' c# _: m" q0 h1 {' F2 N
        char buf[BUFSIZ];
9 R1 ?& s$ K) ?) _  f% r        memset(buf, 0, BUFSIZ);
# q1 U, [7 w- g2 ]. j        size_t src_len = strlen(src);
$ v+ j- x" m9 P0 O$ e$ q        size_t ptn_len = strlen(pattern);# J: Q) h  U* t$ f$ {. D
        unsigned short b=0, p=0, i=0; % n6 g6 O3 b  i4 `3 |
        char c='\0';; k* i! x0 o8 z3 |& x4 Q
        for(i=0; i<src_len; i++){
' X; Y, b3 ], N( j. l                c = src[i];% _8 f/ E6 n3 _* a& B
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
( N& O: N! }8 ], J$ H+ b* |                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
$ \4 @2 C! V6 Z                        else buf[b++]=c; // 匹配到的字符 5 X+ H4 h: j( {9 D% R- h) M9 V" z
                }else if(p<ptn_len){ // 为达到匹配要求2 p: r0 G% m, h& ?3 S" E, S/ Q* C3 F1 N
                        if(c==pattern[p]) p++;- k9 H: j4 ?! F* t
                        else p=0;
' Q, F7 d6 e' _0 `1 Q0 l7 E                }
4 y9 I/ D" j, n& E' o- B        }3 _$ p6 \+ C/ v* d; U2 n
        size_t ret_len = strlen(buf);
, @" [: N& c0 ]% ]  V; b+ J8 X, A        char *ret_p;
$ E+ E% A- f  M% N  T& X        if( ret_len>0 ){# O: V, a* \, z! Z2 [
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0': ?; y' ^! [* {! H4 A; S
                memcpy(ret_p, buf, ret_len);9 B0 V' G+ a. f! J' i# N$ I
        }else ret_p = NULL;3 M% X* s+ j* k/ r
        return ret_p;
" h2 C" _8 }& F2 e6 T& }3 o}1 R% R  b( v- m8 B
' M+ V! S* d) G. D7 W1 ~
// md5 加密函数,用的是网上一个实现的比较通用的版本。
) Q+ _2 p3 ~" L' @" rvoid md5(const char* src, size_t size, char* digest)0 X3 B$ \' L! I
{* Q+ n: \. y5 Z4 E
        md5_state_t state;
* Q. U/ }, v% h- k" |- T        md5_init(&state);6 N9 Z; m, d& T3 b2 D$ G% o
        md5_append(&state,  src, size);6 O8 Z  F# T0 J  ~; w- N  Q# M
        md5_finish(&state, digest);
; h0 p; a" j7 {, M! K}
" Y! z+ i% U7 r7 X$ _; I0 q& x/ a1 I7 ^  U, }5 e% k  T
void p(char*s, int len){
( Y0 v7 K" Z" E" A1 V# p( ]        unsigned short i=0;
8 ]3 [; R/ v/ ~  h. B7 U( W# v$ C  T        for(i=0; i<len; i++) printf("%c",s[i]);
) Y3 j: o$ a- Z# r/ _% G        printf("%c", '\n');
/ a2 a' K6 U+ b* z9 k, ~! _}
1 J- s9 Z  M+ a% H' M
$ [3 V- O+ ]8 h/ o: }5 Y8 }void handshake(const char* src, struct handshake* hs){
, a& X, G) f+ O  F# o; Q        size_t src_len  = strlen(src), i = 0 ;
  M- ^' i% f0 N$ o- _9 Y        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
1 T5 U$ s5 X1 _        hs->host                = match_string(src, "Host: ", '\0');7 y* F0 F8 F" O3 x
        hs->origin              = match_string(src, "Origin: ", '\0');4 _5 i4 C# }; k* Z0 F! w8 K
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');+ U  @! @- e7 T1 b- `8 S2 |
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
( f8 s1 i& s4 ?' k& B2 Z) \; {        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); ' y/ d. _) x! [9 G
        // 获取 key3,即最后的8位字符
( K- N9 v) Z5 X9 n1 B        char key3[8]="\0";+ }7 Z% a- Y4 f$ g( B: i- r8 m
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
9 m* x: ~: }7 D* c+ N        char digits1[64]="\0", digits2[64]="\0", c='\0';
# T9 u9 H0 a' {6 x& i2 q* p        size_t spaces1 = 0, spaces2 = 0;
8 S! x+ R0 h1 j. t; @        size_t key1_len = strlen(hs->key1);
" V) o/ o' n" Z) w7 [2 E6 ?* _4 o        size_t key2_len = strlen(hs->key2);
; E( ^0 [( N( K, J( V8 H1 g        short d1 = 0, d2 = 0;* a5 f1 n" B- z$ I
        unsigned int result1, result2;0 [% O/ ~6 C7 ~/ N
        for (i = 0; i < key1_len; i++){
# ^7 p1 X  e  X! i6 K                c = hs->key1[i];
: t. V2 b" p- z3 D, U1 |  y% u                if (c == 0x20) spaces1++;
/ c& ~' E3 f, G1 D8 A, |# W                else if(c>='0' && c<='9') digits1[d1++]=c;
: A7 m- U8 t- w1 T- q% ?+ G' G' J        }
( s4 k, d" H# L% h  x1 g        for (i = 0; i < key2_len; i++){ 7 v4 D" y2 U+ E0 a; t$ q0 U5 c! U3 B
                c = hs->key2[i];
9 O; p. c) e9 w  \! ~                if (c == 0x20) spaces2++;2 _0 _6 X+ A, G6 A, o
                else if(c>='0' && c<='9') digits2[d2++]=c;
3 x) H# h& c6 |: d/ ?. v5 H: S. r9 G        }% g; d' q2 {5 V4 i$ l4 |
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);( \' z+ W6 e7 g* k
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);4 T. G) V2 O  N4 ~. T& v
        printf("ch1:%s\nch2:%s\n",digits1, digits2);+ }# q- x! w" N3 z4 ]9 l
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
6 u1 X) D7 j3 d8 O5 l        printf("d1:%d\nd2:%d\n"  ,result1, result2); : ^* T5 s' z, j. s! ?& y& [7 F* x
        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
2 l3 `5 \3 o, `        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
- x8 Y7 k& s, ]- g6 }: F        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);+ k2 f7 K! b5 u, I/ B; x
        printf("ch-key1:"); p(chrkey1,4);% G0 H- o6 O9 g2 k2 g) X
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
6 z, U" l( o( r4 }' x9 b4 B        printf("ch-key2:"); p(chrkey2,4);
2 K; o  W! I. _5 R: G        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);+ M3 A7 @1 V4 K/ w
        unsigned char raw[16]="\0", dig[16]="\0";4 W& K9 E. G1 O( C; e2 N$ E7 w" g; L
        memcpy(raw, chrkey1, 4);0 W( U7 Z5 n  j( }* r2 n
        memcpy(&raw[4], chrkey2, 4);3 b6 {  C( W( O- X9 U& b
        memcpy(&raw[8], key3, 8);% x; x& u# l8 G/ y8 f/ d  R
        //计算的md5值$ j+ l% W+ @' H6 v- E( t8 ]) {% }
        printf("\nraw:");" V) }7 H  l, l1 o' i
        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);3 N6 Q$ F3 P, C! e% F* W8 y
        md5(raw, 16, dig);7 ~4 W9 \( U/ C4 j* w3 C* c* E
        printf("\nmd5:");( s9 m3 {& X2 o4 t' E5 I
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
' `' ~" B1 Q' y}
8 L- T8 w/ ~8 s0 U1 l, X6 L* N9 ]: w. u5 L5 c0 A" J- J
5 J) B1 T0 N. e# O
int main()
; w  o. |8 l$ k9 j3 j4 @{
  J* h$ ?" T/ n$ i0 z8 C- }1 J        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\: Z3 \+ |$ I4 }1 o
        Upgrade: WebSocket\r\n\* Z. ]0 u) W! O% y9 U
        Connection: Upgrade\r\n\# p7 k/ m7 }2 E
        Host: localhost:4400\r\n\7 t( n( R7 C8 y& l2 B# V/ ~
        Origin: null\r\n\
9 p/ O: e$ }  r  W% }        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
; {  R% T/ J" {6 B: u1 v3 X        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\7 D) z$ x' I! h, k. y7 L6 V* M+ H
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";6 ^: V# g( e& }6 F7 K7 }# u
        size_t len = strlen(msg);
; ^' |* n! v# t: r( X; `/ N- Y        msg[len]  =0x1f;
# D/ t, s0 ]4 _' {5 _0 m# k        msg[len+1]=0xf6;  e, T( B" ^! w$ k: ~
        msg[len+2]=0xf3;
( G4 n* X. Q5 F        msg[len+3]=0x3f;
* ^; p5 X9 h  d0 l9 G        msg[len+4]=0xc7;, L' {' J5 S4 N
        msg[len+5]=0x17;) G1 \$ I  `/ w: V$ Y; {, w$ ]0 ?$ Y
        msg[len+6]=0x20;  A# ]. w6 B7 e% T7 \8 c
        msg[len+7]=0x88;
0 M3 r/ T4 n% C6 Q8 C: {8 u4 a        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};# C( i% U, ^) X
        handshake(msg, &hs);
( O3 A  d* E* S3 x* b8 Z1 R9 H) P        free_handshake(&hs);& J# D7 S) o- u: J& H  f
        return 0; 8 ]; o4 o7 h4 U2 v+ M
}' `) V$ n& w, @# U
. }7 Y! X& f6 `/ o
! `8 i! n5 A$ z( c" {
测试的结果:! Q; ]4 @, u9 I. v( i" S8 y3 o# m
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88 ; v3 _2 m" h' |
md5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68
  R- P) P2 |% x$ P8 Q$ g- b7 C, a0 z对比了nodejs的版本,握手部分生成没有错误。4 U7 V- ~  j3 a' l
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-12-29 19:34 , Processed in 0.030784 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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