找回密码
 注册
搜索
查看: 5072|回复: 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++庞大的类库和模板。
& X* m. _6 t+ z" ]5 g! G完整的windows版本socket握手实现:
7 w; p) c; I7 d' [; R8 P6 N/ @: V+ C2 k' q
bool WebSocket::handshake(const char* src, struct handshake* hs){
8 ^% Y' c% _5 X0 u. L# n4 c4 f) _6 r        size_t src_len  = strlen(src), i = 0 ;- {' m% @! z; F  s1 y
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前0 c- ~, ?$ [5 B. T, X4 a" o. Q
        hs->host                = match_string(src, "Host: ", '\0');
5 [; @- m' M1 D8 L1 O- r        hs->origin              = match_string(src, "Origin: ", '\0');
2 p1 P/ w+ m  X7 W* M  e        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
* v& w8 m; |( S& U4 N        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
' v( ]4 w0 |; u        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); ) q6 K# w: t/ V: Y
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
* e9 p. t. w7 c0 ~( }' r        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; % ~7 O% Y/ V6 ?. `/ y$ P- U; Q( A+ S
        char digits1[64]="\0", digits2[64]="\0", c='\0';
6 Q# F9 z6 O( ?+ R        size_t spaces1 = 0, spaces2 = 0;4 {% m0 b7 E4 k  o$ m' H) v
        size_t key1_len = strlen(hs->key1);
: p: Y' G7 r6 A2 [( {        size_t key2_len = strlen(hs->key2);/ W+ u. [" p# M: Y3 v
        short d1 = 0, d2 = 0;8 K5 }$ Z0 A2 a! c
        unsigned int result1, result2;. Q5 l" V% h; b. |+ B1 t/ F
        for (i = 0; i < key1_len; i++){ " i) ]) _/ E! V* G& ?; Z
                c = hs->key1[i];2 w: v) `& v* {6 U3 ^
                if (c == 0x20) spaces1++;5 t  \# [9 f- V0 \& z2 L$ X! P
                else if(c>='0' && c<='9') digits1[d1++]=c;
! {+ I( i- ?: k        }, w( F" f# U( X: i' z( v3 \( o
        for (i = 0; i < key2_len; i++){ / A6 W0 p, {9 d3 ?1 n4 ]
                c = hs->key2[i];7 J' `. d- x  L# z# a5 _, H
                if (c == 0x20) spaces2++;  G- I6 `* e9 U3 q
                else if(c>='0' && c<='9') digits2[d2++]=c; ; @+ c; Z! s; p: |# O
        }, X5 o. R  m6 u$ P
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
5 f4 _; u; ]. e* h) O+ ^        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);. W4 ?, u3 i- ?1 @: r) w
        char chrkey1[4]="\0", chrkey2[4]="\0";
( ]3 `1 a9 g, V' x        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);$ K1 s* W: o" J. U4 D0 i3 ?0 k
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
& v- S% A7 q1 e7 h+ i& ?        unsigned char raw[16]="\0", dig[16]="\0";8 R  \9 t$ a# l' Q
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
! _' y5 f! ~. G        //  连接上key2的最后连接上头信息中的最后8位字符。! g( Y& m& K5 a5 d+ u
        memcpy(raw, chrkey1, 4);  |" d; ~( F6 J! N! a* F$ @
        memcpy(&raw[4], chrkey2, 4);3 P/ e- b& l9 a, K: V- M+ x+ L: ?
        memcpy(&raw[8], key3, 8);
7 q: T  ~; f9 P: v  ~        //计算的md5值
( y- X, L( A) R) Q- M' B6 s        md5_state_t state;
2 ?2 S/ }  X2 v' n+ |' ?# R  _        md5_init(&state);
: S9 B: G, ?, \( L        md5_append(&state, raw, 16);. H1 o4 O, J( d1 D8 E: x( N2 _
        md5_finish(&state, dig);
+ k/ g- G! W" O/ M4 e& W        
7 ?3 Y6 A% |# W1 h        char handshake_str[BUFSIZ];
6 T+ c' w0 ]: i" [8 L        memset(handshake_str, 0x00, BUFSIZ);% H4 F9 N  A! ^8 H
        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
% X/ i1 X+ l0 j8 s3 ?                "Upgrade: WebSocket\r\n"
7 ^6 L) N2 a: K# e. h                "Connection: Upgrade\r\n"& w1 R! A7 {: z- F( I, {! h+ a
                "Sec-WebSocket-Origin: %s\r\n" ) s' A( B) s& X5 @3 f) B- e
                "Sec-WebSocket-Location: ws://%s%s\r\n"
$ _" h7 ?- S% P7 ]7 N                "Sec-WebSocket-Protocol: %s\r\n\r\n";
- }9 V7 T& ^* b1 T! ]        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);
. H5 w  z; W) S* e        free_handshake(hs); // 释放handshake指针,已经不用了!' p$ q7 t' v  g; }& b
        char response[BUFSIZ];; k! g9 S( c. U) m* K% k" }
        memset(response,0,BUFSIZ);
- x! d8 o  v" p& w. I        size_t j=0, handshake_len=strlen(handshake_str);
5 b& G5 u0 I2 A        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];/ z6 C" C5 t2 T3 ]+ A
        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
" m0 Q* d7 j# h9 m: ^6 J# Q        // 这里的clientSocket就是连接好的socket对象了。- J& G# B+ l8 z" N
        int sent = send(clientSocket, response, strlen(response), 0);8 {2 D% |9 A6 j/ y% n9 @
        return sent>0;% G! @/ {( j! j5 l# D' \8 m8 C
}
4 T# s% \9 ^2 l1 C* Y目录结构:
( `5 m; @+ Q3 a" V1 l2 lsocket/
% Z$ r! W# R, q' ^3 C1 O! j1 E      socket.c! ^; z; a7 W, b, J+ Q/ \8 H$ _, h
      md5/% J0 }- N; h  i( U# E
        md5.h0 m0 x! a: i2 ]1 ~8 s4 T
        md5.c
6 @$ I( [$ r" V+ D1 U! `库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
' p% q+ V: }0 A5 r" Y6 R编译命令:  gcc socket.c md5/md5.c -o socket.o  # c6 f8 R4 }  f% w
GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。0 z( P+ S$ r6 _& E+ U$ e6 @, ^
// socket.c 代码。
: t% u6 @& w. E* x  \. i% a' m// web-socket example 6 F  Z: Q+ ]6 o6 c
#include <stdio.h>- k4 i5 i+ X( x
#include <stdlib.h>* [1 Z7 T& G# Q$ j$ H7 J: L  H% I
#include <string.h>
* T$ i+ ^) W: {. W- H) r1 S' x#include <inttypes.h>& l0 [$ E& }# e9 V9 d& @
#include "md5/md5.h"+ _  H7 b9 l9 B
+ g& J4 u4 E4 Q( P, ]
// #define BUFSIZ 512; v0 k: Q' u' d/ Z

, y' P5 W9 P% v9 g. `. @8 r//定义handshake结构体变量
& A7 h% ]# Z. v1 i) t7 Z5 \struct handshake {/ j" R0 X2 R, o! _
        char *resource;
  J  f. ?+ \; @% l1 h5 g, h        char *host;9 ^, R+ k2 ~" O
        char *origin;9 Y) y  H+ z' K+ j; ]1 t
        char *protocol;8 o% P3 X$ K4 z( J. i
        char *key1;5 [7 a* r& J' }# {
        char *key2;5 y% ^, v! z8 Y$ G8 j
};9 e+ `( j% \8 q6 ], ?+ ?
6 q+ Z2 o' t6 [* _4 d0 @" G

# u9 f" Q2 p7 S//释放握手后不再使用的部分变量
9 l9 x/ E& ?4 N- F# Uvoid free_handshake(struct handshake* hs){9 l0 T& J: R0 W* h
        if( hs->resource != NULL )      free(hs->resource);
7 S! J( v1 H* _! l9 s# k+ L+ M, Y6 c  `        if( hs->host != NULL )          free(hs->host);% x9 J2 q. \( S3 y0 F/ k
        if( hs->origin != NULL )        free(hs->origin);
3 `2 _7 _. G3 ^. `# S        if( hs->protocol != NULL )      free(hs->protocol);1 M3 M+ ~5 E* {+ j9 B& ~
        if( hs->key1 != NULL )          free(hs->key1);/ w: e' J; `! D
        if( hs->key2 != NULL )          free(hs->key2);$ K) w. N' d3 n! o% C  C4 V0 L
}
8 @' T% Q: `% X: |
; N, i6 M/ L3 N// 这里对上一篇的match_string做了点修改,
* ~6 c7 k/ ^* }// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
0 R  P& {# |& ?7 Echar* match_string(const char* src, const char* pattern, char end){6 s8 _! k% t1 _; c4 u
        char buf[BUFSIZ];
3 V6 v2 e! e6 V4 m- H        memset(buf, 0, BUFSIZ);$ E2 C$ y) e2 s
        size_t src_len = strlen(src);
6 b( h0 @7 P# x6 x1 `2 i: c+ S        size_t ptn_len = strlen(pattern);
8 r4 X6 J1 C& Q8 a4 O+ L8 A, j: U        unsigned short b=0, p=0, i=0; - G+ t' y8 ]* P
        char c='\0';+ Z3 K/ p0 @0 @" Z3 _4 e  I
        for(i=0; i<src_len; i++){7 q' t8 q( C  j" A, f- I  N
                c = src[i];- Y. j% a  |- `$ l) U' u2 ~" |
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
" R1 e' G8 Y3 f                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束  l- c2 P- ]5 L$ p# Z  u1 a0 N
                        else buf[b++]=c; // 匹配到的字符
! d* r% b$ M$ B3 T3 q, G7 ^9 k                }else if(p<ptn_len){ // 为达到匹配要求4 h; _9 H; C; [- y; z  M. T: o
                        if(c==pattern[p]) p++;3 w1 N. E6 F+ ^2 [( P  z. L
                        else p=0;
4 ~  ?9 _( g: c) V) t* j  s                }+ }: d4 h. E7 |, M
        }
6 B. n8 R- [, l8 L3 N% ]8 @& U( J        size_t ret_len = strlen(buf);2 y, q  C% v0 ?
        char *ret_p; 0 L* O: j1 J: P
        if( ret_len>0 ){% ^2 W+ v& Y, [  Q  Z. R; s
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
" D  H' T5 H) Q7 L$ }                memcpy(ret_p, buf, ret_len);
$ W; d+ k! V8 w. k, c        }else ret_p = NULL;
- x! r% |) s* V) ]        return ret_p;
: H1 ^  `5 I! `. A; V}& b  |# b1 i, |% P

% M3 u+ C" U; u/ o, H// md5 加密函数,用的是网上一个实现的比较通用的版本。4 N0 ~6 E$ S, i) J# n
void md5(const char* src, size_t size, char* digest)+ q$ u! b' M' h+ r  `; J
{9 O9 h0 n0 T& Z6 ]8 W
        md5_state_t state;1 N6 U9 F- h# @4 _8 j
        md5_init(&state);
" r. j/ ^2 u- ^        md5_append(&state,  src, size);$ T7 Q1 s2 F7 ~3 ^" ^' J  \! V
        md5_finish(&state, digest);
4 a$ j6 I$ `* w# C: q}
: N4 N: x" x8 R5 y
& g+ J' k/ s$ P- n6 j& zvoid p(char*s, int len){3 J  |' z, u, [/ E9 S8 \7 [3 `
        unsigned short i=0;: v0 p" Q8 ?2 W7 }1 @0 `
        for(i=0; i<len; i++) printf("%c",s[i]);& B' h! t( q: u5 a# H: v" u
        printf("%c", '\n');' A) Y- f  o- U' ?1 v, W) u
}! @. @# n7 O8 o" q4 L9 ?  _% I
' U) W2 ~7 D: |* \
void handshake(const char* src, struct handshake* hs){: m5 A! L  M) n6 v6 D+ u
        size_t src_len  = strlen(src), i = 0 ;* T' d' ?! X: L8 G2 N5 m
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前( g8 u$ U- b3 A+ p/ R% c
        hs->host                = match_string(src, "Host: ", '\0');
+ j4 k4 ?* {) d, o! B7 B: R1 `        hs->origin              = match_string(src, "Origin: ", '\0');
, l+ [: u) j1 [        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');) q* J9 D  i+ ]  s: `( i
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');( v$ ~! O! Z" G4 v1 e
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); . B; m) j" V7 h& h. H' \9 x
        // 获取 key3,即最后的8位字符
. K- {" U5 Y. t& ]/ o3 X        char key3[8]="\0";) {  `' M" t# m- h2 g& J
        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];   ^+ X( N* a0 j6 S* r6 P
        char digits1[64]="\0", digits2[64]="\0", c='\0';
: b8 E0 U/ x) u1 o, X& [        size_t spaces1 = 0, spaces2 = 0;
/ m" w3 K9 v/ Z" J+ A# q        size_t key1_len = strlen(hs->key1);5 k7 H: G( v" \, g! [) y
        size_t key2_len = strlen(hs->key2);
* [! s! b, o' X; g9 T; r# B6 g        short d1 = 0, d2 = 0;1 |* l2 t8 U  ^8 i( A8 m- F0 e3 h
        unsigned int result1, result2;  [" e9 i8 O* A" F7 m
        for (i = 0; i < key1_len; i++){ & B5 m, q& J$ @7 y
                c = hs->key1[i];
; Z" s; x8 R6 {6 l$ a  Q4 \: G: {                if (c == 0x20) spaces1++;$ e1 u# |' u% _* |% Q
                else if(c>='0' && c<='9') digits1[d1++]=c;
& T3 A8 `5 _. U: n- f4 `7 n" z        }
0 s# V0 K7 T) G0 J        for (i = 0; i < key2_len; i++){
2 t0 g" n- g) V7 L) p% R9 I                c = hs->key2[i];" J7 r, z8 `% F7 M# o. L
                if (c == 0x20) spaces2++;
; M5 t( W, ]" G5 x6 l" I: b$ T                else if(c>='0' && c<='9') digits2[d2++]=c;
/ D3 _+ w" C0 W* D% @! E- x        }
4 f# y, i# c" N+ z3 q/ v        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);+ E( P* {- l2 d: j2 K
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
! i" ^& Y8 T3 O+ C3 p7 w- I5 R        printf("ch1:%s\nch2:%s\n",digits1, digits2);
  l6 M7 |0 G; k' L! r) V  c        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);/ E# O1 E0 [$ d' J) F
        printf("d1:%d\nd2:%d\n"  ,result1, result2);
) s3 I7 L% y  s        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";& N3 r5 L% |: F; g
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
- b. Q- c/ \6 Y# @% v7 C+ |7 J) l        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
0 X% E4 |* }! _/ ?4 o6 z* ?        printf("ch-key1:"); p(chrkey1,4);: L4 i- h# {2 b, V0 B; _
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);* k! O$ z: ~" K
        printf("ch-key2:"); p(chrkey2,4);
* T# Z6 D+ i& B3 y* ~$ q& `  y. p) W        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
$ n7 [4 W4 ^, Q; {/ n        unsigned char raw[16]="\0", dig[16]="\0";
* r" w; ^8 |4 m0 D( X        memcpy(raw, chrkey1, 4);3 J) R- l7 T! M, l
        memcpy(&raw[4], chrkey2, 4);- B, H- S; ~7 d2 s$ V4 v3 q3 a
        memcpy(&raw[8], key3, 8);' g& {8 B% f, M7 v$ D
        //计算的md5值/ r& A$ q: }  Y% D2 H8 Z$ p7 T
        printf("\nraw:");
- p$ G  ^  C8 ^  Y+ u+ A        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);
# b# |3 C( @5 H+ C* \9 d0 V! D        md5(raw, 16, dig);
" X4 P% o6 d4 ?5 Z        printf("\nmd5:");
1 E2 o  W7 ^  S3 S; k* ?        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
/ d! W1 A; x. q0 p}
$ d0 ^0 b( l0 e5 `5 S9 e2 G4 T
. T2 A( Q9 g9 i! E' g$ ^. y" I
$ n' U, F' I, @/ v  a% oint main()# r5 Q3 {! r/ E9 S+ Z
{4 c/ H2 g5 ^4 M6 K
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\$ |% s: L3 |) B
        Upgrade: WebSocket\r\n\
0 p# Q+ y- F6 z  S" t; d& E        Connection: Upgrade\r\n\4 X6 U+ {2 I) f, }  W, {- @
        Host: localhost:4400\r\n\) q$ m2 J& u" Q
        Origin: null\r\n\
9 T/ O/ \# N8 q: N5 M' `7 Z        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\
* @$ R, N0 f5 A9 D- ~* b" H        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
6 `( s# X* |: g6 U/ L        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";
2 Z( D) {0 j4 `        size_t len = strlen(msg);
. k* `0 T# N  T  g7 l. }4 S: D        msg[len]  =0x1f;
$ o) \, |3 t: P& S- R* q        msg[len+1]=0xf6;
" x* \$ j& ?( C5 w! {. o2 ~        msg[len+2]=0xf3;
" p0 @9 p; Y. G( @0 J        msg[len+3]=0x3f;, z" j8 `" o& M: f& m: S
        msg[len+4]=0xc7;
9 I5 x8 q, J' y; k        msg[len+5]=0x17;& i+ R0 h6 c' r# j' t
        msg[len+6]=0x20;: e# X" h$ Q  g
        msg[len+7]=0x88;- }- E; h6 w' v; u( _* E
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};
$ ^! \$ Q% a) k        handshake(msg, &hs);
$ K9 y: M& E+ i) l" n9 c        free_handshake(&hs);; N. G3 D- ]8 N; v  K
        return 0; 7 c  p0 f2 B1 f: y* p7 D1 S) S
}
$ p8 H0 H- |8 r, s1 T; s! |8 v: {, H' Y! w- V1 `; [' t

9 d$ V1 l' r6 `2 z测试的结果:
8 D9 R0 G1 _5 f; X  W. ~3 vraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
- P; n: Z% K9 K. |/ |* Wmd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 $ h% G" W/ v: j; Q( q& Q  i8 _
对比了nodejs的版本,握手部分生成没有错误。1 B: J: b( i& u1 ^
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-6-20 10:56 , Processed in 0.043113 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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