找回密码
 注册
查看: 5836|回复: 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++庞大的类库和模板。% {3 [6 p6 l5 K/ Y
完整的windows版本socket握手实现:
! {3 k* e( R2 X! q, i' B( l- x  B8 _3 a+ \& O) r4 r. b
bool WebSocket::handshake(const char* src, struct handshake* hs){: L; w& `0 n7 `# Q7 W% {9 @
        size_t src_len  = strlen(src), i = 0 ;, m- C; x4 e! Q/ q* |" ^' o
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
& i, }& X* ^% t% v5 O        hs->host                = match_string(src, "Host: ", '\0');
5 Q; P  |8 R+ p: c        hs->origin              = match_string(src, "Origin: ", '\0');' w4 \' ~4 D" L: `3 t4 h- u
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');
" [8 y2 n. x; v        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
, R% V0 J# `2 J+ i        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
  P1 ?% E$ [- J6 G        char key3[8]="\0"; // 获取 key3,即最后的8位字符6 [3 I" U# G* A: r. _/ J
        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
9 y) ]8 y7 |% B. a5 z% z( _        char digits1[64]="\0", digits2[64]="\0", c='\0';: A9 j9 s% t% }& ~6 c" O, K
        size_t spaces1 = 0, spaces2 = 0;
/ G% n/ |. l* T% j        size_t key1_len = strlen(hs->key1);2 U3 Y$ E. H7 o+ Z' P' P
        size_t key2_len = strlen(hs->key2);) S$ L# Y2 B5 E
        short d1 = 0, d2 = 0;
. o% f' H: E. g' ^# L$ }& p        unsigned int result1, result2;
+ J! {$ ]+ f: y9 f/ l% z. {; K- a        for (i = 0; i < key1_len; i++){
) X# w9 ]  v. f1 E                c = hs->key1[i];6 A1 I) G+ \, P6 M( S
                if (c == 0x20) spaces1++;- _" b( y- u0 i9 _- A
                else if(c>='0' && c<='9') digits1[d1++]=c;
2 O1 Z5 g. m: N# V$ [" g+ K/ v        }5 m) y9 C+ m+ T8 q: H! w) B& G
        for (i = 0; i < key2_len; i++){
+ l7 j3 \+ D6 E                c = hs->key2[i];
4 ]) O5 c+ B! I2 h. D                if (c == 0x20) spaces2++;2 t% V0 ]/ {5 p. H7 h
                else if(c>='0' && c<='9') digits2[d2++]=c;
$ F% R" o& }, e        }2 V7 k8 ?! V: n# ^8 F7 d
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);+ R! V' f* t( `$ Q  X0 w, E2 o$ A* {
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
" R, B7 m3 x! F        char chrkey1[4]="\0", chrkey2[4]="\0";
/ T# j/ E1 `/ X. @5 A& w        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
9 ~3 H8 l0 `; a/ r, K, d# o% A        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);" y, D: E+ ?5 J8 O, y" W' a0 {
        unsigned char raw[16]="\0", dig[16]="\0";
+ @5 [" u4 x  B; c        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,  M  _+ Z; T) e$ Z4 M, |4 b
        //  连接上key2的最后连接上头信息中的最后8位字符。
: F$ z( Z% L) v$ F1 ?        memcpy(raw, chrkey1, 4);1 h, O1 u  s3 x) E; n8 n
        memcpy(&raw[4], chrkey2, 4);
; V) z% ?& @6 V        memcpy(&raw[8], key3, 8);
0 w! U; f& y4 G5 Q# g        //计算的md5值
# L7 y* h1 R) T8 r& A5 \  p; T        md5_state_t state;
, I0 h8 ]- R$ r1 {7 ?        md5_init(&state);
  d' _/ F: o  ]( w( K) b6 F- N9 k6 b/ o        md5_append(&state, raw, 16);
$ u+ R4 k0 h# p* t, s( _/ q        md5_finish(&state, dig);
/ Y$ w1 X( v! X4 U* I        
4 I  E% W& y( l6 W5 F* Q        char handshake_str[BUFSIZ];5 l1 q, P* y! t3 }* ^: i, W
        memset(handshake_str, 0x00, BUFSIZ);; m- d4 I5 z' r* Y
        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"9 @/ D: G) w, T. _) S5 [  s# r5 E
                "Upgrade: WebSocket\r\n"
9 [* U1 T- q0 L: e; h1 q                "Connection: Upgrade\r\n"
/ G+ D2 o$ x1 M7 e8 z                "Sec-WebSocket-Origin: %s\r\n" & `8 [5 n  O- r+ g  L4 C$ T/ m
                "Sec-WebSocket-Location: ws://%s%s\r\n"
1 ~# Y7 @6 {3 U$ m- ~% o                "Sec-WebSocket-Protocol: %s\r\n\r\n";
: z" E1 k3 Y7 ?4 r1 O1 P; q. B        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);0 s0 ?8 {) t$ B" s1 D6 S5 i' y, ]
        free_handshake(hs); // 释放handshake指针,已经不用了!1 ]" G2 d! d3 i0 r) e& V$ x
        char response[BUFSIZ];( e( z5 u2 u! M, @: `- E  p4 _% M
        memset(response,0,BUFSIZ);; j* C- J, N' z) J
        size_t j=0, handshake_len=strlen(handshake_str);
$ Q. N& J6 w  ~! h* d        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];
7 I4 S; \6 _2 ~# a3 H  G        for (j = 0; j < 16; i++, j++) response[i] = dig[j];
$ q! P2 m, g9 E6 K        // 这里的clientSocket就是连接好的socket对象了。
7 k& v  Z, b# Q  k/ e        int sent = send(clientSocket, response, strlen(response), 0);  ^. d$ W* t/ G: m8 w7 e
        return sent>0;" I2 ?& D5 T% D" t4 j
}
0 j* v$ K% v- E$ m8 J目录结构:
1 L6 u2 t/ ?. e7 k$ l  U4 k" a" Msocket/
* o4 Y- O% u2 ~) D      socket.c7 j# R- f# X6 A( T% v
      md5/
' V. n: _6 _: D9 i+ p        md5.h
8 X/ l# g; F3 E        md5.c3 ^* C% Z' _$ [$ H& z) w3 S
库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
2 b& K- z" i) Z' Q1 O' }编译命令:  gcc socket.c md5/md5.c -o socket.o  
! h! V/ ^; n3 k3 E- H GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。$ {# R+ E" U4 S4 w3 `9 f
// socket.c 代码。
* S' X! {9 ?/ h% F4 H// web-socket example
# e+ Y: u4 o" C#include <stdio.h>/ Q$ y8 P" C, _* K
#include <stdlib.h>1 k/ ~/ x6 @  l+ J: M6 H) r
#include <string.h>
! f3 M+ M: q# Q3 K9 X- ]# p7 U#include <inttypes.h>, s$ P6 S1 h' m. h) |! c8 @
#include "md5/md5.h"- o$ `. C! f9 ~+ F. H

1 E. c6 y2 z$ J- k7 Q* u# f// #define BUFSIZ 512
: e& ?/ z! K2 ]% K% p, F5 M, h3 T& a& c) z. e/ [1 |' ?  T2 P5 S
//定义handshake结构体变量. }6 I* L, ^2 p
struct handshake {
; f9 H6 |, [. S' f+ n- s        char *resource;
8 Z4 Q( T/ ~9 f        char *host;- v" {3 }7 H& v8 B/ {5 k
        char *origin;
$ a* w3 U7 H6 o        char *protocol;
: _" g2 Q/ `: u. u' J+ K        char *key1;
4 `0 f' m, \- l( p' n5 U" g, T/ k        char *key2;0 G6 f% C: @2 a6 t$ b  o
};; Q0 @% R1 h  I' X0 t# {" O$ T; K

- d' G# q1 i/ }  `3 k& C) j+ A8 }, a* d. t/ ^) N5 p# M' p3 I
//释放握手后不再使用的部分变量
) s' D, V5 ]' [. i8 U  @# hvoid free_handshake(struct handshake* hs){  O( H) f" c0 D4 T3 F( T4 m
        if( hs->resource != NULL )      free(hs->resource);) q' U/ g; Y  N' A) h7 V
        if( hs->host != NULL )          free(hs->host);' d1 F, ^7 ]8 J+ c& U7 M
        if( hs->origin != NULL )        free(hs->origin);
$ b7 F$ \* r' G) N7 m        if( hs->protocol != NULL )      free(hs->protocol);* K/ Q1 s( {: z1 D) n
        if( hs->key1 != NULL )          free(hs->key1);
6 `( h& c# [" k        if( hs->key2 != NULL )          free(hs->key2);5 G# ]  P6 s: g# i
}3 A; l) T6 d" ^4 {& v

' `. j2 w& y3 v, s2 U- [3 [9 G// 这里对上一篇的match_string做了点修改,
5 E2 X3 I  I6 F( u5 w// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
3 N/ m; {5 o4 I2 L! @4 j7 ^char* match_string(const char* src, const char* pattern, char end){+ F/ P' V9 c3 t
        char buf[BUFSIZ];
+ d! C2 H9 c2 A        memset(buf, 0, BUFSIZ);
6 g& B, Q" n9 F1 s( y        size_t src_len = strlen(src);
! C: D9 e" N) e4 a- a        size_t ptn_len = strlen(pattern);6 z9 {0 }! M0 i$ f; ~# N' j  }; |
        unsigned short b=0, p=0, i=0;
, B+ t8 U) D! N2 j% `$ z; Y        char c='\0';1 e: G& f( e2 ~; A6 e: |3 D3 p
        for(i=0; i<src_len; i++){
9 P9 c2 _; P; Z7 H) Q+ P                c = src[i];
. ~, E/ E, B% M+ z; r2 r                if(p==ptn_len){ // p==ptn_len 表示正在匹配中
% G) A6 C( [0 [& U0 ~                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束9 {  x. v) F( U: Z2 @7 f$ q
                        else buf[b++]=c; // 匹配到的字符
1 B0 J" }& N+ ^& |                }else if(p<ptn_len){ // 为达到匹配要求$ L3 u9 A% n' H/ v0 Y# f' I
                        if(c==pattern[p]) p++;+ m3 t$ a( N6 V5 Z# k% H1 _; b% f, c% y
                        else p=0;
( E5 t# s  _2 R0 O5 Z# m8 y2 o  x                }5 Q: U/ U4 R6 g2 X0 e) P  |
        }
  V1 k3 G! r, X" H3 D        size_t ret_len = strlen(buf);3 b8 g5 X5 d$ x& q
        char *ret_p; % i2 V& u3 ~, H4 w  r7 W1 Y% _
        if( ret_len>0 ){- g2 p  m/ S" {, C
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'. O6 u: E( ]7 s' X* {% I. q6 l
                memcpy(ret_p, buf, ret_len);
6 B2 g) V8 Y6 B7 H. I$ n! R        }else ret_p = NULL;3 G3 C8 G# C5 s' _
        return ret_p; ( W) \/ i5 [& Z! f0 Q7 V+ p
}0 m% m7 v' }! c' @* n, i
6 f- y8 B) q  t# t) `3 V4 z( R6 j# x
// md5 加密函数,用的是网上一个实现的比较通用的版本。; c* s) A; `" N6 L# ?* k$ r  D" M
void md5(const char* src, size_t size, char* digest)0 Q' m9 s2 z+ Y* |" s5 t
{
/ n5 g7 e! x. L        md5_state_t state;0 [, t+ o/ L# @# s& X) l  r% l
        md5_init(&state);7 V" ]# Z$ q6 k, F- O0 k
        md5_append(&state,  src, size);+ j, v3 S' }; v! M- j2 c
        md5_finish(&state, digest);
: w' u+ q% i. B6 ~9 g}
2 J  b! T+ D1 ]2 y2 O/ `8 \" t$ g$ y" p% ]
void p(char*s, int len){
6 o) |7 B) m  W& n        unsigned short i=0;
* ?0 a4 t% z6 s% ^% M# m7 a        for(i=0; i<len; i++) printf("%c",s[i]);/ S* J6 r2 Z7 }/ t( N
        printf("%c", '\n');( T& i) P) I) r5 M
}" ~$ F0 u* {7 F. n& m/ j, R  h; r) O
( b7 i. q6 D5 k
void handshake(const char* src, struct handshake* hs){) D3 f  y3 L- O" a
        size_t src_len  = strlen(src), i = 0 ;
) C( T& [; E: n& F$ M& k6 b. r        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
7 ~) J4 j) I  g# C' L6 r7 P9 B        hs->host                = match_string(src, "Host: ", '\0');
' W/ z% |- V6 t' N& D* p        hs->origin              = match_string(src, "Origin: ", '\0');
& p- ~/ y# q7 H- N( e# l        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');) t+ s7 ?* U0 i" d
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');9 B/ Y; _* h6 X# w* ~
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
  [6 q- Z$ @5 n7 Y5 e, U        // 获取 key3,即最后的8位字符) [0 q% B9 M% p0 G  I
        char key3[8]="\0";
2 y1 e- i8 X2 u6 T        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
+ W, j! O. H4 d9 x7 v; j        char digits1[64]="\0", digits2[64]="\0", c='\0';+ ^0 t) T& h7 _8 p
        size_t spaces1 = 0, spaces2 = 0;
9 z& Z4 I; G7 e1 s% x- U        size_t key1_len = strlen(hs->key1);
1 o: D* y& ?. M( `        size_t key2_len = strlen(hs->key2);) k) O: c! D! B+ F0 C' A
        short d1 = 0, d2 = 0;
0 t0 T3 T2 o5 o. h8 d  v2 O        unsigned int result1, result2;
; B- j7 P$ L. h2 }( F' }5 y        for (i = 0; i < key1_len; i++){
5 P, V; f1 z0 M+ s* {4 I/ L6 C% S* V                c = hs->key1[i];
$ Y1 i5 b, Q) R2 ]$ I                if (c == 0x20) spaces1++;8 T  X. U3 Q' w) W* L. w
                else if(c>='0' && c<='9') digits1[d1++]=c;
4 a( D, z5 a3 M6 u        }
, M! J, X" d! @6 I2 X9 W) t        for (i = 0; i < key2_len; i++){ ) @8 G0 Z" J/ t
                c = hs->key2[i];
$ n' `/ q& ]. I0 h                if (c == 0x20) spaces2++;
0 X+ m% D! x' X$ o0 S- b                else if(c>='0' && c<='9') digits2[d2++]=c; ) ]/ E  z, K* ~+ g1 ]* x
        }
; l+ C- o$ M$ f, i. F, a        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
, f  L' R9 B8 d1 y9 U) @: j1 m        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);
( U: y% h# s' d1 z        printf("ch1:%s\nch2:%s\n",digits1, digits2);
% N* @0 a% U0 m6 d+ B        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
: Y& r- {  p4 F3 Q# i: n        printf("d1:%d\nd2:%d\n"  ,result1, result2);
' B: X7 S3 h& ]& K8 O5 h        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";
1 O, A& g7 a- j. q9 O! D        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
0 x( l  {& U4 B" d9 {        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);) p' m( @+ V8 D
        printf("ch-key1:"); p(chrkey1,4);
: ~2 v$ u* O* v: f        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
9 @4 {7 X. {6 \7 @        printf("ch-key2:"); p(chrkey2,4);- t2 M9 B2 v- e( ^( s
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);, r" J6 R- L( i1 K. `$ D
        unsigned char raw[16]="\0", dig[16]="\0";
: l  Q' ~$ E+ M3 M! Q& o6 S        memcpy(raw, chrkey1, 4);+ e0 O3 z& U  t* p. x
        memcpy(&raw[4], chrkey2, 4);+ [+ n) G/ P" g
        memcpy(&raw[8], key3, 8);
0 t) L5 |# L+ D8 p9 a  @2 `( @        //计算的md5值
. q, e4 k- v0 i) N        printf("\nraw:");
$ }  m# P6 ^: j$ m; j! B( @0 o        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);' M) Z( `1 |# e$ F: d, C' G1 Z; H  A. P5 G* ]
        md5(raw, 16, dig);
4 Q4 N; _+ u8 J- L, H( W' T        printf("\nmd5:");
  r# w. X& v4 O/ s        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
, [# N$ [& ]0 w}1 a1 r9 M$ Q. T0 O- ~5 Q
4 s0 b, s& W, K/ E
6 K$ }! d% c0 a" d0 T
int main()# d. a5 u% _- b* x) d: J) c
{/ w) B+ j' v" C7 {$ x4 S
        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\3 {4 ?7 U  G- b* ^$ z9 ~
        Upgrade: WebSocket\r\n\
2 o+ U* ]* q3 \2 B$ F/ v! V! W        Connection: Upgrade\r\n\  T/ Q+ T/ L3 v& ^$ O& b
        Host: localhost:4400\r\n\- x- i( R7 U! }% g* N: G4 @
        Origin: null\r\n\0 k3 ^! {, J2 ~! l: V
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\. W. D5 o* ~. V: J6 }
        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\$ D" p1 P0 B: C
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";
3 D. n5 b/ O7 N2 h  s        size_t len = strlen(msg);$ s- e& T: g8 @% Z' a2 j: m! u
        msg[len]  =0x1f;3 V" L3 B3 y* f2 D5 ^+ v
        msg[len+1]=0xf6;
& Q9 f5 a; I+ i4 D5 N, O- U7 o        msg[len+2]=0xf3;
9 v: r$ u( E' k3 Q- W        msg[len+3]=0x3f;8 s, O) B. L- e, Z4 I
        msg[len+4]=0xc7;% o, r8 P0 b( D9 P7 f
        msg[len+5]=0x17;
* b# N+ C. {  D( h) b        msg[len+6]=0x20;% m: n7 Z6 H$ f5 o; i% i. ~! |
        msg[len+7]=0x88;
) o8 L9 i+ r& h  ?5 S        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};/ Y- I& w' j0 U* X7 |2 s* B. p! [
        handshake(msg, &hs);
3 y5 m- k) q, n' v        free_handshake(&hs);
7 t) I* m- ]4 a        return 0; 7 h) w4 F- C, j3 j/ o/ l
}$ \; O; q. @0 A. k" I, Q% X: ~; o
" [1 }; S6 B' Z1 E5 w3 {3 r

. n* j- m9 M) R' \, }2 J$ Y1 m测试的结果:3 \! Q7 u: v! H9 U8 k: S4 H7 g
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
6 E: X3 w- C/ l! I3 hmd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68
' y! X2 L: T; B: g" E9 [对比了nodejs的版本,握手部分生成没有错误。3 j& f2 X5 `6 f/ x- n
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-10-1 19:27 , Processed in 0.018072 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

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