在HTML5中新增了WebSocket,使得通讯变得更加方便。这样一来,Web与硬件的交互除了CGI和XHR的方式外,又有了一个新的方式。那么使用WebSocket又如何与下层通信呢?看看WebSocket的相关介绍就会发现,其类似于HTTP协议的通信,但又不同于HTTP协议通信,其最终使用的是TCP通信。具体的可以参照该文WebScoket 规范 + WebSocket 协议。
我们先来看看通信的效果图
下面是实现的步骤
1.建立SOCKET监听
WebSocket也是TCP通信,所以服务端需要先建立监听,下面是实现的代码。
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#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<bufLen;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);