Linux下用C编写WebSocet服务以响应HTML5的WebSocket请求
http://www.it165.net/os/html/201402/7141.html
http://itlab.idcquan.com/linux/administer/956682.html
在HTML5中新增了WebSocket,使得通讯变得更加方便。这样一来,Web与硬件的交互除了CGI和XHR的方式外,又有了一个新的方式。那么使用WebSocket又如何与下层通信呢?看看WebSocket的相关介绍就会发现,其类似于HTTP协议的通信,但又不同于HTTP协议通信,其最终使用的是TCP通信。具体的可以参照该文WebScoket 规范 + WebSocket 协议。
我们先来看看通信的效果图
下面是实现的步骤
1.建立SOCKET监听
WebSocket也是TCP通信,所以服务端需要先建立监听,下面是实现的代码。
/* server.c */
#include
#include
#include
#include
#include
#include
#include "base64.h"
#include "sha1.h"
#include "intLib.h"
#define REQUEST_LEN_MAX 1024
#define DEFEULT_SERVER_PORT 8000
#define WEB_SOCKET_KEY_LEN_MAX 256
#define RESPONSE_HEADER_LEN_MAX 1024
#define LINE_MAX 256
void shakeHand(int connfd,const char *serverKey);
char * fetchSecKey(const char * buf);
char * computeAcceptKey(const char * buf);
char * analyData(const char * buf,const int bufLen);
char * packData(const char * message,unsigned long * len);
void response(const int connfd,const char * message);
int main(int argc, char *argv[])
{
struct sockaddr_in servaddr, cliaddr;
socklen_t cliaddr_len;
int listenfd, connfd;
char buf[REQUEST_LEN_MAX];
char *data;
char str[INET_ADDRSTRLEN];
char *secWebSocketKey;
int i,n;
int connected=0;//0:not connect.1:connected.
int port= DEFEULT_SERVER_PORT;
if(argc>1)
{
port=atoi(argv[1]);
}
if(port<=0||port>0xFFFF)
{
printf("Port(%d) is out of range(1-%d)\n",port,0xFFFF);
return;
}
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(listenfd, 20);
printf("Listen %d\nAccepting connections ...\n",port);
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
printf("From %s at PORT %d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
ntohs(cliaddr.sin_port));
while (1)
{
memset(buf,0,REQUEST_LEN_MAX);
n = read(connfd, buf, REQUEST_LEN_MAX);
printf("---------------------\n");
if(0==connected)
{
printf("read:%d\n%s\n",n,buf);
secWebSocketKey=computeAcceptKey(buf);
shakeHand(connfd,secWebSocketKey);
connected=1;
continue;
}
data=analyData(buf,n);
response(connfd,data);
}
close(connfd);
}
2.握手
在建立监听后,网页向服务端发现WebSocket请求,这时需要先进行握手。握手时,客户端会在协议中包含一个握手的唯一Key,服务端在拿 到这个Key后,需要加入一个GUID,然后进行sha1的加密,再转换成base64,最后再发回到客户端。这样就完成了一次握手。此种握手方式是针对 chrome websocket 13的版本,其他版本的可能会有所不同。下面是实现的代码。
char * fetchSecKey(const char * buf)
{
char *key;
char *keyBegin;
char *flag="Sec-WebSocket-Key: ";
int i=0, bufLen=0;
key=(char *)malloc(WEB_SOCKET_KEY_LEN_MAX);
memset(key,0, WEB_SOCKET_KEY_LEN_MAX);
if(!buf)
{
return NULL;
}
keyBegin=strstr(buf,flag);
if(!keyBegin)
{
return NULL;
}
keyBegin+=strlen(flag);
bufLen=strlen(buf);
for(i=0;i
{
if(keyBegin[i]==0x0A||keyBegin[i]==0x0D)
{
break;
}
key[i]=keyBegin[i];
}
return key;
}
char * computeAcceptKey(const char * buf)
{
char * clientKey;
char * serverKey;
char * sha1DataTemp;
char * sha1Data;
short temp;
int i,n;
const char * GUID="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
if(!buf)
{
return NULL;
}
clientKey=(char *)malloc(LINE_MAX);
memset(clientKey,0,LINE_MAX);
clientKey=fetchSecKey(buf);
if(!clientKey)
{
return NULL;
}
strcat(clientKey,GUID);
sha1DataTemp=sha1_hash(clientKey);
n=strlen(sha1DataTemp);
sha1Data=(char *)malloc(n/2+1);
memset(sha1Data,0,n/2+1);
for(i=0;i
{
sha1Data[i/2]=htoi(sha1DataTemp,i,2);
}
serverKey = base64_encode(sha1Data, strlen(sha1Data));
return serverKey;
}
void shakeHand(int connfd,const char *serverKey)
{
char responseHeader [RESPONSE_HEADER_LEN_MAX];
if(!connfd)
{
return;
}
if(!serverKey)
{
return;
}
memset(responseHeader,'\0',RESPONSE_HEADER_LEN_MAX);
sprintf(responseHeader, "HTTP/1.1 101 Switching Protocols\r\n");
sprintf(responseHeader, "%sUpgrade: websocket\r\n", responseHeader);
sprintf(responseHeader, "%sConnection: Upgrade\r\n", responseHeader);
sprintf(responseHeader, "%sSec-WebSocket-Accept: %s\r\n\r\n", responseHeader, serverKey);
printf("Response Header:%s\n",responseHeader);
write(connfd,responseHeader,strlen(responseHeader));
}
注意:
1.Connection后面的值与HTTP通信时的不一样了,是Upgrade,而Upgrade又对应到了websocket,这样就标识了该通信协议是websocket的方式。
2.在sha1加密后进行base64编码时,使用sha1加密后的串必须将其当成16进制的字符串,将每两个字符合成一个新的码 (0-0xFF间)来进一步计算后,才可以进行base64换算(我开始时就在这里折腾了很久,后面才弄明白还要加上这一步),如果是直接就 base64,那就会握手失败。
3.对于sha1和base64网上有很多,后面也附上我所使用的代码。
3.数据传输
握手成功后就可以进行数据传输了,只要按照WebSocket的协议来解就可以了。下面是实现的代码
char * analyData(const char * buf,const int bufLen)
{
char * data;
char fin, maskFlag,masks[4];
char * payloadData;
char temp[8];
unsigned long n, payloadLen=0;
unsigned short usLen=0;
int i=0;
if (bufLen < 2)
{
return NULL;
}
fin = (buf[0] & 0x80) == 0x80; // 1bit,1表示最后一帧
if (!fin)
{
return NULL;// 超过一帧暂不处理
}
maskFlag = (buf[1] & 0x80) == 0x80; // 是否包含掩码
if (!maskFlag)
{
return NULL;// 不包含掩码的暂不处理
}
payloadLen = buf[1] & 0x7F; // 数据长度
if (payloadLen == 126)
{
memcpy(masks,buf+4, 4);
payloadLen =(buf[2]&0xFF) << 8 | (buf[3]&0xFF);
payloadData=(char *)malloc(payloadLen);
memset(payloadData,0,payloadLen);
memcpy(payloadData,buf+8,payloadLen);
}
else if (payloadLen == 127)
{
memcpy(masks,buf+10,4);
for ( i = 0; i < 8; i++)
{
temp[i] = buf[9 - i];
}
memcpy(&n,temp,8);
payloadData=(char *)malloc(n);
memset(payloadData,0,n);
memcpy(payloadData,buf+14,n);//toggle error(core dumped) if data is too long.
payloadLen=n;
}
else
{
memcpy(masks,buf+2,4);
payloadData=(char *)malloc(payloadLen);
memset(payloadData,0,payloadLen);
memcpy(payloadData,buf+6,payloadLen);
}
for (i = 0; i < payloadLen; i++)
{
payloadData[i] = (char)(payloadData[i] ^ masks[i % 4]);
}
printf("data(%d):%s\n",payloadLen,payloadData);
return payloadData;
}
char * packData(const char * message,unsigned long * len)
{
char * data=NULL;
unsigned long n;
n=strlen(message);
if (n < 126)
{
data=(char *)malloc(n+2);
memset(data,0,n+2);
data[0] = 0x81;
data[1] = n;
memcpy(data+2,message,n);
*len=n+2;
}
else if (n < 0xFFFF)
{
data=(char *)malloc(n+4);
memset(data,0,n+4);
data[0] = 0x81;
data[1] = 126;
data[2] = (n>>8 & 0xFF);
data[3] = (n & 0xFF);
memcpy(data+4,message,n);
*len=n+4;
}
else
{
// 暂不处理超长内容
*len=0;
}
return data;
}
void response(int connfd,const char * message)
{
char * data;
unsigned long n=0;
int i;
if(!connfd)
{
return;
}
if(!data)
{
return;
}
data=packData(message,&n);
if(!data||n<=0)
{
printf("data is empty!\n");
return;
}
write(connfd,data,n);
}
注意:
1.对于超过0xFFFF长度的数据在分析数据部分虽然作了处理,但是在memcpy时会报core dumped的错误,没有解决,请过路的大牛帮忙指点。在packData部分也未对这一部分作处理。
2.在这里碰到了一个郁闷的问题,在命名函数时,将函数名写的过长了(fetchSecWebSocketAcceptkey),结果导致编译通过,但在运行时却莫名其妙的报core dumped的错误,试了很多方法才发现是这个原因,后将名字改短后就OK了。
3.在回复数据时,只要按websocket的协议进行回应就可以了。
附上sha1、base64和intLib的代码(sha1和base64是从网上摘来的)
sha1.h
//sha1.h:对字符串进行sha1加密
#ifndef _SHA1_H_
#define _SHA1_H_
#include
#include
#include
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;
void SHA1Reset(SHA1Context *);
int SHA1Result(SHA1Context *);
void SHA1Input( SHA1Context *,const char *,unsigned);
#endif
#define SHA1CircularShift(bits,word) ((((word) << (bits)) & 0xFFFFFFFF) | ((word) >> (32-(bits))))
void SHA1ProcessMessageBlock(SHA1Context *);
void SHA1PadMessage(SHA1Context *);
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;
}
int SHA1Result(SHA1Context *context){// 成功返回1,失败返回0
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++;
}
}
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 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 sha1_hash(const char *source, char *lrvar){// Main
SHA1Context sha;
char buf[128];
SHA1Reset(&sha);
SHA1Input(&sha, source, strlen(source));
if (!SHA1Result(&sha)){
printf("SHA1 ERROR: Could not compute message digest");
return -1;
} else {
memset(buf,0,sizeof(buf));
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]);
//lr_save_string(buf, lrvar);
return strlen(buf);
}
}
*/
char * sha1_hash(const char *source){// Main
SHA1Context sha;
char *buf;//[128];
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,sizeof(buf));
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]);
//lr_save_string(buf, lrvar);
//return strlen(buf);
return buf;
}
}
base64.h
#ifndef _BASE64_H_
#define _BASE64_H_
#include
#include
#include
const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
char* base64_encode(const char* data, int data_len);
char *base64_decode(const char* data, int data_len);
static char find_pos(char ch);
/* */
char *base64_encode(const char* data, int data_len)
{
//int data_len = strlen(data);
int prepare = 0;
int ret_len;
int temp = 0;
char *ret = NULL;
char *f = NULL;
int tmp = 0;
char changed[4];
int i = 0;
ret_len = data_len / 3;
temp = data_len % 3;
if (temp > 0)
{
ret_len += 1;
}
ret_len = ret_len*4 + 1;
ret = (char *)malloc(ret_len);
if ( ret == NULL)
{
printf("No enough memory.\n");
exit(0);
}
memset(ret, 0, ret_len);
f = ret;
while (tmp < data_len)
{
temp = 0;
prepare = 0;
memset(changed, '\0', 4);
while (temp < 3)
{
//printf("tmp = %d\n", tmp);
if (tmp >= data_len)
{
break;
}
prepare = ((prepare << 8) | (data[tmp] & 0xFF));
tmp++;
temp++;
}
prepare = (prepare<<((3-temp)*8));
//printf("before for : temp = %d, prepare = %d\n", temp, prepare);
for (i = 0; i < 4 ;i++ )
{
if (temp < i)
{
changed[i] = 0x40;
}
else
{
changed[i] = (prepare>>((3-i)*6)) & 0x3F;
}
*f = base[changed[i]];
//printf("%.2X", changed[i]);
f++;
}
}
*f = '\0';
return ret;
}
/* */
static char find_pos(char ch)
{
char *ptr = (char*)strrchr(base, ch);//the last position (the only) in base[]
return (ptr - base);
}
/* */
char *base64_decode(const char *data, int data_len)
{
int ret_len = (data_len / 4) * 3;
int equal_count = 0;
char *ret = NULL;
char *f = NULL;
int tmp = 0;
int temp = 0;
char need[3];
int prepare = 0;
int i = 0;
if (*(data + data_len - 1) == '=')
{
equal_count += 1;
}
if (*(data + data_len - 2) == '=')
{
equal_count += 1;
}
if (*(data + data_len - 3) == '=')
{//seems impossible
equal_count += 1;
}
switch (equal_count)
{
case 0:
ret_len += 4;//3 + 1 [1 for NULL]
break;
case 1:
ret_len += 4;//Ceil((6*3)/8)+1
break;
case 2:
ret_len += 3;//Ceil((6*2)/8)+1
break;
case 3:
ret_len += 2;//Ceil((6*1)/8)+1
break;
}
ret = (char *)malloc(ret_len);
if (ret == NULL)
{
printf("No enough memory.\n");
exit(0);
}
memset(ret, 0, ret_len);
f = ret;
while (tmp < (data_len - equal_count))
{
temp = 0;
prepare = 0;
memset(need, 0, 4);
while (temp < 4)
{
if (tmp >= (data_len - equal_count))
{
break;
}
prepare = (prepare << 6) | (find_pos(data[tmp]));
temp++;
tmp++;
}
prepare = prepare << ((4-temp) * 6);
for (i=0; i<3 ;i++ )
{
if (i == temp)
{
break;
}
*f = (char)((prepare>>((2-i)*8)) & 0xFF);
f++;
}
}
*f = '\0';
return ret;
}
#endif
intLib.h
#ifndef _INT_LIB_H_
#define _INT_LIB_H_
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')) //判断是否有前导0x或者0X
{
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;
}
#endif
-
在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_MAX102415.#define DEFEULT_SERVER_PORT800016.#define WEB_SOCKET_KEY_LEN_MAX25617.#define RESPONSE_HEADER_LEN_MAX102418.#define LINE_MAX25619.20.21.voidshakeHand(intconnfd,constchar*serverKey);22.char* fetchSecKey(constchar* buf);23.char* computeAcceptKey(constchar* buf);24.char* analyData(constchar* buf,constintbufLen);25.char* packData(constchar* message,unsignedlong* len);26.voidresponse(constintconnfd,constchar* message);27.28.intmain(intargc,char*argv[])29.{30.struct sockaddr_in servaddr, cliaddr;31.socklen_t cliaddr_len;32.intlistenfd, connfd;33.charbuf[REQUEST_LEN_MAX];34.char*data;35.charstr[INET_ADDRSTRLEN];36.char*secWebSocketKey;37.inti,n;38.intconnected=0;//0:not connect.1:connected.39.intport= 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 %d63.Accepting connections ...64.",port);65.cliaddr_len = sizeof(cliaddr);66.connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);67.printf("From %s at PORT %d68.",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:%d84.%s85.",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(constchar* buf)002.{003.char*key;004.char*keyBegin;005.char*flag="Sec-WebSocket-Key: ";006.inti=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.returnNULL;013.}014.015.keyBegin=strstr(buf,flag);016.if(!keyBegin)017.{018.returnNULL;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><preclass="brush:java;">char* analyData(constchar* buf,constintbufLen)027.{028.char* data;029.charfin, maskFlag,masks[4];030.char* payloadData;031.chartemp[8];032.unsignedlongn, payloadLen=0;033.unsignedshortusLen=0;034.inti=0;035.036.037.if(bufLen <2)038.{039.returnNULL;040.}041.042.fin = (buf[0] &0x80) ==0x80;// 1bit,1表示最后一帧043.if(!fin)044.{045.returnNULL;// 超过一帧暂不处理046.}047.048.maskFlag = (buf[1] &0x80) ==0x80;// 是否包含掩码049.if(!maskFlag)050.{051.returnNULL;// 不包含掩码的暂不处理052.}053.054.payloadLen = buf[1] &0x7F;// 数据长度055.if(payloadLen ==126)056.{057.memcpy(masks,buf+4,4);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.elseif(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.else078.{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):%s091.",payloadLen,payloadData);092.returnpayloadData;093.}094.095.char* packData(constchar* message,unsignedlong* len)096.{097.char* data=NULL;098.unsignedlongn;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.elseif(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.else122.{123.124.// 暂不处理超长内容125.*len=0;126.}127.128.129.returndata;130.}131.132.voidresponse(intconnfd,constchar* message)133.{134.char* data;135.unsignedlongn=0;136.inti;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.<preclass="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.unsignedcharMessage_Block[64];181.intMessage_Block_Index;182.intComputed;183.intCorrupted;184.} SHA1Context;185.186.voidSHA1Reset(SHA1Context *);187.intSHA1Result(SHA1Context *);188.voidSHA1Input( SHA1Context *,constchar*,unsigned);189.#endif190.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.voidSHA1ProcessMessageBlock(SHA1Context *);195.voidSHA1PadMessage(SHA1Context *);196.197.voidSHA1Reset(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.intSHA1Result(SHA1Context *context){// 成功返回1,失败返回0214.if(context->Corrupted) {215.return0;216.}217.if(!context->Computed) {218.SHA1PadMessage(context);219.context->Computed =1;220.}221.return1;222.}223.224.225.voidSHA1Input(SHA1Context *context,constchar*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.voidSHA1ProcessMessageBlock(SHA1Context *context){253.constunsigned K[] = {0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6};254.intt;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.voidSHA1PadMessage(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){// Main342.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(constchar*source){// Main363.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.returnNULL;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.returnbuf;381.}382.}383.</string.h></stdlib.h></stdio.h></pre>384.<br>385.base64.h386.<p> </p>387.<p> </p>388.<preclass="brush:java;">#ifndef _BASE64_H_389.#define _BASE64_H_390.391.#include <stdio.h>392.#include <stdlib.h>393.#include <string.h>394.395.constcharbase[] ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";396.char* base64_encode(constchar* data,intdata_len);397.char*base64_decode(constchar* data,intdata_len);398.staticcharfind_pos(charch);399.400./* */401.char*base64_encode(constchar* data,intdata_len)402.{403.//int data_len = strlen(data);404.intprepare =0;405.intret_len;406.inttemp =0;407.char*ret = NULL;408.char*f = NULL;409.inttmp =0;410.charchanged[4];411.inti =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 = %d437.", 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 = %d448.", temp, prepare);449.for(i =0; i <4;i++ )450.{451.if(temp < i)452.{453.changed[i] =0x40;454.}455.else456.{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.returnret;467.468.}469./* */470.staticcharfind_pos(charch)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(constchar*data,intdata_len)477.{478.intret_len = (data_len /4) *3;479.intequal_count =0;480.char*ret = NULL;481.char*f = NULL;482.inttmp =0;483.inttemp =0;484.charneed[3];485.intprepare =0;486.inti =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 impossible497.equal_count +=1;498.}499.switch(equal_count)500.{501.case0:502.ret_len +=4;//3 + 1 [1 for NULL]503.break;504.case1:505.ret_len +=4;//Ceil((6*3)/8)+1506.break;507.case2:508.ret_len +=3;//Ceil((6*2)/8)+1509.break;510.case3:511.ret_len +=2;//Ceil((6*1)/8)+1512.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,0,4);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.returnret;551.}552.553.#endif554.</string.h></stdlib.h></stdio.h></pre>555.<br>556.intLib.h557.<p> </p>558.<p> </p>559.<preclass="brush:java;">#ifndef _INT_LIB_H_560.#define _INT_LIB_H_561.inttolower(intc)562.{563.if(c >='A'&& c <='Z')564.{565.returnc +'a'-'A';566.}567.else568.{569.returnc;570.}571.}572.573.inthtoi(constchars[],intstart,intlen)574.{575.inti,j;576.intn =0;577.if(s[0] =='0'&& (s[1]=='x'|| s[1]=='X'))//判断是否有前导0x或者0X578.{579.i =2;580.}581.else582.{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.else599.{600.n =16* n + (tolower(s[i]) -'0');601.}602.j++;603.}604.returnn;605.}606.607.608.#endif609.</pre>610.<br>611.出处http://blog.csdn.net/xxdddail/article/details/19070149612.<p> </p>613.</buflen;i++)>

浙公网安备 33010602011771号