找回密码
 注册
搜索
查看: 5351|回复: 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++庞大的类库和模板。
4 k/ {) `! G: a& H# G完整的windows版本socket握手实现:) m! M( o7 l3 `$ ~/ p
1 Q3 Q  M5 A2 k& l8 K: W4 g
bool WebSocket::handshake(const char* src, struct handshake* hs){  z, c8 j& v8 V. }; o
        size_t src_len  = strlen(src), i = 0 ;
2 x0 P* p8 _/ D/ k$ C! }        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
6 }. `- c* Z- |8 O; Y- S5 A% F        hs->host                = match_string(src, "Host: ", '\0');1 x3 A& ~# U3 s+ p8 H
        hs->origin              = match_string(src, "Origin: ", '\0');
7 i$ `/ P; e" C3 f6 J/ A& A  v        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
8 m' J6 o7 p( r+ @4 w' j; n        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
% f# W8 _, q! v- L        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); ; q* a9 N0 i4 e6 n
        char key3[8]="\0"; // 获取 key3,即最后的8位字符
! u1 i. Y: l: F8 q1 I8 l5 ]- v8 B        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i]; 9 E8 M' m# l. b/ s( M4 o
        char digits1[64]="\0", digits2[64]="\0", c='\0';
. U6 b* m, }7 V; H' J) ?" y/ \' D        size_t spaces1 = 0, spaces2 = 0;
% q8 _+ L1 a3 |  t        size_t key1_len = strlen(hs->key1);
9 U7 m/ o! z$ ^" L2 B        size_t key2_len = strlen(hs->key2);. f* I/ Q/ P; I6 G6 k. f
        short d1 = 0, d2 = 0;
' Z+ g# t# Z; f! u8 g9 B        unsigned int result1, result2;; d) t/ M4 F, r  b# n! B
        for (i = 0; i < key1_len; i++){
' i* I1 e( H8 h, t6 h; D. v                c = hs->key1[i];4 @1 `4 }1 D* {/ @: R) H) u! }8 q. r
                if (c == 0x20) spaces1++;
( A) F8 {! {0 i5 M                else if(c>='0' && c<='9') digits1[d1++]=c;
; X8 f. M& T; r5 `- l        }
$ B4 y7 |+ T4 S) [* `        for (i = 0; i < key2_len; i++){
$ p" [, ~# Q6 Q) |0 a- `                c = hs->key2[i];  S6 J& Y6 ^& X3 [) X
                if (c == 0x20) spaces2++;( e$ j- C- a( Q; k5 S; R
                else if(c>='0' && c<='9') digits2[d2++]=c;
  B4 {5 O- S3 m        }
5 m4 P; T. q6 ~2 I  F& C, V        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
- c; E  c! P$ G9 g: @        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
2 y( i( ~: V7 o  G        char chrkey1[4]="\0", chrkey2[4]="\0";
8 b( H% ]. Z" j2 N" [3 g        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
4 N! w# b4 H# T4 D4 a        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
" i6 G' n* l) f6 G        unsigned char raw[16]="\0", dig[16]="\0";% F- f, P, A, H% M% G
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,
  V" J9 o* o# g        //  连接上key2的最后连接上头信息中的最后8位字符。
3 Q8 Q8 ~, s. X! W1 _6 q        memcpy(raw, chrkey1, 4);
7 L! ?( j: g6 l: @        memcpy(&raw[4], chrkey2, 4);; X0 _* z. [+ }  V, R
        memcpy(&raw[8], key3, 8);0 R# Q; {; T- z
        //计算的md5值
# `* Z4 l% V) |3 `" w7 o        md5_state_t state;
- I4 \( S5 N+ n) |        md5_init(&state);
/ P5 X* l: s) E: k: @: z/ p5 n  @        md5_append(&state, raw, 16);
2 M, H0 B, {( N3 ?8 x; F        md5_finish(&state, dig);
5 i7 [  H7 ^( ?/ l        # v& @& n6 y1 {+ a1 ~, t
        char handshake_str[BUFSIZ];
, |1 t! S+ S5 X; [9 c        memset(handshake_str, 0x00, BUFSIZ);
( Y& y+ E6 ?8 U' Y+ X" t        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"( \& ?6 o$ [$ X, `# A. s
                "Upgrade: WebSocket\r\n"0 I/ S, e7 ]: `
                "Connection: Upgrade\r\n"
% n0 q9 u4 H; V& n: ^8 Z9 p                "Sec-WebSocket-Origin: %s\r\n" . X* _2 C: b, N! `$ r/ }# w
                "Sec-WebSocket-Location: ws://%s%s\r\n": w2 b: j- ?( a# J' y/ p
                "Sec-WebSocket-Protocol: %s\r\n\r\n";
! S- v8 G5 q% t        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);1 l: q" M1 A1 H! o- ~% d  O
        free_handshake(hs); // 释放handshake指针,已经不用了!9 _: x5 ]% Z" s
        char response[BUFSIZ];
- E$ E& f8 r9 D/ |/ m4 i& |. a        memset(response,0,BUFSIZ);1 L3 s3 W- Q% |0 o- W1 v
        size_t j=0, handshake_len=strlen(handshake_str);  a4 n, l9 L( |- M* @2 k
        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
% L6 m  _7 y2 h: t" r. ~0 x6 |) N( J        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
" b- c) s+ e' a: R        // 这里的clientSocket就是连接好的socket对象了。
  \* X) H" n( h# l/ K5 N9 m        int sent = send(clientSocket, response, strlen(response), 0);
7 l0 A2 c# h7 M5 B: Q4 G        return sent>0;4 J; u1 v- P$ i# y" }
}
( m6 w! @) N3 r' K& ?; q( A目录结构:
2 S- G  N$ M; M( G$ ksocket/
; g, |% R$ q& k6 b      socket.c4 F$ S: j3 z# ~6 W1 u# h
      md5/
6 X: s4 o! b/ n, B3 S        md5.h. w! L6 n8 Y+ ^# w6 h) r
        md5.c
( Z" r& e. H7 X# W' r7 |  K库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,) r  f, W  X4 R! f
编译命令:  gcc socket.c md5/md5.c -o socket.o  
6 ]8 X8 Z9 Z7 c) k0 a' P- b2 Y, l GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
1 q4 i7 y/ W  B% \- U: @3 l$ |$ I; @// socket.c 代码。
; z/ C+ y, ~7 ~' ]8 Z// web-socket example
& x4 ~6 v) p$ P3 L- z: ~, Q/ O#include <stdio.h>7 T3 r- J+ V4 [8 b& J$ U  Y
#include <stdlib.h>
  h8 P8 w( t  _7 S8 e( e#include <string.h>
6 J' Z% h8 j: o7 E& C: _#include <inttypes.h>3 z& q" U7 [* v1 [4 w  F. D/ c4 [
#include "md5/md5.h"
4 `) E- c; K2 H8 r0 @$ l# S" @( h4 o# R# |0 l1 Q) {/ o
// #define BUFSIZ 512
# v6 Y* [2 d9 Q, L. R
9 f2 v0 }/ n6 ?4 [//定义handshake结构体变量7 b9 ~' e* S. H6 \' V
struct handshake {& W% }  e3 Z, M: c" g
        char *resource;
' j+ L3 ^4 X( U+ }$ ]2 ?, M" V        char *host;
+ J8 J+ s" ~" l. t/ L) O/ K# C        char *origin;; M( a" F; q1 U$ l4 \" [- o
        char *protocol;% [+ \( s/ S. U+ h
        char *key1;; ^# ~" e/ j' T
        char *key2;4 `* U! j, a  Z' C2 d
};! B: `: N  [6 D2 N4 M  }# c; m
9 U5 b) V6 T; ^" g

( {" y7 z# T( n5 G0 Z$ X9 E  H3 z8 z//释放握手后不再使用的部分变量9 k: i# D* J4 e0 R: k- J+ |% x& Q9 D
void free_handshake(struct handshake* hs){
# L- r& t8 V3 N3 b. i        if( hs->resource != NULL )      free(hs->resource);
% D( X7 G% Q% T; [: ~* G% i+ q        if( hs->host != NULL )          free(hs->host);: b4 G. }5 L8 @
        if( hs->origin != NULL )        free(hs->origin);
4 \& w3 C# ^" V. H  K% v9 e# {        if( hs->protocol != NULL )      free(hs->protocol);
$ K, R9 m7 v0 e( l, |4 Z5 B7 y+ S        if( hs->key1 != NULL )          free(hs->key1);
0 r2 q, c: p6 d3 C% Z/ @. w! i  d        if( hs->key2 != NULL )          free(hs->key2);' j& S; q3 y3 I" ]
}' K) V# a: M: k1 f
; S% w- J4 N6 v! r$ V( h
// 这里对上一篇的match_string做了点修改,
# g+ Y5 s# y- T' i" n// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
# r+ o  N7 A" g" v+ V+ \char* match_string(const char* src, const char* pattern, char end){+ L- N+ ]7 w4 D7 V6 b. P
        char buf[BUFSIZ];
# k, L$ X) V: O3 ~        memset(buf, 0, BUFSIZ);
2 Z( g5 B3 J) s. t8 ]        size_t src_len = strlen(src);
7 Z4 ^8 p4 Z$ a! ~+ E        size_t ptn_len = strlen(pattern);3 y# `7 l- j# h/ W: Z
        unsigned short b=0, p=0, i=0;
4 m# F& g& B3 i3 q        char c='\0';
: u* n/ q7 i  A        for(i=0; i<src_len; i++){
. r0 J( M! I* w5 N' X                c = src[i];& D, D! B# c5 i! S0 H0 f
                if(p==ptn_len){ // p==ptn_len 表示正在匹配中7 x5 A/ |3 L' G' E; ~. G
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
0 k' H3 [+ M/ ]8 D7 f9 \                        else buf[b++]=c; // 匹配到的字符
  |+ ]; [# g4 R2 G                }else if(p<ptn_len){ // 为达到匹配要求
/ E; G  T4 d8 p# ~# z8 X; g                        if(c==pattern[p]) p++;6 d% B: ?+ @. o5 z+ k+ j5 d; x+ o1 E
                        else p=0;1 \, j4 J$ ~, }2 I
                }6 y6 G) t, v7 t1 _0 [# a
        }
- z  v* w4 u! R  T+ I1 U& w7 ]        size_t ret_len = strlen(buf);' w: X, p( ]! F) i! k; |
        char *ret_p; / L0 A8 S) v- {2 b! m/ t8 i
        if( ret_len>0 ){
8 ~6 {  p( w$ p                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'
' q$ `1 \. l* ?0 M                memcpy(ret_p, buf, ret_len);
! w3 C8 d. ]: S( r        }else ret_p = NULL;1 ^5 ?; J  ]# g1 L. P
        return ret_p; & F! g1 j( r% r' |# P0 ?9 _
}
- ?  N% U9 P# c; a9 {/ b! l$ O0 D$ y& p# M/ S2 @$ a5 K
// md5 加密函数,用的是网上一个实现的比较通用的版本。
, x& K2 [. i) D5 u0 I, tvoid md5(const char* src, size_t size, char* digest). n7 K: G) r7 p: ~) K
{! Y6 P% o/ O! A. ~
        md5_state_t state;
. s3 c! |; C+ s' A% |6 a$ b" h+ J6 i        md5_init(&state);& |8 J  f; r8 j( {+ _
        md5_append(&state,  src, size);
" o# \$ W% y% ^# }        md5_finish(&state, digest);
# z; U' }1 W, q, M' g9 W}) u* ?! |+ ]3 b/ V1 ~/ R  G2 ^4 H
$ e3 d" U, ]  Y0 Q( Y% X
void p(char*s, int len){- `, T4 a3 B/ z9 y
        unsigned short i=0;$ J# [4 U5 V4 c
        for(i=0; i<len; i++) printf("%c",s[i]);9 @. x+ t+ b& }- {" S: F
        printf("%c", '\n');
2 p6 U( b- o/ N9 f) F6 ^1 }}
( y% Z8 m% X. J  F
+ P, t- |2 t/ T/ hvoid handshake(const char* src, struct handshake* hs){
0 _6 A) i2 i  Q* W5 M, I        size_t src_len  = strlen(src), i = 0 ;2 D- M/ N0 a, {9 O) t5 s
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
& J8 z; H/ A3 d, V3 @( |        hs->host                = match_string(src, "Host: ", '\0');
* G% [  L  g! a  r* M/ `' [3 h) z        hs->origin              = match_string(src, "Origin: ", '\0');
! q0 D1 z0 R) w  J% W$ W  L        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
$ Q" B0 t0 A. h8 K9 V/ d        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
- G) b5 L  }/ t. |. n8 O: _! O        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); 7 J+ F  R: z+ _( A' T+ ]+ i
        // 获取 key3,即最后的8位字符) E8 S+ r- U! n# A
        char key3[8]="\0";
  r- _" U2 |1 x  |5 \$ `0 i        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i]; 9 V3 M8 ~$ `$ M0 E
        char digits1[64]="\0", digits2[64]="\0", c='\0';' W: G' V/ U+ k1 C  y
        size_t spaces1 = 0, spaces2 = 0;+ C* C# Y, w3 h+ U& W
        size_t key1_len = strlen(hs->key1);/ s! n- o7 z' }4 Q" v
        size_t key2_len = strlen(hs->key2);
8 Z, K7 l+ N& S# d+ l        short d1 = 0, d2 = 0;
) _' J, G: }. S* a* T        unsigned int result1, result2;  Y0 l; G  ?, V+ R
        for (i = 0; i < key1_len; i++){ 6 `# _/ s3 X" h# l
                c = hs->key1[i];/ U7 X6 |+ h/ u3 L7 g( e9 U# _
                if (c == 0x20) spaces1++;/ P/ m+ C' i2 Y" S0 q7 M$ \3 A
                else if(c>='0' && c<='9') digits1[d1++]=c;
: y' F$ J9 N0 N) a; q7 {8 [        }
- |9 j5 k) ?4 U  K$ C- n& p# z        for (i = 0; i < key2_len; i++){
8 s: V# x: Q7 ]4 S$ H# S6 v                c = hs->key2[i];  I7 T: |! J1 j! C' G
                if (c == 0x20) spaces2++;& F9 R% e& |% i+ O& I5 V" v2 A6 f! z
                else if(c>='0' && c<='9') digits2[d2++]=c; 3 e9 w( [! J% J  B3 H
        }; }- ?" |# F  e) L1 O" U
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
( j( Y) O8 H8 k8 O( _        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
, s  J% C6 R7 r0 v9 B        printf("ch1:%s\nch2:%s\n",digits1, digits2);
- h7 R5 X3 E9 O2 u  g/ _        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
+ J! V, L3 ?6 E8 c5 r        printf("d1:%d\nd2:%d\n"  ,result1, result2);
! O! r2 H* N2 h: h! l        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
/ c/ k; I; k( Z9 _; `5 l% C        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);3 y+ M! s9 ^( Y: |6 e( [
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);
  e1 o! Q9 L( f        printf("ch-key1:"); p(chrkey1,4);0 J" N8 \! X& ^; I4 f& c+ n" Y
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);0 x/ U- q: y( d6 ~4 x$ D' J  ]
        printf("ch-key2:"); p(chrkey2,4);
5 g) X% a! t( b        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
1 q9 M  w* y$ V# I  x5 M5 [        unsigned char raw[16]="\0", dig[16]="\0";
; Z% J  u( ~' U7 l5 @! _( s. W& ?        memcpy(raw, chrkey1, 4);$ K% S; g: z2 _
        memcpy(&raw[4], chrkey2, 4);3 W9 O: r- j/ h; R5 F
        memcpy(&raw[8], key3, 8);
( D6 @; A* y$ Y" n' C        //计算的md5值/ u9 r1 d) {* i3 n
        printf("\nraw:");
) j& {; Y& B1 y5 J* L: t        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);5 u, n" }9 ]8 m5 O3 m6 N: k0 k2 g
        md5(raw, 16, dig);* w, n4 t5 t6 J9 D/ X
        printf("\nmd5:");
( H7 t/ S1 f) E        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
! l0 F0 B. d0 s2 Q: i}
0 i4 ?7 Z5 o5 \) G. F, h( _
! D- F) t9 a: ?0 g
; t! l9 Y$ I+ zint main()
3 S7 C: e( n) H+ d( [9 c5 `{
5 m5 f& G5 @( y* L6 I6 x1 A        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\0 l: R0 E, K5 J! }& g7 x
        Upgrade: WebSocket\r\n\
5 g4 b) u8 c9 P5 a3 O% J8 s        Connection: Upgrade\r\n\
4 U* ~2 G1 v8 N$ Q( L) C& B        Host: localhost:4400\r\n\
) j  C! l2 w7 L- S- [+ C0 J        Origin: null\r\n\  i: [' o, H$ H* y- r- N
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\0 ^5 p0 a  a; \% H) V. d% O3 N
        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\
" p7 C9 {$ \: e0 w$ m8 g! I0 u* n% y        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";  Q7 n9 i1 A" q# z
        size_t len = strlen(msg);
4 O/ j  G/ m6 G% j/ ^        msg[len]  =0x1f;, L# W2 y9 Y% Y. o' A5 H
        msg[len+1]=0xf6;( Y, F* p; P5 y: @- i; r" {& X
        msg[len+2]=0xf3;
% w( b3 `0 X. K( k: Q: J        msg[len+3]=0x3f;
: O$ w( S8 _; H( o        msg[len+4]=0xc7;! m6 o5 r& z* `! I. [
        msg[len+5]=0x17;
  Q. u: \7 E3 x& ?9 I$ c" Z        msg[len+6]=0x20;
2 `1 w% I0 b! w! Q5 P        msg[len+7]=0x88;6 Z! b7 h7 s5 R$ c6 k
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};" J* M) `% k! Z9 C
        handshake(msg, &hs);  m) Z# b0 i' F
        free_handshake(&hs);, O2 k, L0 o; X: Y' c+ J: e+ U
        return 0;
) |! n2 V1 R' M: s  u5 ^$ U}6 r3 @* Q) c2 w, \% q" \# p3 C
. X2 W" A7 m/ B1 n: R0 j2 s
0 o7 N( e" ^. u7 h! q0 K
测试的结果:
5 s: z, F  F) o# H2 A1 Zraw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
; W# t' g2 O9 Imd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 0 z+ |# \7 x" `# N- r
对比了nodejs的版本,握手部分生成没有错误。
0 R* w4 M, k1 x  K$ m
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2025-12-30 02:19 , Processed in 0.022674 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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