Websocket是一种可双向通讯的网络协议,其底层的数据收发是基于socket的,所以使用c语言来实现理论上是没有问题的,主要难点在于协议中要求对个别数据进行加密处理,这些加密方法(库)在java、c#等专门开发web的平台中都是自带的API(随调随到),而在用到c语言时则苦于去寻找这些加密方法的源码和库,这使得用c来实现Websocket变得繁琐而吐血!所以非要用c语言来实现Websocket的童鞋,要做好刀耕火种的准备。。。
前面已经翻阅过很多博文,不管是协议还是c的代码都可以找到很多,本文也是参考了这些前辈的资料而得,但苦在搜罗到的代码都是片段或不够工整的,所以本文重点在尝试整合c实现Websocket的代码,以方便后来的小白快速上手使用(大牛随便喵喵留下点建议就好)。协议解析部分比较粗糙,已经了解过的人可以不用看一、二章。
一、建立连接
一切的开始,先上一张网络数据抓包图(这里用的Wireshark软件,还不知道抓包的童鞋可自行百度先玩玩)
GET /null HTTP/1.1
Connection: Upgrade
Host: 172.16.104.78:9999
Sec-WebSocket-Key: J2BJc+GQuSw34hi2TjyVpg==
Sec-WebSocket-Version: 13
Upgrade: websocket
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Server: Microsoft-HTTPAPI/2.0
Connection: Upgrade
Sec-WebSocket-Accept: uGY1yScptmHNgZqrDnpq1Ws1xho=
Date: Sat, 15 Jul 2017 15:35:16 +08
..Hello !...lx!. .M.LY..I am Server_Test...lx!.L.L./.H...~. .U..You are carefree ......lx!.L.L...S.
D.LY..You are carefree ....!\O^O/ <-.<- TAT =.=# -.-! .....h.R"K...(..f<.w|N.y}A.z.6.sj ...You are carefree ....!\O^O/ <-.<- TAT =.=# -.-! .....n../M.B...\k:.9qH.7pG.4.0.=g&...You are carefree ....!\O^O/ <-.<- TAT =.=# -.-! ........e.>.F.[.!.\.;.=.:.0.O.>.-.R..You are carefree ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17

这是一张websocket通讯下,服务器和客户端交互时的数据抓包,图中红色、蓝色分别是客户端、服务器发出的数据。
websocket实现数据通讯的步骤:
1.client向server发送http请求,数据内容如同图中第一大段红色字符串,其中携带了3个参数。
①要调用server的接口的路径字符串(不明白先不管)
②服务器的IP和端口
③握手Key
大家可以把这当作一个模版放到代码里。
const char httpDemo[] = "GET %s HTTP/1.1\r\n"
"Connection: Upgrade\r\n"
"Host: %s:%d\r\n"
"Sec-WebSocket-Key: %s\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Upgrade: websocket\r\n\r\n";
2.server收到http请求后,检查3个参数内容OK,然后返回携带参数的特定内容
④回应的握手Key
⑤时间字符串(格式: Date: 星期, 日 月 年 时:分:秒 时区)
同样大家可以作为模版使用
const char httpDemo[] = "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Server: Microsoft-HTTPAPI/2.0\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"%s\r\n\r\n";
3.client收到返回后,检查返回状态(主要看”101”)以及拿自己发出的握手Key (J2BJc+GQuSw34hi2TjyVpg==) 和收到server返回的Key (uGY1yScptmHNgZqrDnpq1Ws1xho=) 按照一套加密方式(这里用的sha1哈希加密,只一次握手)进行匹配OK,之后client和server就可以互发数据了
二、数据传输
通过上面抓包的截图还看到,建立连接之后互发的数据是比较奇怪的,很多字符并不是可见的ASCII码,这是因为Websocket协议里对数据的传输做了一些规定,简单说就是有一定的打包格式,其权威的解释请看这里draft-ietf-hybi-thewebsocketprotocol-13 - The WebSocket Protocol,不权威的解释请继续往下看
把上面的抓包数据按Hex16显示(这里只看后面互发数据的内容)
000000C5 81 07 48 65 6c 6c 6f 20 21 ..Hello !
000000A1 81 87 c3 6c 78 21 8b 09 14 4d ac 4c 59 ...lx!.. .M.LY
000000CE 81 10 49 20 61 6d 20 53 65 72 76 65 72 5f 54 65 ..I am S erver_Te
000000DE 73 74 st
000000AE 81 90 c3 6c 78 21 8a 4c 19 4c e3 2f 14 48 a6 02 ...lx!.L .L./.H..
000000BE 0c 7e 97 09 0b 55 .~...U
000000E0 81 14 59 6f 75 20 61 72 65 20 63 61 72 65 66 72 ..You ar e carefr
000000F0 65 65 20 2e 2e 2e ee ...
000000C4 81 8f c3 6c 78 21 8a 4c 19 4c e3 0f 19 53 a6 0a ...lx!.L .L...S..
000000D4 0a 44 a6 4c 59 .D.LY
000000F6 81 14 59 6f 75 20 61 72 65 20 63 61 72 65 66 72 ..You ar e carefr
00000106 65 65 20 2e 2e 2e ee ...
0000010C 81 21 5c 4f 5e 4f 2f 20 20 3c 2d 2e 3c 2d 20 20 .!\O^O/ <-.<-
0000011C 54 41 54 20 20 3d 2e 3d 23 20 20 2d 2e 2d 21 20 TAT =.=
0000012C 2e 2e 2e ...
000000D9 81 9a 68 b6 52 22 4b 93 0c 01 28 f6 12 66 3c f1 ..h.R"K. ..(..f<.
000000E9 77 7c 4e 90 79 7d 41 9d 7a 08 36 93 73 6a 20 ff w|N.y}A. z.6.sj .
0000012F 81 14 59 6f 75 20 61 72 65 20 63 61 72 65 66 72 ..You ar e carefr
0000013F 65 65 20 2e 2e 2e ee ...
00000145 81 21 5c 4f 5e 4f 2f 20 20 3c 2d 2e 3c 2d 20 20 .!\O^O/ <-.<-
00000155 54 41 54 20 20 3d 2e 3d 23 20 20 2d 2e 2d 21 20 TAT =.= # -.-!
00000165 2e 2e 2e ...
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
额~还是先放一张官方的图。。。
翻译一下
简单概括一下,每包数据组成按顺序归类为:
数据头(1字节) + 是否掩码标志(1个二进制位) + 数据长度(半字节~8字节) + 掩码(4字节或没有) + 数据内容
如果觉得乱,看不明白,那这里用对应的颜色标注再解释
可以看到0x81是数据头:最高位FIN必须置1即0x80,最低位用0x1代表该包数据类型为文本类,所以得到数据头0x81。
先看server打包的数据的第二位0x07:最高位表示是否使用掩码,这里为0表示不用;后7位表示数据长度,这里为0x07 = 7,也就是“48 65 6c 6c 6f 20 21”这7个数据的长度咯~
再看client打包的数据的第二位0x87:最高位这里为0x80表示使用掩码;后7位这里为0x07 = 7,所以0x87后面紧跟着的是4字节是掩码“c3 6c 78 21”,然后才是7个数据“8b 09 14 4d ac 4c 59”。
以上博主测试的只是小数据包,还不能完全反映图示的信息,这里记录数据长度的方式有三种:
第一种: 0<数据长度<126(0x7E),使用半个字节来记录数据长度,也就是上面例子
81 07 48 65 6c 6c 6f 20 21
81 8f c3 6c 78 21 8a 4c 19 4c e3 0f 19 53 a6 0a
第二种: 126=<数据长度<65536(0x10000),数据包的第二位强制为 0x7E(再并上掩码位),然后紧跟的2个字节表示数据长度,再接着才是掩码和数据,例如
81 fe 00 a3 3a d2 f4 7c 59 b3 96 1a 0a ...
第三种: 65536=<数据长度<=0xFFFFFFFFFFFFFFFF(谁tm一包数据上G,8个字节记录长度),数据包的第二位强制为 0x7F(再并上掩码位),然后紧跟的8个字节表示数据长度,再接着才是掩码和数据。
==注意== 协议严格规定数据量不足的必须使用少字节的记录方式,也就是你不能装逼给个 7f 然后跟 00 00 00 00 00 00 00 01。
说完数据长度,再来解析下掩码,这里掩码是固定4个字节长度的,内容可以用随机数生成。
这4个掩码会依次和要发送的数据进行异或处理,最后得到数据区里的数据,例如
81 87 c3 6c 78 21 8b 09 14 4d ac 4c 59
掩码为”c3 6c 78 21”,数据区”8b 09 14 4d ac 4c 59”,实际数据是”Helo !”即”48 65 6C 6C 6F 20 21”,打包过程为:
0x8b = 0xc3 X0r 0x48
0x09 = 0x6c X0r 0x65
0x14 = 0x78 X0r 0x6c
0x4d = 0x21 X0r 0x6c
0xac = 0xc3 X0r 0x6f // 这里循环使用4个掩码
0x4c = 0x6c X0r 0x20
0x59 = 0x78 X0r 0x21
那么解码呢?异或的解码任然是异或!
附带一提,掩码处理后的数据会出现大量的非可见ASCII字符,甚至数据0x00,不要随便使用0x00来判断数据结束。
==注意== 规定client发数据给server时必须使用掩码处理,而server下发数据给client时一般不进行掩码处理
三、代码实验
websocket_common.h
#ifndef _WEBSOCKET_COMMON_H_
#define _WEBSOCKET_COMMON_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef enum{
WCT_MINDATA = -20,
WCT_TXTDATA = -19,
WCT_BINDATA = -18,
WCT_DISCONN = -17,
WCT_PING = -16,
WCT_PONG = -15,
WCT_ERR = -1,
WCT_NULL = 0
}Websocket_CommunicationType;
int webSocket_clientLinkToServer(char *ip, int port, char *interface_path);
int webSocket_serverLinkToClient(int fd, char *recvBuf, unsigned int bufLen);
int webSocket_send(int fd, unsigned char *data, unsigned int dataLen, bool mod, Websocket_CommunicationType type);
int webSocket_recv(int fd, unsigned char *data, unsigned int dataMaxLen);
void delayms(unsigned int ms);
#endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
websocket_common.c
加密方法部分
其中sha1哈希加密的代码来自Linux下用C编写WebSocet服务以响应HTML5的WebSocket请求
#include "websocket_common.h"
typedef struct SHA1Context{
unsigned Message_Digest[5];
unsigned Length_Low;
unsigned Length_High;
unsigned char Message_Block[64];
int Message_Block_Index;
int Computed;
int Corrupted;
} SHA1Context;
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
void SHA1ProcessMessageBlock(SHA1Context *context)
{
const unsigned K[] = {0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 };
int t;
unsigned temp;
unsigned W[80];
unsigned A, B, C, D, E;
for(t = 0; t < 16; t++)
{
W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8;
W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]);
}
for(t = 16; t < 80; t++)
W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
A = context->Message_Digest[0];
B = context->Message_Digest[1];
C = context->Message_Digest[2];
D = context->Message_Digest[3];
E = context->Message_Digest[4];
for(t = 0; t < 20; t++)
{
temp = SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 20; t < 40; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 40; t < 60; t++)
{
temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
for(t = 60; t < 80; t++)
{
temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
temp &= 0xFFFFFFFF;
E = D;
D = C;
C = SHA1CircularShift(30,B);
B = A;
A = temp;
}
context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF;
context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF;
context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF;
context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF;
context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF;
context->Message_Block_Index = 0;
}
void SHA1Reset(SHA1Context *context)
{
context->Length_Low = 0;
context->Length_High = 0;
context->Message_Block_Index = 0;
context->Message_Digest[0] = 0x67452301;
context->Message_Digest[1] = 0xEFCDAB89;
context->Message_Digest[2] = 0x98BADCFE;
context->Message_Digest[3] = 0x10325476;
context->Message_Digest[4] = 0xC3D2E1F0;
context->Computed = 0;
context->Corrupted = 0;
}
void SHA1PadMessage(SHA1Context *context)
{
if (context->Message_Block_Index > 55)
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 64) context->Message_Block[context->Message_Block_Index++] = 0;
SHA1ProcessMessageBlock(context);
while(context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0;
}
else
{
context->Message_Block[context->Message_Block_Index++] = 0x80;
while(context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0;
}
context->Message_Block[56] = (context->Length_High >> 24 ) & 0xFF;
context->Message_Block[57] = (context->Length_High >> 16 ) & 0xFF;
context->Message_Block[58] = (context->Length_High >> 8 ) & 0xFF;
context->Message_Block[59] = (context->Length_High) & 0xFF;
context->Message_Block[60] = (context->Length_Low >> 24 ) & 0xFF;
context->Message_Block[61] = (context->Length_Low >> 16 ) & 0xFF;
context->Message_Block[62] = (context->Length_Low >> 8 ) & 0xFF;
context->Message_Block[63] = (context->Length_Low) & 0xFF;
SHA1ProcessMessageBlock(context);
}
int SHA1Result(SHA1Context *context)
{
if (context->Corrupted)
{
return 0;
}
if (!context->Computed)
{
SHA1PadMessage(context);
context->Computed = 1;
}
return 1;
}
void SHA1Input(SHA1Context *context,const char *message_array,unsigned length){
if (!length)
return;
if (context->Computed || context->Corrupted)
{
context->Corrupted = 1;
return;
}
while(length-- && !context->Corrupted)
{
context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
context->Length_Low += 8;
context->Length_Low &= 0xFFFFFFFF;
if (context->Length_Low == 0)
{
context->Length_High++;
context->Length_High &= 0xFFFFFFFF;
if (context->Length_High == 0) context->Corrupted = 1;
}
if (context->Message_Block_Index == 64)
{
SHA1ProcessMessageBlock(context);
}
message_array++;
}
}
char * sha1_hash(const char *source){
SHA1Context sha;
char *buf;
SHA1Reset(&sha);
SHA1Input(&sha, source, strlen(source));
if (!SHA1Result(&sha))
{
printf("SHA1 ERROR: Could not compute message digest");
return NULL;
}
else
{
buf = (char *)malloc(128);
memset(buf, 0, 128);
sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1],
sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);
return buf;
}
}
int tolower(int c)
{
if (c >= 'A' && c <= 'Z')
{
return c + 'a' - 'A';
}
else
{
return c;
}
}
int htoi(const char s[], int start, int len)
{
int i, j;
int n = 0;
if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))
{
i = 2;
}
else
{
i = 0;
}
i+=start;
j=0;
for (; (s[i] >= '0' && s[i] <= '9')
|| (s[i] >= 'a' && s[i] <= 'f') || (s[i] >='A' && s[i] <= 'F');++i)
{
if(j>=len)
{
break;
}
if (tolower(s[i]) > '9')
{
n = 16 * n + (10 + tolower(s[i]) - 'a');
}
else
{
n = 16 * n + (tolower(s[i]) - '0');
}
j++;
}
return n;
}
const char base64char[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int base64_encode( const unsigned char *bindata, char *base64, int binlength)
{
int i, j;
unsigned char current;
for ( i = 0, j = 0 ; i < binlength ; i += 3 )
{
current = (bindata[i] >> 2) ;
current &= (unsigned char)0x3F;
base64[j++] = base64char[(int)current];
current = ( (unsigned char)(bindata[i] << 4 ) ) & ( (unsigned char)0x30 ) ;
if ( i + 1 >= binlength )
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
base64[j++] = '=';
break;
}
current |= ( (unsigned char)(bindata[i+1] >> 4) ) & ( (unsigned char) 0x0F );
base64[j++] = base64char[(int)current];
current = ( (unsigned char)(bindata[i+1] << 2) ) & ( (unsigned char)0x3C ) ;
if ( i + 2 >= binlength )
{
base64[j++] = base64char[(int)current];
base64[j++] = '=';
break;
}
current |= ( (unsigned char)(bindata[i+2] >> 6) ) & ( (unsigned char) 0x03 );
base64[j++] = base64char[(int)current];
current = ( (unsigned char)bindata[i+2] ) & ( (unsigned char)0x3F ) ;
base64[j++] = base64char[(int)current];
}
base64[j] = '\0';
return j;
}
int base64_decode( const char *base64, unsigned char *bindata)
{
int i, j;
unsigned char k;
unsigned char temp[4];
for ( i = 0, j = 0; base64[i] != '\0' ; i += 4 )
{
memset( temp, 0xFF, sizeof(temp) );
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i] )
temp[0]= k;
}
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i+1] )
temp[1]= k;
}
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i+2] )
temp[2]= k;
}
for ( k = 0 ; k < 64 ; k ++ )
{
if ( base64char[k] == base64[i+3] )
temp[3]= k;
}
bindata[j++] = ((unsigned char)(((unsigned char)(temp[0] << 2))&0xFC)) | \
((unsigned char)((unsigned char)(temp[1]>>4)&0x03));
if ( base64[i+2] == '=' )
break;
bindata[j++] = ((unsigned char)(((unsigned char)(temp[1] << 4))&0xF0)) | \
((unsigned char)((unsigned char)(temp[2]>>2)&0x0F));
if ( base64[i+3] == '=' )
break;
bindata[j++] = ((unsigned char)(((unsigned char)(temp[2] << 6))&0xF0)) | \
((unsigned char)(temp[3]&0x3F));
}
return j;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
websocket_common.c
websocket通讯部分
#define REPORT_LOGIN_CONNECT_TIMEOUT 1000
#define REPORT_LOGIN_RESPOND_TIMEOUT (1000 + REPORT_LOGIN_CONNECT_TIMEOUT)
#define REPORT_ANALYSIS_ERR_RESEND_DELAY 500
#define WEBSOCKET_SHAKE_KEY_LEN 16
/*******************************************************************************
* 名称: webSocket_getRandomString
* 功能: 生成随机字符串
* 形参: *buf:随机字符串存储到
* len : 生成随机字符串长度
* 返回: 无
* 说明: 无
******************************************************************************/
void webSocket_getRandomString(unsigned char *buf, unsigned int len)
{
unsigned int i;
unsigned char temp;
srand((int)time(0));
for(i = 0; i < len; i++)
{
temp = (unsigned char)(rand()%256);
if(temp == 0)
temp = 128;
buf[i] = temp;
}
}
/*******************************************************************************
* 名称: webSocket_buildShakeKey
* 功能: client端使用随机数构建握手用的key
* 形参: *key:随机生成的握手key
* 返回: key的长度
* 说明: 无
******************************************************************************/
int webSocket_buildShakeKey(unsigned char *key)
{
unsigned char tempKey[WEBSOCKET_SHAKE_KEY_LEN] = {0};
webSocket_getRandomString(tempKey, WEBSOCKET_SHAKE_KEY_LEN);
return base64_encode((const unsigned char *)tempKey, (char *)key, WEBSOCKET_SHAKE_KEY_LEN);
}
/*******************************************************************************
* 名称: webSocket_buildRespondShakeKey
* 功能: server端在接收client端的key后,构建回应用的key
* 形参: *acceptKey:来自客户端的key字符串
* acceptKeyLen : 长度
* *respondKey : 在 acceptKey 之后加上 GUID, 再sha1哈希, 再转成base64得到 respondKey
* 返回: respondKey的长度(肯定比acceptKey要长)
* 说明: 无
******************************************************************************/
int webSocket_buildRespondShakeKey(unsigned char *acceptKey, unsigned int acceptKeyLen, unsigned char *respondKey)
{
char *clientKey;
char *sha1DataTemp;
char *sha1Data;
int i, n;
const char GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
unsigned int GUIDLEN;
if(acceptKey == NULL)
return 0;
GUIDLEN = sizeof(GUID);
clientKey = (char *)calloc(1, sizeof(char)*(acceptKeyLen + GUIDLEN + 10));
memset(clientKey, 0, (acceptKeyLen + GUIDLEN + 10));
memcpy(clientKey, acceptKey, acceptKeyLen);
memcpy(&clientKey[acceptKeyLen], GUID, GUIDLEN);
clientKey[acceptKeyLen + GUIDLEN] = '\0';
sha1DataTemp = sha1_hash(clientKey);
n = strlen(sha1DataTemp);
sha1Data = (char *)calloc(1, n / 2 + 1);
memset(sha1Data, 0, n / 2 + 1);
for(i = 0; i < n; i += 2)
sha1Data[ i / 2 ] = htoi(sha1DataTemp, i, 2);
n = base64_encode((const unsigned char *)sha1Data, (char *)respondKey, (n / 2));
free(sha1DataTemp);
free(sha1Data);
free(clientKey);
return n;
}
/*******************************************************************************
* 名称: webSocket_matchShakeKey
* 功能: client端收到来自服务器回应的key后进行匹配,以验证握手成功
* 形参: *myKey:client端请求握手时发给服务器的key
* myKeyLen : 长度
* *acceptKey : 服务器回应的key
* acceptKeyLen : 长度
* 返回: 0 成功 -1 失败
* 说明: 无
******************************************************************************/
int webSocket_matchShakeKey(unsigned char *myKey, unsigned int myKeyLen, unsigned char *acceptKey, unsigned int acceptKeyLen)
{
int retLen;
unsigned char tempKey[256] = {0};
retLen = webSocket_buildRespondShakeKey(myKey, myKeyLen, tempKey);
if(retLen != acceptKeyLen)
{
printf("webSocket_matchShakeKey : len err\r\n%s\r\n%s\r\n%s\r\n", myKey, tempKey, acceptKey);
return -1;
}
else if(strcmp((const char *)tempKey, (const char *)acceptKey) != 0)
{
printf("webSocket_matchShakeKey : str err\r\n%s\r\n%s\r\n", tempKey, acceptKey);
return -1;
}
return 0;
}
/*******************************************************************************
* 名称: webSocket_buildHttpHead
* 功能: 构建client端连接服务器时的http协议头, 注意websocket是GET形式的
* 形参: *ip:要连接的服务器ip字符串
* port : 服务器端口
* *interfacePath : 要连接的端口地址
* *shakeKey : 握手key, 可以由任意的16位字符串打包成base64后得到
* *package : 存储最后打包好的内容
* 返回: 无
* 说明: 无
******************************************************************************/
void webSocket_buildHttpHead(char *ip, int port, char *interfacePath, unsigned char *shakeKey, char *package)
{
const char httpDemo[] = "GET %s HTTP/1.1\r\n"
"Connection: Upgrade\r\n"
"Host: %s:%d\r\n"
"Sec-WebSocket-Key: %s\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Upgrade: websocket\r\n\r\n";
sprintf(package, httpDemo, interfacePath, ip, port, shakeKey);
}
/*******************************************************************************
* 名称: webSocket_buildHttpRespond
* 功能: 构建server端回复client连接请求的http协议
* 形参: *acceptKey:来自client的握手key
* acceptKeyLen : 长度
* *package : 存储
* 返回: 无
* 说明: 无
******************************************************************************/
void webSocket_buildHttpRespond(unsigned char *acceptKey, unsigned int acceptKeyLen, char *package)
{
const char httpDemo[] = "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Server: Microsoft-HTTPAPI/2.0\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"%s\r\n\r\n";
time_t now;
struct tm *tm_now;
char timeStr[256] = {0};
unsigned char respondShakeKey[256] = {0};
webSocket_buildRespondShakeKey(acceptKey, acceptKeyLen, respondShakeKey);
time(&now);
tm_now = localtime(&now);
strftime(timeStr, sizeof(timeStr), "Date: %a, %d %b %Y %T %Z", tm_now);
sprintf(package, httpDemo, respondShakeKey, timeStr);
}
/*******************************************************************************
* 名称: webSocket_enPackage
* 功能: websocket数据收发阶段的数据打包, 通常client发server的数据都要isMask(掩码)处理, 反之server到client却不用
* 形参: *data:准备发出的数据
* dataLen : 长度
* *package : 打包后存储地址
* packageMaxLen : 存储地址可用长度
* isMask : 是否使用掩码 1要 0 不要
* type : 数据类型, 由打包后第一个字节决定, 这里默认是数据传输, 即0x81
* 返回: 打包后的长度(会比原数据长2~16个字节不等) <=0 打包失败
* 说明: 无
******************************************************************************/
int webSocket_enPackage(unsigned char *data, unsigned int dataLen, unsigned char *package, unsigned int packageMaxLen, bool isMask, Websocket_CommunicationType type)
{
unsigned char maskKey[4] = {0};
unsigned char temp1, temp2;
int count;
unsigned int i, len = 0;
if(packageMaxLen < 2)
return -1;
if(type == WCT_MINDATA)
*package++ = 0x00;
else if(type == WCT_TXTDATA)
*package++ = 0x81;
else if(type == WCT_BINDATA)
*package++ = 0x82;
else if(type == WCT_DISCONN)
*package++ = 0x88;
else if(type == WCT_PING)
*package++ = 0x89;
else if(type == WCT_PONG)
*package++ = 0x8A;
else
return -1;
if(isMask)
*package = 0x80;
len += 1;
if(dataLen < 126)
{
*package++ |= (dataLen&0x7F);
len += 1;
}
else if(dataLen < 65536)
{
if(packageMaxLen < 4)
return -1;
*package++ |= 0x7E;
*package++ = (char)((dataLen >> 8) & 0xFF);
*package++ = (unsigned char)((dataLen >> 0) & 0xFF);
len += 3;
}
else if(dataLen < 0xFFFFFFFF)
{
if(packageMaxLen < 10)
return -1;
*package++ |= 0x7F;
*package++ = 0;
*package++ = 0;
*package++ = 0;
*package++ = 0;
*package++ = (char)((dataLen >> 24) & 0xFF);
*package++ = (char)((dataLen >> 16) & 0xFF);
*package++ = (char)((dataLen >> 8) & 0xFF);
*package++ = (char)((dataLen >> 0) & 0xFF);
len += 9;
}
if(isMask)
{
if(packageMaxLen < len + dataLen + 4)
return -1;
webSocket_getRandomString(maskKey, sizeof(maskKey));
*package++ = maskKey[0];
*package++ = maskKey[1];
*package++ = maskKey[2];
*package++ = maskKey[3];
len += 4;
for(i = 0, count = 0; i < dataLen; i++)
{
temp1 = maskKey[count];
temp2 = data[i];
*package++ = (char)(((~temp1)&temp2) | (temp1&(~temp2)));
count += 1;
if(count >= sizeof(maskKey))
count = 0;
}
len += i;
*package = '\0';
}
else
{
if(packageMaxLen < len + dataLen)
return -1;
memcpy(package, data, dataLen);
package[dataLen] = '\0';
len += dataLen;
}
return len;
}
/*******************************************************************************
* 名称: webSocket_dePackage
* 功能: websocket数据收发阶段的数据解包, 通常client发server的数据都要isMask(掩码)处理, 反之server到client却不用
* 形参: *data:解包的数据
* dataLen : 长度
* *package : 解包后存储地址
* packageMaxLen : 存储地址可用长度
* *packageLen : 解包所得长度
* 返回: 解包识别的数据类型 如 : txt数据, bin数据, ping, pong等
* 说明: 无
******************************************************************************/
int webSocket_dePackage(unsigned char *data, unsigned int dataLen, unsigned char *package, unsigned int packageMaxLen, unsigned int *packageLen)
{
unsigned char maskKey[4] = {0};
unsigned char temp1, temp2;
char Mask = 0, type;
int count, ret;
unsigned int i, len = 0, dataStart = 2;
if(dataLen < 2)
return -1;
type = data[0]&0x0F;
if((data[0]&0x80) == 0x80)
{
if(type == 0x01)
ret = WCT_TXTDATA;
else if(type == 0x02)
ret = WCT_BINDATA;
else if(type == 0x08)
ret = WCT_DISCONN;
else if(type == 0x09)
ret = WCT_PING;
else if(type == 0x0A)
ret = WCT_PONG;
else
return WCT_ERR;
}
else if(type == 0x00)
ret = WCT_MINDATA;
else
return WCT_ERR;
if((data[1] & 0x80) == 0x80)
{
Mask = 1;
count = 4;
}
else
{
Mask = 0;
count = 0;
}
len = data[1] & 0x7F;
if(len == 126)
{
if(dataLen < 4)
return WCT_ERR;
len = data[2];
len = (len << 8) + data[3];
if(dataLen < len + 4 + count)
return WCT_ERR;
if(Mask)
{
maskKey[0] = data[4];
maskKey[1] = data[5];
maskKey[2] = data[6];
maskKey[3] = data[7];
dataStart = 8;
}
else
dataStart = 4;
}
else if(len == 127)
{
if(dataLen < 10)
return WCT_ERR;
if(data[2] != 0 || data[3] != 0 || data[4] != 0 || data[5] != 0)
return WCT_ERR;
len = data[6];
len = (len << 8) + data[7];
len = (len << 8) + data[8];
len = (len << 8) + data[9];
if(dataLen < len + 10 + count)
return WCT_ERR;
if(Mask)
{
maskKey[0] = data[10];
maskKey[1] = data[11];
maskKey[2] = data[12];
maskKey[3] = data[13];
dataStart = 14;
}
else
dataStart = 10;
}
else
{
if(dataLen < len + 2 + count)
return WCT_ERR;
if(Mask)
{
maskKey[0] = data[2];
maskKey[1] = data[3];
maskKey[2] = data[4];
maskKey[3] = data[5];
dataStart = 6;
}
else
dataStart = 2;
}
if(dataLen < len + dataStart)
return WCT_ERR;
if(packageMaxLen < len + 1)
return WCT_ERR;
if(Mask)
{
for(i = 0, count = 0; i < len; i++)
{
temp1 = maskKey[count];
temp2 = data[i + dataStart];
*package++ = (char)(((~temp1)&temp2) | (temp1&(~temp2)));
count += 1;
if(count >= sizeof(maskKey))
count = 0;
}
*package = '\0';
}
else
{
memcpy(package, &data[dataStart], len);
package[len] = '\0';
}
*packageLen = len;
return ret;
}
/*******************************************************************************
* 名称: webSocket_clientLinkToServer
* 功能: 向websocket服务器发送http(携带握手key), 以和服务器构建连接, 非阻塞模式
* 形参: *ip:服务器ip
* port : 服务器端口
* *interface_path : 接口地址
* 返回: >0 返回连接句柄 <= 0 连接失败或超时, 所花费的时间 ms
* 说明: 无
******************************************************************************/
int webSocket_clientLinkToServer(char *ip, int port, char *interface_path)
{
int ret, fd , timeOut;
unsigned char loginBuf[512] = {0}, recBuf[512] = {0}, shakeKey[128] = {0}, *p;
struct sockaddr_in report_addr;
memset(&report_addr,0,sizeof(report_addr));
report_addr.sin_family = AF_INET;
report_addr.sin_addr.s_addr = inet_addr(ip);
report_addr.sin_port = htons(port);
if((fd = socket(AF_INET,SOCK_STREAM, 0)) < 0)
{
printf("webSocket_login : cannot create socket\r\n");
return -1;
}
ret = fcntl(fd , F_GETFL , 0);
fcntl(fd , F_SETFL , ret | O_NONBLOCK);
timeOut = 0;
while(connect(fd , (struct sockaddr *)&report_addr,sizeof(struct sockaddr)) == -1)
{
if(++timeOut > REPORT_LOGIN_CONNECT_TIMEOUT)
{
printf("webSocket_login : %s:%d cannot connect ! %d\r\n" , ip, port, timeOut);
sprintf(loginBuf, "webSocket_login : %s:%d cannot connect ! %d" , ip, port, timeOut);
close(fd);
return -timeOut;
}
delayms(1);
}
memset(shakeKey, 0, sizeof(shakeKey));
webSocket_buildShakeKey(shakeKey);
memset(loginBuf, 0, sizeof(loginBuf));
webSocket_buildHttpHead(ip, port, interface_path, shakeKey, (char *)loginBuf);
ret = send(fd , loginBuf , strlen((const char*)loginBuf) , MSG_NOSIGNAL);
while(1)
{
memset(recBuf , 0 , sizeof(recBuf));
ret = recv(fd , recBuf , sizeof(recBuf) , MSG_NOSIGNAL);
if(ret > 0)
{
if(strncmp((const char *)recBuf, (const char *)"HTTP", strlen((const char *)"HTTP")) == 0)
{
if((p = (unsigned char *)strstr((const char *)recBuf, (const char *)"Sec-WebSocket-Accept: ")) != NULL)
{
p += strlen((const char *)"Sec-WebSocket-Accept: ");
sscanf((const char *)p, "%s\r\n", p);
if(webSocket_matchShakeKey(shakeKey, strlen((const char *)shakeKey), p, strlen((const char *)p)) == 0)
return fd;
else
{
ret = send(fd , loginBuf , strlen((const char*)loginBuf) , MSG_NOSIGNAL);
}
}
else
{
ret = send(fd , loginBuf , strlen((const char*)loginBuf) , MSG_NOSIGNAL);
}
}
}
else if(ret <= 0)
{
;
}
if(++timeOut > REPORT_LOGIN_RESPOND_TIMEOUT)
{
close(fd);
return -timeOut;
}
delayms(1);
}
close(fd);
return -timeOut;
}
/*******************************************************************************
* 名称: webSocket_serverLinkToClient
* 功能: 服务器回复客户端的连接请求, 以建立websocket连接
* 形参: fd:连接句柄
* *recvBuf : 接收到来自客户端的数据(内含http连接请求)
* bufLen :
* 返回: =0 建立websocket连接成功 <0 建立websocket连接失败
* 说明: 无
******************************************************************************/
int webSocket_serverLinkToClient(int fd, char *recvBuf, unsigned int bufLen)
{
char *p;
int ret;
char recvShakeKey[512], respondPackage[1024];
if((p = strstr(recvBuf, "Sec-WebSocket-Key: ")) == NULL)
return -1;
p += strlen("Sec-WebSocket-Key: ");
memset(recvShakeKey, 0, sizeof(recvShakeKey));
sscanf(p, "%s", recvShakeKey);
ret = strlen(recvShakeKey);
if(ret < 1)
return -1;
memset(respondPackage, 0, sizeof(respondPackage));
webSocket_buildHttpRespond(recvShakeKey, ret, respondPackage);
return send(fd, respondPackage, strlen(respondPackage), MSG_NOSIGNAL);
}
/*******************************************************************************
* 名称: webSocket_send
* 功能: websocket数据基本打包和发送
* 形参: fd:连接句柄
* *data : 数据
* dataLen : 长度
* mod : 数据是否使用掩码, 客户端到服务器必须使用掩码模式
* type : 数据要要以什么识别头类型发送(txt, bin, ping, pong ...)
* 返回: 调用send的返回
* 说明: 无
******************************************************************************/
int webSocket_send(int fd, unsigned char *data, unsigned int dataLen, bool mod, Websocket_CommunicationType type)
{
unsigned char *webSocketPackage;
unsigned int retLen, ret;
webSocketPackage = (unsigned char *)calloc(1, sizeof(char)*(dataLen + 128)); memset(webSocketPackage, 0, (dataLen + 128));
retLen = webSocket_enPackage(data, dataLen, webSocketPackage, (dataLen + 128), mod, type);
ret = send(fd, webSocketPackage, retLen, MSG_NOSIGNAL);
free(webSocketPackage);
return ret;
}
/*******************************************************************************
* 名称: webSocket_recv
* 功能: websocket数据接收和基本解包
* 形参: fd:连接句柄
* *data : 数据接收地址
* dataMaxLen : 接收区可用最大长度
* 返回: <= 0 没有收到有效数据 > 0 成功接收并解包数据
* 说明: 无
******************************************************************************/
int webSocket_recv(int fd, unsigned char *data, unsigned int dataMaxLen)
{
unsigned char *webSocketPackage, *recvBuf;
int ret, ret2 = 0;
unsigned int retLen = 0;
recvBuf = (unsigned char *)calloc(1, sizeof(char)*dataMaxLen); memset(recvBuf, 0, dataMaxLen);
ret = recv(fd, recvBuf, dataMaxLen, MSG_NOSIGNAL);
if(ret > 0)
{
if(strncmp(recvBuf, "GET", 3) == 0)
{
ret2 = webSocket_serverLinkToClient(fd, recvBuf, ret);
free(recvBuf);
if(ret2 < 0)
{
memset(data, 0, dataMaxLen);
strcpy(data, "connect false !\r\n");
return strlen("connect false !\r\n");
}
memset(data, 0, dataMaxLen);
strcpy(data, "connect ...\r\n");
return strlen("connect ...\r\n");
}
webSocketPackage = (unsigned char *)calloc(1, sizeof(char)*(ret + 128)); memset(webSocketPackage, 0, (ret + 128));
ret2 = webSocket_dePackage(recvBuf, ret, webSocketPackage, (ret + 128), &retLen);
if(ret2 == WCT_PING && retLen > 0)
{
webSocket_send(fd, webSocketPackage, retLen, true, WCT_PONG);
printf("webSocket_recv : PING %d\r\n%s\r\n" , retLen, webSocketPackage);
free(recvBuf);
free(webSocketPackage);
return WCT_NULL;
}
else if(retLen > 0 && (ret2 == WCT_TXTDATA || ret2 == WCT_BINDATA || ret2 == WCT_MINDATA))
{
memcpy(data, webSocketPackage, retLen);
free(recvBuf);
free(webSocketPackage);
return retLen;
}
free(recvBuf);
free(webSocketPackage);
return -ret;
}
else
{
free(recvBuf);
return ret;
}
}
void delayms(unsigned int ms)
{
struct timeval tim;
tim.tv_sec = ms/1000;
tim.tv_usec = (ms%1000)*1000;
select(0, NULL, NULL, NULL, &tim);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576
- 577
- 578
- 579
- 580
- 581
- 582
- 583
- 584
- 585
- 586
- 587
- 588
- 589
- 590
- 591
- 592
- 593
- 594
- 595
- 596
- 597
- 598
- 599
- 600
- 601
- 602
- 603
- 604
- 605
- 606
- 607
- 608
- 609
- 610
- 611
- 612
- 613
- 614
- 615
- 616
- 617
- 618
- 619
- 620
- 621
- 622
- 623
- 624
- 625
- 626
- 627
- 628
- 629
- 630
- 631
- 632
- 633
- 634
- 635
- 636
- 637
- 638
- 639
- 640
- 641
- 642
- 643
- 644
- 645
- 646
- 647
- 648
- 649
- 650
- 651
- 652
- 653
- 654
- 655
- 656
- 657
- 658
- 659
- 660
- 661
- 662
- 663
- 664
- 665
- 666
- 667
- 668
- 669
- 670
- 671
- 672
- 673
- 674
- 675
- 676
- 677
- 678
- 679
- 680
- 681
- 682
- 683
- 684
- 685
- 686
- 687
- 688
- 689
- 690
- 691
- 692
- 693
- 694
- 695
- 696
- 697
- 698
- 699
- 700
- 701
- 702
- 703
- 704
- 705
server_main.c 服务器示例,仅供参考
#include "websocket_common.h"
#define EPOLL_RESPOND_NUM 100 // epoll同时响应事件数量
typedef int (*CallBackFun)(int fd, char *buf, unsigned int bufLen);
typedef struct{
int fd;
int client_fd_array[EPOLL_RESPOND_NUM][2];
char ip[24];
int port;
char buf[1024];
CallBackFun action;
}Websocket_Server;
int arrayAddItem(int array[][2], int arraySize, int value)
{
int i;
for(i = 0; i < arraySize; i++)
{
if(array[i][1] == 0)
{
array[i][0] = value;
array[i][1] = 1;
return 0;
}
}
return -1;
}
int arrayRemoveItem(int array[][2], int arraySize, int value)
{
int i;
for(i = 0; i < arraySize; i++)
{
if(array[i][0] == value)
{
array[i][0] = 0;
array[i][1] = 0;
return 0;
}
}
return -1;
}
int call(CallBackFun function, int fd, char *buf, unsigned int bufLen)
{
return function(fd, buf, bufLen);
}
void server_thread_fun(void *arge)
{
int ret , i , j;
int accept_fd;
int socAddrLen;
struct sockaddr_in acceptAddr;
struct sockaddr_in serverAddr;
Websocket_Server *ws = (Websocket_Server *)arge;
memset(&serverAddr , 0 , sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = INADDR_ANY;
serverAddr.sin_port = htons(ws->port);
socAddrLen = sizeof(struct sockaddr_in);
ws->fd = socket(AF_INET, SOCK_STREAM,0);
if(ws->fd <= 0)
{
printf("server cannot create socket !\r\n");
exit(1);
}
ret = fcntl(ws->fd , F_GETFL , 0);
fcntl(ws->fd , F_SETFL , ret | O_NONBLOCK);
while(bind(ws->fd, (struct sockaddr *)&serverAddr, sizeof(struct sockaddr)) < 0 )
delayms(1);
ret = listen(ws->fd, 0);
if(ret < 0)
{
printf("server cannot listen request\r\n");
close(ws->fd);
exit(1);
}
int epoll_fd;
epoll_fd = epoll_create(EPOLL_RESPOND_NUM);
if(epoll_fd < 0)
{
printf("server epoll_create failed\r\n");
exit(1);
}
int nfds;
struct epoll_event ev;
struct epoll_event events[EPOLL_RESPOND_NUM];
ev.events = EPOLLIN|EPOLLET;
ev.data.fd = ws->fd;
if(epoll_ctl(epoll_fd , EPOLL_CTL_ADD , ws->fd , &ev) < 0)
{
printf("server epll_ctl : ws->fd register failed\r\n");
close(epoll_fd);
exit(1);
}
printf("\r\n\r\n========== server start ! ==========\r\n\r\n");
while(1)
{
nfds = epoll_wait(epoll_fd , events , EPOLL_RESPOND_NUM , -1);
if(nfds < 0)
{
printf("server start epoll_wait failed\r\n");
close(epoll_fd);
exit(1);
}
for(j = 0 ; j < nfds ; j++)
{
if ((events[j].events & EPOLLERR) || (events[j].events & EPOLLHUP))
{
printf("accept close : %d\r\n", events[j].data.fd);
ev.data.fd = events[j].data.fd;
if(epoll_ctl(epoll_fd , EPOLL_CTL_DEL , events[j].data.fd , &ev) < 0)
{
printf("server epoll_ctl : EPOLL_CTL_DEL failed !\r\n");
close(epoll_fd);
exit(1);
}
arrayRemoveItem(ws->client_fd_array, EPOLL_RESPOND_NUM, events[j].data.fd);
close(events[j].data.fd);
}
else if(events[j].data.fd == ws->fd)
{
accept_fd = accept(ws->fd, (struct sockaddr *)&acceptAddr, &socAddrLen);
if(accept_fd >= 0)
{
ev.data.fd = accept_fd;
if(epoll_ctl(epoll_fd , EPOLL_CTL_ADD , accept_fd , &ev) < 0)
{
printf("server epoll_ctl : EPOLL_CTL_ADD failed !\r\n");
close(epoll_fd);
exit(1);
}
printf("server fd/%d : accept\r\n", accept_fd);
arrayAddItem(ws->client_fd_array, EPOLL_RESPOND_NUM, accept_fd);
}
}
else if(events[j].events & EPOLLIN)
{
memset(ws->buf, 0, sizeof(ws->buf));
ret = call(ws->action, events[j].data.fd , ws->buf , sizeof(ws->buf));
if(ret <= 0)
{
if(errno == EAGAIN || errno == EINTR)
;
else
{
printf("accept close : %d\r\n", events[j].data.fd);
ev.data.fd = events[j].data.fd;
if(epoll_ctl(epoll_fd , EPOLL_CTL_DEL , events[j].data.fd , &ev) < 0)
{
printf("server epoll_ctl : EPOLL_CTL_DEL failed !\r\n");
close(epoll_fd);
exit(1);
}
arrayRemoveItem(ws->client_fd_array, EPOLL_RESPOND_NUM, events[j].data.fd);
close(events[j].data.fd);
}
}
}
else if(events[j].events & EPOLLOUT)
;
}
}
close(epoll_fd);
close(ws->fd);
pthread_exit(NULL);
}
int server_action(int fd, char *buf, unsigned int bufLen)
{
int ret;
ret = webSocket_recv(fd , buf , bufLen);
if(ret > 0)
{
printf("server fd/%d : len/%d %s\r\n", fd, ret, buf);
if(strstr(buf, "connect") != NULL)
ret = webSocket_send(fd, "Hello !", strlen("Hello !"), false, WCT_TXTDATA);
else if(strstr(buf, "Hello") != NULL)
ret = webSocket_send(fd, "I am Server_Test", strlen("I am Server_Test"), false, WCT_TXTDATA);
else
ret = webSocket_send(fd, "You are carefree ...", strlen("You are carefree ..."), false, WCT_TXTDATA);
}
return ret;
}
int main(void)
{
int ret;
int i, client_fd;
pthread_t sever_thread_id;
Websocket_Server ws;
memset(&ws, 0, sizeof(ws));
ws.port = 9999;
ws.action = &server_action;
ret = pthread_create(&sever_thread_id, NULL, (void*)&server_thread_fun, (void *)(&ws));
if(ret != 0)
{
printf("create server false !\r\n");
exit(1);
}
while(1)
{
for(i = 0; i < EPOLL_RESPOND_NUM; i++)
{
if(ws.client_fd_array[i][1] != 0 && ws.client_fd_array[i][0] > 0)
{
client_fd = ws.client_fd_array[i][0];
ret = webSocket_send(client_fd, "\\O^O/ <-.<- TAT =.=# -.-! ...", strlen("\\O^O/ <-.<- TAT =.=# -.-! ..."), false, WCT_TXTDATA);
}
}
delayms(5000);
}
pthread_join(sever_thread_id, NULL);
printf("server close !\r\n");
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
client_main.c 客户端示例,仅供参考
#include "websocket_common.h"
char ip[] = "172.16.21.120";
int port = 9999;
int main(void)
{
int ret, timeCount = 0;
int fd;
char buf[1024];
fd = webSocket_clientLinkToServer(ip, port, "/null");
if(fd <= 0)
{
printf("client link to server failed !\r\n");
return -1;
}
sleep(1);
ret = webSocket_send(fd, "Hello !", strlen("Hello !"), true, WCT_TXTDATA);
printf("\r\n\r\n========== client start ! ==========\r\n\r\n");
while(1)
{
memset(buf, 0, sizeof(buf));
ret = webSocket_recv(fd, buf, sizeof(buf));
if(ret > 0)
{
printf("client recv : len/%d %s\r\n", ret, buf);
if(strstr(buf, "Hello") != NULL)
ret = webSocket_send(fd, "I am Client_Test", strlen("I am Client_Test"), true, WCT_TXTDATA);
else if(strstr(buf, "Server_Test") != NULL)
ret = webSocket_send(fd, "I am carefree !", strlen("I am carefree !"), true, WCT_TXTDATA);
else
;
if(ret <= 0)
{
close(fd);
break;
}
}
else
{
if(errno == EAGAIN || errno == EINTR)
;
else
{
close(fd);
break;
}
}
delayms(10);
timeCount += 10;
if(timeCount >= 4000)
{
timeCount = 0;
ret = webSocket_send(fd, "#%^#@@@DTG%^&&+_)+(*^%!HHI", strlen("#%^#@@@DTG%^&&+_)+(*^%!HHI"), true, WCT_TXTDATA);
if(ret <= 0)
{
close(fd);
break;
}
}
}
printf("client close !\r\n");
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
编译:
gcc -o client_process client_main.c websocket_common.c -lpthread
gcc -o server_process server_main.c websocket_common.c -lpthread
工程测试包websocket_for_linux.tar.gz
附带一提:
一、本文实现的只是websocket的一次握手连接,并且key加密方式是sha1哈希,不支持传说中的3次握手以及md5加密
二、在实际应用中,数据在发出阶段并不直接打包发出,一般会先转成base64再打包;比较重要的数据甚至会先进行加密(md5、aes、des等密匙加密)再转base64,不然这些数据一旦被抓包就会被别人Copy走。这也是为什么叫我们不要随便连公共wifi的原因,因为提供wifi的设备可以轻易把你使用手机联网服务的数据抓包;又或者通过获取手机IP和抓包中http协议中携带的参数规律,并对其下的特定端口进行高频率的tcp访问(万一中了呢!),以达到窃取数据的目的。