找回密码
 注册
搜索
查看: 5349|回复: 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++庞大的类库和模板。
6 q. m! Y+ z0 j+ o完整的windows版本socket握手实现:( ^- }5 Q3 g9 _9 R

6 ]3 T) g7 o0 I! bbool WebSocket::handshake(const char* src, struct handshake* hs){
% `2 t5 Z& P3 j: D3 R        size_t src_len  = strlen(src), i = 0 ;
( p7 W2 r6 P( D        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
+ I# H+ Y) E  |        hs->host                = match_string(src, "Host: ", '\0');
0 p$ r; Q. u7 C/ o, K        hs->origin              = match_string(src, "Origin: ", '\0');" Q% ^3 S* \" p% {. h7 b9 E7 i* ^
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');, ]' x7 K% m; H
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');. S  i+ I5 _: Z& j" p+ W2 z$ R
        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0');
: o: h  _! ]: G3 y/ C& l        char key3[8]="\0"; // 获取 key3,即最后的8位字符
# F* }4 g9 u8 W        for (i = 0; i < 8; i++) key3[i] = src[src_len-8+i];
* G4 \7 J8 B! `& c5 p3 R# q        char digits1[64]="\0", digits2[64]="\0", c='\0';' g) P$ ~3 ^7 Y  ]) K
        size_t spaces1 = 0, spaces2 = 0;) E! `! h. w  ~2 z* o) [& Q. h/ f
        size_t key1_len = strlen(hs->key1);" \9 t5 X9 j- G
        size_t key2_len = strlen(hs->key2);8 w; t, W, e/ u, T. u7 M
        short d1 = 0, d2 = 0;$ ?- C& ^: R+ J4 Z; K) B- i0 u
        unsigned int result1, result2;
/ A9 N4 V; G. m/ u        for (i = 0; i < key1_len; i++){
+ [7 `9 u. K5 Y& u# q( k. k/ [# i                c = hs->key1[i];3 c  C5 B  z7 h1 |- D9 g
                if (c == 0x20) spaces1++;
+ G! I. Z3 A$ G8 u                else if(c>='0' && c<='9') digits1[d1++]=c; ) s7 ~5 _% X$ P  U) Z
        }
+ x$ I4 A+ ]0 C9 D        for (i = 0; i < key2_len; i++){ & _/ J9 f5 V% {
                c = hs->key2[i];
8 f- Q. Z3 W" Q* `                if (c == 0x20) spaces2++;: m( i" r, k- S# e0 E, V1 |3 `
                else if(c>='0' && c<='9') digits2[d2++]=c; # T! t) a3 Y6 [, _( F6 K  _
        }
1 j, W  T% m6 n        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);- W" W5 |+ U' T& q
        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);4 J- ?" p/ A3 }" G3 k
        char chrkey1[4]="\0", chrkey2[4]="\0";# E1 e- D) K9 M2 ~
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);
4 W! w/ F1 R+ U( @2 A7 h, j        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);6 I0 V. w7 d  d- o2 l
        unsigned char raw[16]="\0", dig[16]="\0";" S2 p0 h! M2 w1 \4 I
        // raw 表示未md5之前的字符串,规则就是前4位key1中的数字/空格数的整数值,( F# o( e5 J' c6 S$ r$ ^& G% f* j( \
        //  连接上key2的最后连接上头信息中的最后8位字符。& a$ R8 g: E3 _6 _/ \
        memcpy(raw, chrkey1, 4);; d4 A* `& O! U; v3 v6 ^8 H% [: ~
        memcpy(&raw[4], chrkey2, 4);
6 i/ W  [4 r9 \8 V& k8 V( B2 e        memcpy(&raw[8], key3, 8);
* u4 i9 L1 N/ q" P6 W        //计算的md5值
5 I5 ~4 f$ A" ]% c3 I1 H! M* Y/ Z        md5_state_t state;9 M7 x/ q- d: j$ e2 P
        md5_init(&state);5 x0 \0 L3 @0 A; B' v
        md5_append(&state, raw, 16);
1 y$ [3 a6 N1 }( M1 K; a; Y$ m6 |/ h; ~        md5_finish(&state, dig);' \- X8 |$ Q! _; T) d5 Q% j
        + D+ ?$ k, F: }
        char handshake_str[BUFSIZ];; h6 j" u4 F# m1 X2 B: {/ N
        memset(handshake_str, 0x00, BUFSIZ);$ S( j' X8 b! T7 \5 N
        char* handshakeFormat = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"9 u# V# V, _3 q
                "Upgrade: WebSocket\r\n"
9 r. w* }/ J9 j: P8 Y/ S2 K                "Connection: Upgrade\r\n"
* T. a! x  A$ h                "Sec-WebSocket-Origin: %s\r\n" 6 R# H' L; X; z* K! P
                "Sec-WebSocket-Location: ws://%s%s\r\n": @+ c5 N9 N  u/ y
                "Sec-WebSocket-Protocol: %s\r\n\r\n";
9 w3 q/ M  W2 j% ^        sprintf(handshake_str, handshakeFormat, hs->origin, hs->host, hs->resource, hs->protocol);" ]$ p3 F4 y* O5 @* q
        free_handshake(hs); // 释放handshake指针,已经不用了!, P( I* |' Y! ^* _* d# d- r
        char response[BUFSIZ];
. z' E  M5 v3 `" M7 O: V        memset(response,0,BUFSIZ);& p! x; ?2 _/ |  b
        size_t j=0, handshake_len=strlen(handshake_str);
) i( n0 s+ D& L6 ]# P( F        for (i = 0; i < handshake_len; i++) response[i] = handshake_str[i];) L9 P. O! Z) {- \' ]
        for (j = 0; j < 16; i++, j++) response[i] = dig[j];; W! w6 c" s/ l' d
        // 这里的clientSocket就是连接好的socket对象了。
: P' s/ b8 [& @' f/ H+ L. Z# ^$ l        int sent = send(clientSocket, response, strlen(response), 0);
" y: r) m. m2 Y! _. ^        return sent>0;4 n( I/ {8 d. Z; @+ k0 j
}2 [- {* g; }/ z* Y5 f
目录结构:
7 X7 |6 B& ?! hsocket/
7 C) L4 d& {5 b# o7 k      socket.c) _% J' ~! r8 _. A+ i
      md5/* D( ?1 r5 _" [% x8 Y" n. F+ w
        md5.h
8 z8 C! x4 B1 R" X! d2 }( N        md5.c
' H4 K) U$ z5 @, w库文件下载位置: http://sourceforge.net/projects/libmd5-rfc/files/ 总共3个文件,
9 a) w5 Z1 G4 K: i: D编译命令:  gcc socket.c md5/md5.c -o socket.o  
3 V3 [4 W$ |* Z! X! V; m GCC 4.3编译通过,执行 ./socket.o 可以看到打出的raw和md5后的字符串信息。 这里使用了一个网友用nodejs写的websocket实现版本,打出详细的头信息,然后复制到程序里,以测试生成的握手字符串是否一样。
7 ?/ ^- j6 n  w// socket.c 代码。4 b4 p; ~# s- ]
// web-socket example 0 w+ g2 R/ E/ w- o; Y3 h& v
#include <stdio.h>
3 r0 E3 ^) S* e9 \#include <stdlib.h>- B, c( \0 G/ J% d5 M
#include <string.h>6 F9 Z' H, O; z1 |* W) q1 M( O' _
#include <inttypes.h>
- y2 j) ~8 E2 T3 E#include "md5/md5.h"% w/ w' ^) J! s+ {6 P
0 k$ H" \' z5 t( F: [" U
// #define BUFSIZ 512, ^" x- D. m5 v2 q1 U  J2 n( q5 P# Q2 T
. o0 Q+ ^1 U: I! Y4 `/ k
//定义handshake结构体变量
- u' `: w& D; A6 a3 B( e  }" u5 |' Istruct handshake {- @. u- ~3 S( U; z! U! D6 B" k5 T
        char *resource;
' D3 W8 X( ]" E+ o* a        char *host;
% W: b1 W3 ]! ~" z/ e3 o! Y' O        char *origin;( G' s  x: g4 z3 Q5 m
        char *protocol;4 M* R' `" B1 V/ I  k+ h8 _
        char *key1;
5 f! p8 V1 ~& {4 r0 z0 z        char *key2;
; `; a+ }. O7 U7 W3 z};
( T9 L9 s0 m( R- Q
4 V  M! P9 Z+ ]2 c/ r% V% P! p
% d" g4 f% M) s! E3 H( p0 s//释放握手后不再使用的部分变量1 ]% S9 K8 Q5 H0 D
void free_handshake(struct handshake* hs){
4 V- u% ^* ~+ ?; y- _- U        if( hs->resource != NULL )      free(hs->resource);' J: I) I1 B" I
        if( hs->host != NULL )          free(hs->host);3 z/ R, p) M! y. _& H9 m* [
        if( hs->origin != NULL )        free(hs->origin);4 ~0 t$ L- h3 i8 M3 c
        if( hs->protocol != NULL )      free(hs->protocol);" c# ?. K; ^" X) u9 j
        if( hs->key1 != NULL )          free(hs->key1);/ `9 a, m' G$ l( N  k$ g) z
        if( hs->key2 != NULL )          free(hs->key2);
  B+ v8 f6 d! y8 o6 N}2 R, i; w3 ?2 F: E* g: R/ U" r4 l
4 \. Y3 m. [1 S! {3 u8 q. I# I' x8 K2 _
// 这里对上一篇的match_string做了点修改,
( U1 K  g9 E/ t, s) p' ^" R// 增加了一个end参数,这样不必提取出字符串后,再做剔除处理
4 y: D9 D3 z- I- _char* match_string(const char* src, const char* pattern, char end){; A6 o1 C* E$ Q
        char buf[BUFSIZ];
2 P! o# x( w% e3 Q" Y6 S        memset(buf, 0, BUFSIZ);; o6 w3 S' b. f5 Q1 r4 X$ {. J
        size_t src_len = strlen(src); 4 d  ]; p$ y* S% e7 k' I- p- Z
        size_t ptn_len = strlen(pattern);0 t5 n; ]6 n  _3 N9 q
        unsigned short b=0, p=0, i=0;
  m$ e. I- _- I2 X$ R        char c='\0';/ `$ k6 |5 x; |3 p3 T3 `
        for(i=0; i<src_len; i++){: z% ?) u' @# k
                c = src[i];
8 {" G; O# v9 D6 U3 i1 {4 n  g2 U0 z6 \                if(p==ptn_len){ // p==ptn_len 表示正在匹配中8 S# H' Q' m- ^& O) n
                        if(c=='\r' || c=='\n'  || (end !='\0' && c==end) ) p++; // 匹配结束
; a  D* R( T; P4 _6 U* f, B- f; a. O. G                        else buf[b++]=c; // 匹配到的字符
5 o1 T+ N" Y, e8 G1 ^                }else if(p<ptn_len){ // 为达到匹配要求& S9 J5 c3 M7 r9 N
                        if(c==pattern[p]) p++;2 ^7 |; Q. D+ p- |& S1 j- D
                        else p=0;8 D+ N( X( L8 [  _0 _" C. X# }  l
                }1 r+ Y# ]: a4 R3 u* H1 \
        }
& ~* m" C0 Y' @$ Q        size_t ret_len = strlen(buf);
% H! t; r' U3 E- X% G. m3 L) P+ G3 i& N        char *ret_p; ) A- ]0 K2 y7 }8 w6 n
        if( ret_len>0 ){: A% p& A* v0 i
                ret_p = (char*)calloc(ret_len+1,sizeof(char)); // 加 1 为了存储 '\0'9 ]) _* U4 d$ {! T$ r' c2 Y
                memcpy(ret_p, buf, ret_len);3 G8 a6 ?, k% z$ ~, v4 u+ P( r  Z
        }else ret_p = NULL;% H# w; f6 g/ @2 Y5 @3 y% M8 q( I
        return ret_p; 8 d# k- K1 _5 `& z/ t. P- K
}
5 M& ]2 Y* O7 ~' t3 x% _9 H( X2 Y
0 H  N4 L2 n4 i% D/ b- i% \// md5 加密函数,用的是网上一个实现的比较通用的版本。7 k  u7 f1 d: W+ [9 ~- t" d
void md5(const char* src, size_t size, char* digest)0 C) C. w* S8 k: H0 o5 c2 b' \
{
+ Y$ U0 N- N* r, D. q8 X! u9 r; \        md5_state_t state;" I0 x0 `: O2 i/ A' h# N' q
        md5_init(&state);
( l7 p2 ]+ c7 s9 x        md5_append(&state,  src, size);7 A) ^4 Z8 s4 v8 D! {
        md5_finish(&state, digest);
0 e  \% V/ M6 S}
& r, h' C% ?# m% W) {8 K5 t: D4 D+ g8 L
void p(char*s, int len){
- a  r# R& v: M8 }* m        unsigned short i=0;
- `! @1 a: T$ ]. V  d4 |        for(i=0; i<len; i++) printf("%c",s[i]);$ H5 u, K/ L  p7 \
        printf("%c", '\n');
+ I8 k" D+ E- D}
2 \, r9 Z& x- {: t7 {( ~5 N
& X( j0 j9 E' hvoid handshake(const char* src, struct handshake* hs){! r4 o# L7 ?9 u+ A
        size_t src_len  = strlen(src), i = 0 ;. D9 s) e$ r0 X# Y2 i
        hs->resource    = match_string(src, "GET ", 0x20); // 提取空格之前
# g3 B, ~, G# o' n7 ?        hs->host                = match_string(src, "Host: ", '\0');3 R+ G* d& j- Z5 G  \* {9 s
        hs->origin              = match_string(src, "Origin: ", '\0');& l9 U5 X. ~1 \) S! C( p
        hs->protocol    = match_string(src, "Sec-WebSocket-Protocol: ", '\0');; _- g3 j6 B7 V" Z; b
        hs->key1                = match_string(src, "Sec-WebSocket-Key1: ", '\0');
' Z5 ^* ]4 b1 {' [: k5 S0 x' f: j7 Y        hs->key2                = match_string(src, "Sec-WebSocket-Key2: ", '\0'); - i& W: E  W! ?+ X* u" ^3 ?/ A. n
        // 获取 key3,即最后的8位字符
( z6 C% \% B" D0 H& _1 ~        char key3[8]="\0";
! F' X" u% r- }( ~9 \. a  G        for (i = 0; i < 8; i++) key3[i] = src[src_len - 8 + i];
- `" w, x" Y# V+ _: n3 x        char digits1[64]="\0", digits2[64]="\0", c='\0';0 x/ d7 d1 U" {4 y
        size_t spaces1 = 0, spaces2 = 0;
8 `! q' o7 O' v3 B/ o3 ]: ~6 m        size_t key1_len = strlen(hs->key1);
9 `% g* b, u1 ]' W        size_t key2_len = strlen(hs->key2);
" B$ E- z8 a6 s! t& U        short d1 = 0, d2 = 0;. P- P3 m5 K  m+ W) o
        unsigned int result1, result2;3 V, C+ b6 P: C+ c
        for (i = 0; i < key1_len; i++){ " _4 r' R5 n1 c! r) Y4 @" z) f
                c = hs->key1[i];
; l* o% n/ V) a' z# D: w1 P3 B                if (c == 0x20) spaces1++;
; ~. O1 _! N5 o: N                else if(c>='0' && c<='9') digits1[d1++]=c; 6 m  A6 ^2 K0 h% |3 w# [6 S
        }
) s$ r( ?$ J9 \" K  D4 Q' ?. l1 ^        for (i = 0; i < key2_len; i++){
9 |5 l/ P: C! G" V7 }0 ?% t" N                c = hs->key2[i];
; k3 G/ R! Y! Y                if (c == 0x20) spaces2++;
! \0 H! f( X, M7 a% Z1 _4 Y* X                else if(c>='0' && c<='9') digits2[d2++]=c;
* {8 C" q* D$ `: v: g) A9 E        }8 X, o0 M) k4 k' c
        result1 = (unsigned int) (strtoul(digits1, NULL, 10) / spaces1);
; u# I4 W9 _2 R* t" y- |! U        result2 = (unsigned int) (strtoul(digits2, NULL, 10) / spaces2);! a3 x) N. p/ i: _4 p0 [
        printf("ch1:%s\nch2:%s\n",digits1, digits2);$ |# E5 {# l! t# D8 Q
        printf("sp1:%d\nsp2:%d\n",spaces1, spaces2);
& p; L+ @+ p/ B; v% M1 F3 ?        printf("d1:%d\nd2:%d\n"  ,result1, result2); 2 u0 j. }. L: J/ s8 X/ X6 S
        unsigned char chrkey1[4]="\0", chrkey2[4]="\0";  g: w. N  V. _3 V9 {: ^$ c: v) a2 |
        for (i = 0; i < 4; i++) chrkey1[i] = result1 << (8 * i) >> (8 * 3);1 n  l5 a" B1 q, O( l! x
        for (i = 0; i < 4; i++) chrkey2[i] = result2 << (8 * i) >> (8 * 3);5 v0 r9 X5 z7 \  ~) ^
        printf("ch-key1:"); p(chrkey1,4);/ n( g8 R( F- {- f" ~
        for(i=0; i<4;i++)printf("0x%02x ",chrkey1[i]);
3 _: v4 v$ Z7 \        printf("ch-key2:"); p(chrkey2,4);! f9 P! S# |, |0 q  Q* K+ }- v
        for(i=0; i<4;i++)printf("0x%02x ",chrkey2[i]);
. @& v3 ?7 Z& d) H- H8 f8 o        unsigned char raw[16]="\0", dig[16]="\0";( o- i, n. w( ~, P+ A5 E
        memcpy(raw, chrkey1, 4);, Q/ ?) B' J2 v8 [
        memcpy(&raw[4], chrkey2, 4);# U1 ]2 v) I4 a$ ?6 S
        memcpy(&raw[8], key3, 8);
" I3 F% R% S& c        //计算的md5值
5 _: Z0 t6 b! o& `: P        printf("\nraw:");
. v) C* Y' L1 O/ D  _9 C3 R        for(i=0; i<16; i++) printf("0x%02x ",raw[i]);3 |3 ?* I- n2 v" P
        md5(raw, 16, dig);
& o2 a% {: E* |' W  B: F% g        printf("\nmd5:");; _: B" D% N& m6 C" l6 T2 j
        for(i=0; i<16; i++) printf("0x%02x ",dig[i]);
$ W8 w# ?  u3 i8 [+ ~% l- _}$ h+ f1 B# }9 o( B1 \

% T9 H, G4 i/ t8 t6 K( l; d8 T9 B% i7 G) Q4 }( T& q
int main(): C' A2 F' I2 i
{
3 y/ z; x; z& `4 \        unsigned char msg[512] = "GET /pub/chat?q=me HTTP/1.1\r\n\: w% s+ b* M6 z0 a
        Upgrade: WebSocket\r\n\- o7 N! q$ |7 H* q" ?
        Connection: Upgrade\r\n\
. o4 T5 Y, p3 }  E' u        Host: localhost:4400\r\n\
; r1 e" X3 N% H. w        Origin: null\r\n\( ~0 e7 K/ S% u
        Sec-WebSocket-Protocol: my-custom-chat-protocol\r\n\1 n1 L$ I5 E: B) S0 [* i* f
        Sec-WebSocket-Key1: x EO2 59186 4  28\\dY 0+\r\n\, n) x& B: ~/ M) p0 w0 A3 U4 k; ^
        Sec-WebSocket-Key2: 1  9  3  57695W    0\r\n\r\n";. I# [& n) P; }$ _( w8 f
        size_t len = strlen(msg);
. D7 A8 V1 q( q. _        msg[len]  =0x1f;
# l/ t& R8 X. w        msg[len+1]=0xf6;
( |6 L# n4 t/ p        msg[len+2]=0xf3;
0 }8 `( R# M7 E( O( M: Q8 K        msg[len+3]=0x3f;# h" L5 r) ^7 ~0 I9 H
        msg[len+4]=0xc7;
' O5 a, Q5 v, y        msg[len+5]=0x17;
' J2 a2 W# Q- t1 U2 I        msg[len+6]=0x20;! w# u" y/ F! d2 `# Q, \
        msg[len+7]=0x88;# B6 t: p) ~5 _  [
        struct handshake hs = {NULL, NULL, NULL, NULL, NULL, NULL};7 X9 x' m" M% m1 |( H( x# F4 F. F7 z
        handshake(msg, &hs);
2 C) z% x$ p4 H- L# ?$ Q        free_handshake(&hs);
. f  W- ]1 `0 ^' K        return 0;
. K9 K0 a8 ?/ d}
. b; t9 b2 s$ h# P! V6 r" y5 i" D' ?9 U  |. R% P6 |

8 Z9 v. N% y$ k  b8 a测试的结果:2 b( d9 g2 [: L' x
raw:0x19 0xbf 0x73 0xa4 0x01 0x27 0x5f 0xff 0x1f 0xf6 0xf3 0x3f 0xc7 0x17 0x20 0x88
1 \" U8 U* G5 [7 h5 Hmd5:0x61 0x30 0x1e 0xe8 0x8a 0x17 0xaf 0x39 0xd6 0xad 0xef 0xb9 0x6f 0x00 0x0f 0x68 4 C, p! D' q  Z; z( B
对比了nodejs的版本,握手部分生成没有错误。
; @' G) x$ a$ G- ]5 p. R, F
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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