websocket

道法自然 2015/10/30 19:59:17
SendRequestWebData
SendWinsockDataFromStr
道法自然 2015/10/30 20:00:45
SendWebPackDataFromStr
SendlLoginWebData
. 2015/10/30 20:01:26
吃点东西再加班吧
道法自然 2015/10/30 20:01:52
BuidWebSocketPacket
一会就回去了啊
ProcWebSocketKeyValue
SendRequestWebData
道法自然 2015/10/30 20:05:23
SendWebPackDataFromStr
道法自然 2015/10/31 8:10:56
大智者必谦和,大善者必宽容。唯有小智者才咄咄逼人,小善者才斤斤计较。大气象者,不讲排场;讲大排场者,露小气象。大成者谦逊平和,小成者不可一世。真正优雅的,必定有包容万物、宽待众生的胸怀;真正高贵的,面对强于己者不卑不亢,面对弱于己者平等视之。





websocket协议

1.1 背景

1.2 协议概览

协议包含两部分:握手,数据传输。

客户端的握手如下:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

服务端的握手如下:
        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat
客户端和服务端都发送了握手,并且成功,数据传输即可开始。
 
1.3 发起握手
发起握手是为了兼容基于HTTP的服务端程序,这样一个端口可以同时处理HTTP客户端和WebSocket客户端
因此WebSocket客户端握手是一个HTTP Upgrade请求:
        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13
握手中的域的顺序是任意的。
 
5 数据帧
5.1 概述
WebScoket协议中,数据以帧序列的形式传输。
考虑到数据安全性,客户端向服务器传输的数据帧必须进行掩码处理。服务器若接收到未经过掩码处理的数据帧,则必须主动关闭连接。
服务器向客户端传输的数据帧一定不能进行掩码处理。客户端若接收到经过掩码处理的数据帧,则必须主动关闭连接。
针对上情况,发现错误的一方可向对方发送close帧(状态码是1002,表示协议错误),以关闭连接。
5.2 帧协议
WebSocket数据帧结构如下图所示:
      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+
 
FIN:1位
表示这是消息的最后一帧(结束帧),一个消息由一个或多个数据帧构成。若消息由一帧构成,起始帧即结束帧。
 
RSV1,RSV2,RSV3:各1位
MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
这里我翻译不好,大致意思是如果未定义扩展,各位是0;如果定义了扩展,即为非0值。如果接收的帧此处非0,扩展中却没有该值的定义,那么关闭连接。
 
OPCODE:4位
解释PayloadData,如果接收到未知的opcode,接收端必须关闭连接。
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
 
MASK:1位
用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。
 
Payload length:7位,7+16位,7+64位
PayloadData的长度(以字节为单位)。
如果其值在0-125,则是payload的真实长度。
如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
如果值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度。注意,网络字节序,需要转换。
长度表示遵循一个原则,用最少的字节表示长度(我理解是尽量减少不必要的传输)。举例说,payload真实长度是124,在0-125之间,必须用前7位表示;不允许长度1是126或127,然后长度2是124,这样违反原则。
Payload长度是ExtensionData长度与ApplicationData长度之和。ExtensionData长度可能是0,这种情况下,Payload长度即是ApplicationData长度。
 
 
WebSocket协议规定数据通过帧序列传输。
客户端必须对其发送到服务器的所有帧进行掩码处理。
服务器一旦收到无掩码帧,将关闭连接。服务器可能发送一个状态码是1002(表示协议错误)的Close帧。
而服务器发送客户端的数据帧不做掩码处理,一旦客户端发现经过掩码处理的帧,将关闭连接。客户端可能使用状态码1002。
 

消息分片

分片目的是发送长度未知的消息。如果不分片发送,即一帧,就需要缓存整个消息,计算其长度,构建frame并发送;使用分片的话,可使用一个大小合适的buffer,用消息内容填充buffer,填满即发送出去。

分片规则:

1.一个未分片的消息只有一帧(FIN为1,opcode非0)

2.一个分片的消息由起始帧(FIN为0,opcode非0),若干(0个或多个)帧(FIN为0,opcode为0),结束帧(FIN为1,opcode为0)。

3.控制帧可以出现在分片消息中间,但控制帧本身不允许分片。

4.分片消息必须按次序逐帧发送。

5.如果未协商扩展的情况下,两个分片消息的帧之间不允许交错。

6.能够处理存在于分片消息帧之间的控制帧

7.发送端为非控制消息构建长度任意的分片

8.client和server兼容接收分片消息与非分片消息

9.控制帧不允许分片,中间媒介不允许改变分片结构(即为控制帧分片)

10.如果使用保留位,中间媒介不知道其值表示的含义,那么中间媒介不允许改变消息的分片结构

11.如果协商扩展,中间媒介不知道,那么中间媒介不允许改变消息的分片结构,同样地,如果中间媒介不了解一个连接的握手信息,也不允许改变该连接的消息的分片结构

12.由于上述规则,一个消息的所有分片是同一数据类型(由第一个分片的opcode定义)的数据。因为控制帧不允许分片,所以一个消息的所有分片的数据类型是文本、二进制、opcode保留类型中的一种。

需要注意的是,如果控制帧不允许夹杂在一个消息的分片之间,延迟会较大,比如说当前正在传输一个较大的消息,此时的ping必须等待消息传输完成,才能发送出去,会导致较大的延迟。为了避免类似问题,需要允许控制帧夹杂在消息分片之间。

控制帧

  • linux cwebservice server code

  • 在HTML5中新增了WebSocket,使得通讯变得更加方便。这样一来,Web与硬件的交互除了CGI和XHR的方式外,又有了一个新的方式。那么使用WebSocket又如何与下层通信呢?看看WebSocket的相关介绍就会发现,其类似于HTTP协议的通信,但又不同于HTTP协议通信,其最终使用的是TCP通信。具体的可以参照该文WebScoket 规范 + WebSocket 协议。

    我们先来看看通信的效果图

    \

    \

    下面是实现的步骤

    1.建立SOCKET监听

    WebSocket也是TCP通信,所以服务端需要先建立监听,下面是实现的代码。

     

    01. /* server.c */
    02. #include <stdio.h>
    03. #include <stdlib.h>
    04. #include <string.h>
    05. #include <unistd.h>
    06. #include <sys socket.h="">
    07. #include <netinet in.h="">
    08.  
    09. #include "base64.h"
    10. #include "sha1.h"
    11. #include "intLib.h"
    12.  
    13.  
    14. #define REQUEST_LEN_MAX 1024
    15. #define DEFEULT_SERVER_PORT 8000
    16. #define WEB_SOCKET_KEY_LEN_MAX 256
    17. #define RESPONSE_HEADER_LEN_MAX 1024
    18. #define LINE_MAX 256
    19.  
    20.  
    21. void shakeHand(int connfd,const char *serverKey);
    22. char * fetchSecKey(const char * buf);
    23. char * computeAcceptKey(const char * buf);
    24. char * analyData(const char * buf,const int bufLen);
    25. char * packData(const char * message,unsigned long * len);
    26. void response(const int connfd,const char * message);
    27.  
    28. int main(int argc, char *argv[])
    29. {
    30. struct sockaddr_in servaddr, cliaddr;
    31. socklen_t cliaddr_len;
    32. int listenfd, connfd;
    33. char buf[REQUEST_LEN_MAX];
    34. char *data;
    35. char str[INET_ADDRSTRLEN];
    36. char *secWebSocketKey;
    37. int i,n;
    38. int connected=0;//0:not connect.1:connected.
    39. int port= DEFEULT_SERVER_PORT;
    40.  
    41. if(argc>1)
    42. {
    43. port=atoi(argv[1]);
    44. }
    45. if(port<=0||port>0xFFFF)
    46. {
    47. printf("Port(%d) is out of range(1-%d)
    48. ",port,0xFFFF);
    49. return;
    50. }
    51. listenfd = socket(AF_INET, SOCK_STREAM, 0);
    52.  
    53. bzero(&servaddr, sizeof(servaddr));
    54. servaddr.sin_family = AF_INET;
    55. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    56. servaddr.sin_port = htons(port);
    57.  
    58. bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    59.  
    60. listen(listenfd, 20);
    61.  
    62. printf("Listen %d
    63. Accepting connections ...
    64. ",port);
    65. cliaddr_len = sizeof(cliaddr);
    66. connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
    67. printf("From %s at PORT %d
    68. ",
    69. inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
    70. ntohs(cliaddr.sin_port));
    71.  
    72. while (1)
    73. {
    74.  
    75. memset(buf,0,REQUEST_LEN_MAX);
    76. n = read(connfd, buf, REQUEST_LEN_MAX);
    77. printf("---------------------
    78. ");
    79.  
    80.  
    81. if(0==connected)
    82. {
    83. printf("read:%d
    84. %s
    85. ",n,buf);
    86. secWebSocketKey=computeAcceptKey(buf); 
    87. shakeHand(connfd,secWebSocketKey);
    88. connected=1;
    89. continue;
    90. }
    91.  
    92. data=analyData(buf,n);
    93. response(connfd,data);
    94. }
    95. close(connfd);
    96. }</netinet></sys></unistd.h></string.h></stdlib.h></stdio.h>

     

    2.握手

     

    在建立监听后,网页向服务端发现WebSocket请求,这时需要先进行握手。握手时,客户端会在协议中包含一个握手的唯一Key,服务端在拿到这个Key后,需要加入一个GUID,然后进行sha1的加密,再转换成base64,最后再发回到客户端。这样就完成了一次握手。此种握手方式是针对chrome websocket 13的版本,其他版本的可能会有所不同。下面是实现的代码。

     

    001. char * fetchSecKey(const char * buf)
    002. {
    003. char *key;
    004. char *keyBegin;
    005. char *flag="Sec-WebSocket-Key: ";
    006. int i=0, bufLen=0;
    007.  
    008. key=(char *)malloc(WEB_SOCKET_KEY_LEN_MAX);
    009. memset(key,0, WEB_SOCKET_KEY_LEN_MAX);
    010. if(!buf)
    011. {
    012. return NULL;
    013. }
    014.  
    015. keyBegin=strstr(buf,flag);
    016. if(!keyBegin)
    017. {
    018. return NULL;
    019. }
    020. keyBegin+=strlen(flag);
    021.  
    022. bufLen=strlen(buf);
    023. for(i=0;i<buflen;i++) 1.1="" 101="" char="" clientkey="(char" const="" guid="258EAFA5-E914-47DA-95CA-C5AB0DC85B11" http="" i="" int="" n="" pre="" protocols="" response=""responseheader="" return="" s="" sconnection:="" serverkey="base64_encode(sha1Data,"sha1data="(char" sha1datatemp="sha1_hash(clientKey);" short="" ssec-websocket-accept:="" supgrade:="" switching="" upgrade="" void="" websocket="">
    024.  
    025.  
    026. 注意:<p> </p><p>1.Connection后面的值与HTTP通信时的不一样了,是Upgrade,而Upgrade又对应到了websocket,这样就标识了该通信协议是websocket的方式。</p><p>2.在sha1加密后进行base64编码时,使用sha1加密后的串必须将其当成16进制的字符串,将每两个字符合成一个新的码(0-0xFF间)来进一步计算后,才可以进行base64换算(我开始时就在这里折腾了很久,后面才弄明白还要加上这一步),如果是直接就base64,那就会握手失败。</p><p>3.对于sha1和base64网上有很多,后面也附上我所使用的代码。</p><h3>3.数据传输</h3><p>握手成功后就可以进行数据传输了,只要按照WebSocket的协议来解就可以了。下面是实现的代码</p><p> </p><pre class="brush:java;">char * analyData(const char * buf,const int bufLen)
    027. {
    028. char * data;
    029. char fin, maskFlag,masks[4];
    030. char * payloadData;
    031. char temp[8];
    032. unsigned long n, payloadLen=0;
    033. unsigned short usLen=0;
    034. int i=0;
    035.  
    036.  
    037. if (bufLen < 2)
    038. {
    039. return NULL;
    040. }
    041.  
    042. fin = (buf[0] & 0x80) == 0x80// 1bit,1表示最后一帧 
    043. if (!fin)
    044. {
    045. return NULL;// 超过一帧暂不处理
    046. }
    047.  
    048. maskFlag = (buf[1] & 0x80) == 0x80// 是否包含掩码 
    049. if (!maskFlag)
    050. {
    051. return NULL;// 不包含掩码的暂不处理
    052. }
    053.  
    054. payloadLen = buf[1] & 0x7F// 数据长度
    055. if (payloadLen == 126)
    056. {     
    057. memcpy(masks,buf+44);     
    058. payloadLen =(buf[2]&0xFF) << 8 | (buf[3]&0xFF); 
    059. payloadData=(char *)malloc(payloadLen);
    060. memset(payloadData,0,payloadLen);
    061. memcpy(payloadData,buf+8,payloadLen);
    062. }
    063. else if (payloadLen == 127)
    064. {
    065. memcpy(masks,buf+10,4); 
    066. for ( i = 0; i < 8; i++)
    067. {
    068. temp[i] = buf[9 - i];
    069. }
    070.  
    071. memcpy(&n,temp,8); 
    072. payloadData=(char *)malloc(n);
    073. memset(payloadData,0,n);
    074. memcpy(payloadData,buf+14,n);//toggle error(core dumped) if data is too long.
    075. payloadLen=n;   
    076. }
    077. else
    078. {  
    079. memcpy(masks,buf+2,4);   
    080. payloadData=(char *)malloc(payloadLen);
    081. memset(payloadData,0,payloadLen);
    082. memcpy(payloadData,buf+6,payloadLen);
    083. }
    084.  
    085. for (i = 0; i < payloadLen; i++)
    086. {
    087. payloadData[i] = (char)(payloadData[i] ^ masks[i % 4]);
    088. }
    089.  
    090. printf("data(%d):%s
    091. ",payloadLen,payloadData);
    092. return payloadData;
    093. }
    094.  
    095. char *  packData(const char * message,unsigned long * len)
    096. {
    097. char * data=NULL;
    098. unsigned long n;
    099.  
    100. n=strlen(message);
    101. if (n < 126)
    102. {
    103. data=(char *)malloc(n+2);
    104. memset(data,0,n+2);   
    105. data[0] = 0x81;
    106. data[1] = n;
    107. memcpy(data+2,message,n);
    108. *len=n+2;
    109. }
    110. else if (n < 0xFFFF)
    111. {
    112. data=(char *)malloc(n+4);
    113. memset(data,0,n+4);
    114. data[0] = 0x81;
    115. data[1] = 126;
    116. data[2] = (n>>8 0xFF);
    117. data[3] = (n & 0xFF);
    118. memcpy(data+4,message,n);   
    119. *len=n+4;
    120. }
    121. else
    122. {
    123.  
    124. // 暂不处理超长内容 
    125. *len=0;
    126. }
    127.  
    128.  
    129. return data;
    130. }
    131.  
    132. void response(int connfd,const char * message)
    133. {
    134. char * data;
    135. unsigned long n=0;
    136. int i;
    137. if(!connfd)
    138. {
    139. return;
    140. }
    141.  
    142. if(!data)
    143. {
    144. return;
    145. }
    146. data=packData(message,&n);
    147.  
    148. if(!data||n<=0)
    149. {
    150. printf("data is empty!
    151. ");
    152. return;
    153. }
    154.  
    155. write(connfd,data,n);
    156.  
    157. }</pre>
    158. <br>
    159. 注意:
    160. <p> </p>
    161. <p>1.对于超过0xFFFF长度的数据在分析数据部分虽然作了处理,但是在memcpy时会报core dumped的错误,没有解决,请过路的大牛帮忙指点。在packData部分也未对这一部分作处理。</p>
    162. <p>2.在这里碰到了一个郁闷的问题,在命名函数时,将函数名写的过长了(fetchSecWebSocketAcceptkey),结果导致编译通过,但在运行时却莫名其妙的报core dumped的错误,试了很多方法才发现是这个原因,后将名字改短后就OK了。</p>
    163. <p>3.在回复数据时,只要按websocket的协议进行回应就可以了。</p>
    164. <p>附上sha1、base64和intLib的代码(sha1和base64是从网上摘来的)</p>
    165. <p>sha1.h</p>
    166. <p> </p>
    167. <pre class="brush:java;">//sha1.h:对字符串进行sha1加密
    168. #ifndef _SHA1_H_
    169. #define _SHA1_H_
    170.  
    171. #include <stdio.h>
    172. #include <stdlib.h>
    173. #include <string.h>
    174.  
    175.  
    176. typedef struct SHA1Context{
    177. unsigned Message_Digest[5];     
    178. unsigned Length_Low;            
    179. unsigned Length_High;           
    180. unsigned char Message_Block[64];
    181. int Message_Block_Index;        
    182. int Computed;                   
    183. int Corrupted;                  
    184. } SHA1Context;
    185.  
    186. void SHA1Reset(SHA1Context *);
    187. int SHA1Result(SHA1Context *);
    188. void SHA1Input( SHA1Context *,const char *,unsigned);
    189. #endif
    190.  
    191.  
    192. #define SHA1CircularShift(bits,<a href="http://www.it165.net/edu/ebg/" target="_blank"class="keylink">word</a>) ((((<a href="http://www.it165.net/edu/ebg/" target="_blank"class="keylink">word</a>) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
    193.  
    194. void SHA1ProcessMessageBlock(SHA1Context *);
    195. void SHA1PadMessage(SHA1Context *);
    196.  
    197. void SHA1Reset(SHA1Context *context){// 初始化动作
    198. context->Length_Low             = 0;
    199. context->Length_High            = 0;
    200. context->Message_Block_Index    = 0;
    201.  
    202. context->Message_Digest[0]      = 0x67452301;
    203. context->Message_Digest[1]      = 0xEFCDAB89;
    204. context->Message_Digest[2]      = 0x98BADCFE;
    205. context->Message_Digest[3]      = 0x10325476;
    206. context->Message_Digest[4]      = 0xC3D2E1F0;
    207.  
    208. context->Computed   = 0;
    209. context->Corrupted  = 0;
    210. }
    211.  
    212.  
    213. int SHA1Result(SHA1Context *context){// 成功返回1,失败返回0
    214. if (context->Corrupted) {
    215. return 0;
    216. }
    217. if (!context->Computed) {
    218. SHA1PadMessage(context);
    219. context->Computed = 1;
    220. }
    221. return 1;
    222. }
    223.  
    224.  
    225. void SHA1Input(SHA1Context *context,const char *message_array,unsigned length){
    226. if (!length) return;
    227.  
    228. if (context->Computed || context->Corrupted){
    229. context->Corrupted = 1;
    230. return;
    231. }
    232.  
    233. while(length-- && !context->Corrupted){
    234. context->Message_Block[context->Message_Block_Index++] = (*message_array & 0xFF);
    235.  
    236. context->Length_Low += 8;
    237.  
    238. context->Length_Low &= 0xFFFFFFFF;
    239. if (context->Length_Low == 0){
    240. context->Length_High++;
    241. context->Length_High &= 0xFFFFFFFF;
    242. if (context->Length_High == 0) context->Corrupted = 1;
    243. }
    244.  
    245. if (context->Message_Block_Index == 64){
    246. SHA1ProcessMessageBlock(context);
    247. }
    248. message_array++;
    249. }
    250. }
    251.  
    252. void SHA1ProcessMessageBlock(SHA1Context *context){
    253. const unsigned K[] = {0x5A8279990x6ED9EBA10x8F1BBCDC0xCA62C1D6 };
    254. int         t;               
    255. unsigned    temp;            
    256. unsigned    W[80];           
    257. unsigned    A, B, C, D, E;   
    258.  
    259. for(t = 0; t < 16; t++) {
    260. W[t] = ((unsigned) context->Message_Block[t * 4]) << 24;
    261. W[t] |= ((unsigned) context->Message_Block[t * 4 1]) << 16;
    262. W[t] |= ((unsigned) context->Message_Block[t * 4 2]) << 8;
    263. W[t] |= ((unsigned) context->Message_Block[t * 4 3]);
    264. }
    265.  
    266. for(t = 16; t < 80; t++)  W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
    267.  
    268. A = context->Message_Digest[0];
    269. B = context->Message_Digest[1];
    270. C = context->Message_Digest[2];
    271. D = context->Message_Digest[3];
    272. E = context->Message_Digest[4];
    273.  
    274. for(t = 0; t < 20; t++) {
    275. temp =  SHA1CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
    276. temp &= 0xFFFFFFFF;
    277. E = D;
    278. D = C;
    279. C = SHA1CircularShift(30,B);
    280. B = A;
    281. A = temp;
    282. }
    283. for(t = 20; t < 40; t++) {
    284. temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
    285. temp &= 0xFFFFFFFF;
    286. E = D;
    287. D = C;
    288. C = SHA1CircularShift(30,B);
    289. B = A;
    290. A = temp;
    291. }
    292. for(t = 40; t < 60; t++) {
    293. temp = SHA1CircularShift(5,A) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
    294. temp &= 0xFFFFFFFF;
    295. E = D;
    296. D = C;
    297. C = SHA1CircularShift(30,B);
    298. B = A;
    299. A = temp;
    300. }
    301. for(t = 60; t < 80; t++) {
    302. temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
    303. temp &= 0xFFFFFFFF;
    304. E = D;
    305. D = C;
    306. C = SHA1CircularShift(30,B);
    307. B = A;
    308. A = temp;
    309. }
    310. context->Message_Digest[0] = (context->Message_Digest[0] + A) & 0xFFFFFFFF;
    311. context->Message_Digest[1] = (context->Message_Digest[1] + B) & 0xFFFFFFFF;
    312. context->Message_Digest[2] = (context->Message_Digest[2] + C) & 0xFFFFFFFF;
    313. context->Message_Digest[3] = (context->Message_Digest[3] + D) & 0xFFFFFFFF;
    314. context->Message_Digest[4] = (context->Message_Digest[4] + E) & 0xFFFFFFFF;
    315. context->Message_Block_Index = 0;
    316. }
    317.  
    318. void SHA1PadMessage(SHA1Context *context){
    319. if (context->Message_Block_Index > 55) {
    320. context->Message_Block[context->Message_Block_Index++] = 0x80;
    321. while(context->Message_Block_Index < 64)  context->Message_Block[context->Message_Block_Index++] = 0;
    322. SHA1ProcessMessageBlock(context);
    323. while(context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0;
    324. else {
    325. context->Message_Block[context->Message_Block_Index++] = 0x80;
    326. while(context->Message_Block_Index < 56) context->Message_Block[context->Message_Block_Index++] = 0;
    327. }
    328. context->Message_Block[56] = (context->Length_High >> 24 ) & 0xFF;
    329. context->Message_Block[57] = (context->Length_High >> 16 ) & 0xFF;
    330. context->Message_Block[58] = (context->Length_High >> 8 ) & 0xFF;
    331. context->Message_Block[59] = (context->Length_High) & 0xFF;
    332. context->Message_Block[60] = (context->Length_Low >> 24 ) & 0xFF;
    333. context->Message_Block[61] = (context->Length_Low >> 16 ) & 0xFF;
    334. context->Message_Block[62] = (context->Length_Low >> 8 ) & 0xFF;
    335. context->Message_Block[63] = (context->Length_Low) & 0xFF;
    336.  
    337. SHA1ProcessMessageBlock(context);
    338. }
    339.  
    340. /*
    341. int sha1_hash(const char *source, char *lrvar){// Main
    342. SHA1Context sha;
    343. char buf[128];
    344.  
    345. SHA1Reset(&sha);
    346. SHA1Input(&sha, source, strlen(source));
    347.  
    348. if (!SHA1Result(&sha)){
    349. printf("SHA1 ERROR: Could not compute message digest");
    350. return -1;
    351. } else {
    352. memset(buf,0,sizeof(buf));
    353. sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1],
    354. sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);
    355. //lr_save_string(buf, lrvar);
    356.  
    357. return strlen(buf);
    358. }
    359. }
    360. */
    361.  
    362. char * sha1_hash(const char *source){// Main
    363. SHA1Context sha;
    364. char *buf;//[128];
    365.  
    366. SHA1Reset(&sha);
    367. SHA1Input(&sha, source, strlen(source));
    368.  
    369. if (!SHA1Result(&sha)){
    370. printf("SHA1 ERROR: Could not compute message digest");
    371. return NULL;
    372. else {
    373. buf=(char *)malloc(128);
    374. memset(buf,0,sizeof(buf));
    375. sprintf(buf, "%08X%08X%08X%08X%08X", sha.Message_Digest[0],sha.Message_Digest[1],
    376. sha.Message_Digest[2],sha.Message_Digest[3],sha.Message_Digest[4]);
    377. //lr_save_string(buf, lrvar);
    378.  
    379. //return strlen(buf);
    380. return buf;
    381. }
    382. }
    383. </string.h></stdlib.h></stdio.h></pre>
    384. <br>
    385. base64.h
    386. <p> </p>
    387. <p> </p>
    388. <pre class="brush:java;">#ifndef _BASE64_H_
    389. #define _BASE64_H_
    390.  
    391. #include <stdio.h>
    392. #include <stdlib.h>
    393. #include <string.h>
    394.  
    395. const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    396. char* base64_encode(const char* data, int data_len);
    397. char *base64_decode(const char* data, int data_len);
    398. static char find_pos(char ch);
    399.  
    400. /* */
    401. char *base64_encode(const char* data, int data_len)
    402. {
    403. //int data_len = strlen(data);
    404. int prepare = 0;
    405. int ret_len;
    406. int temp = 0;
    407. char *ret = NULL;
    408. char *f = NULL;
    409. int tmp = 0;
    410. char changed[4];
    411. int i = 0;
    412. ret_len = data_len / 3;
    413. temp = data_len % 3;
    414. if (temp > 0)
    415. {
    416. ret_len += 1;
    417. }
    418. ret_len = ret_len*4 1;
    419. ret = (char *)malloc(ret_len);
    420.  
    421. if ( ret == NULL)
    422. {
    423. printf("No enough memory.
    424. ");
    425. exit(0);
    426. }
    427. memset(ret, 0, ret_len);
    428. f = ret;
    429. while (tmp < data_len)
    430. {
    431. temp = 0;
    432. prepare = 0;
    433. memset(changed, ''4);
    434. while (temp < 3)
    435. {
    436. //printf("tmp = %d
    437. ", tmp);
    438. if (tmp >= data_len)
    439. {
    440. break;
    441. }
    442. prepare = ((prepare << 8) | (data[tmp] & 0xFF));
    443. tmp++;
    444. temp++;
    445. }
    446. prepare = (prepare<<((3-temp)*8));
    447. //printf("before for : temp = %d, prepare = %d
    448. ", temp, prepare);
    449. for (i = 0; i < 4 ;i++ )
    450. {
    451. if (temp < i)
    452. {
    453. changed[i] = 0x40;
    454. }
    455. else
    456. {
    457. changed[i] = (prepare>>((3-i)*6)) & 0x3F;
    458. }
    459. *f = base[changed[i]];
    460. //printf("%.2X", changed[i]);
    461. f++;
    462. }
    463. }
    464. *f = '';
    465.  
    466. return ret;
    467.  
    468. }
    469. /* */
    470. static char find_pos(char ch)  
    471. {
    472. char *ptr = (char*)strrchr(base, ch);//the last position (the only) in base[]
    473. return (ptr - base);
    474. }
    475. /* */
    476. char *base64_decode(const char *data, int data_len)
    477. {
    478. int ret_len = (data_len / 4) * 3;
    479. int equal_count = 0;
    480. char *ret = NULL;
    481. char *f = NULL;
    482. int tmp = 0;
    483. int temp = 0;
    484. char need[3];
    485. int prepare = 0;
    486. int i = 0;
    487. if (*(data + data_len - 1) == '=')
    488. {
    489. equal_count += 1;
    490. }
    491. if (*(data + data_len - 2) == '=')
    492. {
    493. equal_count += 1;
    494. }
    495. if (*(data + data_len - 3) == '=')
    496. {//seems impossible
    497. equal_count += 1;
    498. }
    499. switch (equal_count)
    500. {
    501. case 0:
    502. ret_len += 4;//3 + 1 [1 for NULL]
    503. break;
    504. case 1:
    505. ret_len += 4;//Ceil((6*3)/8)+1
    506. break;
    507. case 2:
    508. ret_len += 3;//Ceil((6*2)/8)+1
    509. break;
    510. case 3:
    511. ret_len += 2;//Ceil((6*1)/8)+1
    512. break;
    513. }
    514. ret = (char *)malloc(ret_len);
    515. if (ret == NULL)
    516. {
    517. printf("No enough memory.
    518. ");
    519. exit(0);
    520. }
    521. memset(ret, 0, ret_len);
    522. f = ret;
    523. while (tmp < (data_len - equal_count))
    524. {
    525. temp = 0;
    526. prepare = 0;
    527. memset(need, 04);
    528. while (temp < 4)
    529. {
    530. if (tmp >= (data_len - equal_count))
    531. {
    532. break;
    533. }
    534. prepare = (prepare << 6) | (find_pos(data[tmp]));
    535. temp++;
    536. tmp++;
    537. }
    538. prepare = prepare << ((4-temp) * 6);
    539. for (i=0; i<3 ;i++ )
    540. {
    541. if (i == temp)
    542. {
    543. break;
    544. }
    545. *f = (char)((prepare>>((2-i)*8)) & 0xFF);
    546. f++;
    547. }
    548. }
    549. *f = '';
    550. return ret;
    551. }
    552.  
    553. #endif
    554. </string.h></stdlib.h></stdio.h></pre>
    555. <br>
    556. intLib.h
    557. <p> </p>
    558. <p> </p>
    559. <pre class="brush:java;">#ifndef _INT_LIB_H_
    560. #define _INT_LIB_H_
    561. int tolower(int c)
    562. {
    563. if (c >= 'A' && c <= 'Z')
    564. {
    565. return c + 'a' 'A';
    566. }
    567. else
    568. {
    569. return c;
    570. }
    571. }
    572.  
    573. int htoi(const char s[],int start,int len)
    574. {
    575. int i,j;
    576. int n = 0;
    577. if (s[0] == '0' && (s[1]=='x' || s[1]=='X')) //判断是否有前导0x或者0X
    578. {
    579. i = 2;
    580. }
    581. else
    582. {
    583. i = 0;
    584. }
    585. i+=start;
    586. j=0;
    587. for (; (s[i] >= '0' && s[i] <= '9')
    588. || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >='A' && s[i] <= 'F');++i)
    589. {  
    590. if(j>=len)
    591. {
    592. break;
    593. }
    594. if (tolower(s[i]) > '9')
    595. {
    596. n = 16 * n + (10 + tolower(s[i]) - 'a');
    597. }
    598. else
    599. {
    600. n = 16 * n + (tolower(s[i]) - '0');
    601. }
    602. j++;
    603. }
    604. return n;
    605. }
    606.  
    607.  
    608. #endif
    609. </pre>
    610. <br>
    611. 出处http://blog.csdn.net/xxdddail/article/details/19070149
    612. <p> </p>
    613. </buflen;i++)>

TCP连接websocket服务端数据帧封装如何实现?


服务端用的是java websocket api实现,客户端是用c语言实现,因为是做硬件产品,让硬件单片机采用websocket协议实现连接服务器上传数据,c的socket里实现http协议不难,构造http头就可以了,但是怎么去实现websocket呢?现在已经能够用c构造http升级到websocket协议,与服务端建立连接,但是发送数据却接收不到,是发送的数据数据帧构造的有问题。这里有一篇博客:http://jinnianshilongnian.iteye.com/blog/1899876  。讲的是websocket的数据帧封装。难点主要还在对于masking key的计算,还请懂的人指点指点呀。谢谢了先。

下面是用TCP客户端模拟包的错误信息:


阶前梧叶 阶前梧叶
发帖于 11个月前
3回/812阅
标签: <无>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
2022 / 01/ 30: 新版esptool 刷micropython固件指令不是 esptool.py cmd... 而是 esptool cmd... 即可;另外rshell 在 >= python 3.10 的时候出错解决方法可以查看:  已于2022年发布的: 第二章:修复rshell在python3.10出错 免费内容: https://edu.csdn.net/course/detail/29666 micropython语法和python3一样,编写起来非常方便。如果你快速入门单片机玩物联网而且像轻松实现各种功能,那绝力推荐使用micropython。方便易懂易学。 同时如果你懂C语音,也可以用C写好函数并编译进micropython固件里然后进入micropython调用(非必须)。 能通过WIFI联网(2.1章),也能通过sim卡使用2G/3G/4G/5G联网(4.5章)。 为实现语音控制,本教程会教大家使用tensorflow利用神经网络训练自己的语音模型并应用。为实现通过网页控制,本教程会教大家linux(debian10 nginx->uwsgi->python3->postgresql)网站前后台入门。为记录单片机传输过来的数据, 本教程会教大家入门数据库。  本教程会通过通俗易懂的比喻来讲解各种原理与思路,并手把手编写程序来实现各项功能。 本教程micropython版本是 2019年6月发布的1.11; 更多内容请看视频列表。  学习这门课程之前你需要至少掌握: 1: python3基础(变量, 循环, 函数, 常用库, 常用方法)。 本视频使用到的零件与淘宝上大致价格:     1: 超声波传感器(3)     2: MAX9814麦克风放大模块(8)     3: DHT22(15)     4: LED(0.1)     5: 8路5V低电平触发继电器(12)     6: HX1838红外接收模块(2)     7:红外发射管(0.1),HX1838红外接收板(1)     other: 电表, 排线, 面包板(2)*2,ESP32(28)  

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值