[转]websocket 连接 C Server的尝试
websocket:
它是基于 TCP链接的 全双工通讯,但与普通的TCP又不同,它提供的是消息流,而不是字节流。
基于 HTTP 长连接的“服务器推”技术
这项技术是在Ajax之后 备受追捧的一项技术,
推送技术Server
Push的基础思想是将浏览器主动查询信息改为服务器主动发送信息。服务器发送一批数据,浏览器显示这些数据,同时保证与服务器的连接。当服务器需要再次
发送一批数据时,浏览器显示数据并保持连接。以后,服务器仍然可以发送批量数据,浏览器继续显示数据,依次类推。
客户端拉曳(Client Pull)
在客户端拖曳技术中,服务器发送一批数据,在HTTP响应或文档头标记中插入指令,让浏览器“在5秒内再次装入这些数据”或“10秒内前往某URL装入数据”。当指定的时间达到时,客户端就按照服务器的指示去做,或者刷新当前数据,或者调入新的数据。
而普通的HTTP,则没有这个链接,它通过 客户端主动像Server发起链接请求,主动获取数据,但这样就会有一个致命的缺点:“会产生很多无用的HTTP请求。”
比如 Discuz论坛的短消息:
它通过JS 设置一个时间循环事件,一定时间之后会激活事件向服务器发起一个HTTP请求,取到数据通过js更改当前页面的数据。
如果在没有新数据的情况下,对于服务器来讲,这个HTTP请求就是无意义的连接,在并发量很大的站点中,会给服务器造成很大的压力。
于是…服务器推送 出现了。
它有个别名 Comet!
在client会保持一个HTTP连接到server,这个连接成功之后,server会不断的循环检测数据是否存在,如果存在,那么就像客户端发送这些数据。
它的优点显而易见。
现在已经有很多不错的技术在生产环境中使用了:
1、 通过 Flash作为中间节点,客户端与server的请求通过Flash来转发。
此方法的缺点
客户端必须安装 Flash 播放器;
因为 XMLSocket 没有 HTTP 隧道功能,XMLSocket 类不能自动穿过防火墙;
因为是使用套接口,需要设置一个通信端口,防火墙、代理服务器也可能对非 HTTP 通道端口进行限制;
2、Java Applet 套接口
在客户端使用 Java Applet,通过 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket 建立与服务器端的套接口连接,从而实现“服务器推”。
这种方案最大的不足在于 Java applet 在收到服务器端返回的信息后,无法通过 JavaScript 去更新 HTML 页面的内容。
3、基于Ajax的轮询
AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP 请求,JavaScript
响应处理函数根据服务器返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现“服务器推”与传统的 AJAX 应用不同之处在于:
服务器端会阻塞请求直到有数据传递或超时才返回。
客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。
4、基于 Iframe 及 htmlfile 的流(streaming)方式
iframe 是很早就存在的一种 HTML 标记, 通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
浏览器支持
-
Chrome Supported in version 4+
- Firefox Supported in version 4+
- Internet Explorer Supported in version 10+
- Opera Supported in version 10+
- Safari Supported in version 5+
WebSocket 概述
建立一个websocket连接,客户端发送握手请求,服务器返回握手响应,
客户端发送的数据如下:
- GET /mychat HTTP/1.1
- Host: server.example.com
- Upgrade: websocket
- Connection: Upgrade
- Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
- Sec-WebSocket-Protocol: chat
- Sec-WebSocket-Version: 13
- Origin: http://example.com
Connection: Upgrade
- Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
- Sec-WebSocket-Protocol: chat
Note that each line ends with an EOL (end of line) sequence, \n or \r\n. There must be a blank line at the end.
这个握手很像HTTP,但是实际上却不是,它允许服务器以HTTP的方式解释一部分handshake的请求,然后切换为websocket
注意,每一行结尾都要以EOL 空白符结束,\n 或者\r\n。
这里是非常重要的地方!网上资料很少,我就是在这里遇到了问题,开始建立连接之后,永远都不返回句柄,我以为只需要客户端像server发送数据,而server不用返回确认数据,看到这里我才知道我错了!
客 户端发送 base64加密的 Sec-WebSocket-Key,服务器接收到这个key之后,将258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼在 key之后,生成HSmrc0sMlYUkAGmm5OPpG2HaGWk=258EAFA5-E914-47DA-95CA- C5AB0DC85B11,然后将得到的结果进行sha-1哈希,最后base64加密,作为Sec-WebSocket-Accept的值 发送给客户端。
如果accept这个值错误的话会抛出 Error during WebSocket handshake: Sec-WebSocket-Accept mismatch
最后关闭连接。。一定要注意哦。
如图
1、构造函数 WebSocket(char *host);
var ws=new WebSocket(“ws://10.32.21.27:9880”);
连接成功后触发
连接出错触发
server端有数据返回时触发
关闭连接时触发
6、ws.send
7、ws.close
>
- <html>
- <head>
- <meta http-equiv="content-type" content="text/html" />
- <meta name="author" content="blog.anchen8.net" />
- <title>Untitled 2</title>
- <script>
- var socket;
- function connect(){
- try{
- socket=new WebSocket('ws://10.32.21.27:9880');
- }catch(e){
- alert('error');
- return;
- }
- socket.onopen = sOpen;
- socket.onerror=sError;
- socket.onmessage=sMessage;
- socket.onclose=sClose
- }
- function sOpen(){
- alert('connect success!')
- }
- function sError(){
- alert('connect error')
- }
- function sMessage(msg){
- alert('server says:'+msg)
- }
- function sClose(){
- alert('connect close')
- }
- function send(){
- socket.send('hello ,i am siren!')
- }
- function close(){
- socket.close();
- }
- </script>
- </head>
- <body>
- <input id="msg" type="text">
- <button id="connect" onclick="connect();">Connect</button>
- <button id="send" onclick="send();">Send</button>
- <button id="close" onclick="close();">Close</button>
- </body>
- </html>
* =====================================================================================
- *
- * Filename: socket.c
- *
- * Description:
- *
- * Version: 1.0
- * Created: 07/09/2012 07:00:25 PM
- * Revision: none
- * Compiler: gcc
- *
- * Author: 斯人,imsiren.com
- * Organization:
- *
- * =====================================================================================
- */
- #include <stdlib.h>
- #include <stdio.h>
- #include <arpa/inet.h>
- #include <netinet/in.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <string.h>
- #include <openssl/sha.h>
- #define PORT 9880
- #define MAXLENGTH 1024+1
- void parsestr(char *request,char *data){
- int needle;
- strcat(request,"HTTP/1.1 101 WebSocket Protocol Handshake\r\n");
- strcat(request,"Upgrade:WebSocket\r\n");
- strcat(request,"Connection:Upgrade\r\n");
- //这个值等于 base64_encode(sha1(key+258EAFA5-E914-47DA-95CA-C5AB0DC85B11)) 由于我这里没有合适的base64算法和sha1算法,所以就不写了。
- strcat(request,"Sec-WebSocket-Accept:ZmQ5OWUxMjgwMTViNTEyM2FmZTRlOGViODZkNTk3OTBjMWRiYjBiYg==\r\n");
- }
- int main(int argc,char ** argv){
- int sockfd,len,maxfd,ret,retval,newfd;
- int reuse=1;
- fd_set rwfd;
- struct sockaddr_in l_addr;
- struct sockaddr_in c_addr;
- struct timeval tv;
- char buf[MAXLENGTH];
- char request[MAXLENGTH];
- sockfd=socket(AF_INET,SOCK_STREAM,0);
- setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int));
- if(sockfd<0)
- perror("socket");
- l_addr.sin_port=htons(PORT);
- l_addr.sin_family=AF_INET;
- l_addr.sin_addr.s_addr=INADDR_ANY;
- if(bind(sockfd,(struct sockaddr*)&l_addr,sizeof(struct sockaddr))<0)
- perror("bind");
- listen(sockfd,5);
- len=sizeof(struct sockaddr);
- tv.tv_sec=5;
- tv.tv_usec=0;
- bzero(request,MAXLENGTH);
- while(1){
- newfd=accept(sockfd,(struct sockaddr*)&c_addr,&len);
- if(newfd==-1){
- perror("accept");
- exit(1);
- }else{
- printf("%s is comming\n",inet_ntoa(c_addr.sin_addr));
- }
- while(1){
- FD_ZERO(&rwfd);
- FD_SET(0,&rwfd);
- FD_SET(newfd,&rwfd);
- maxfd=0;
- if(newfd>maxfd)
- maxfd=newfd;
- retval=select(maxfd+1,&rwfd,NULL,NULL,&tv);
- if(retval==-1){
- perror("select");
- break;
- /* }else if(retval==0){
- printf("no data\n");
- continue;
- */
- }else{
- if(FD_ISSET(0,&rwfd)){
- bzero(buf,MAXLENGTH);
- fgets(buf,MAXLENGTH,stdin);
- parsestr(request,buf);
- len=send(newfd,request,MAXLENGTH,0);
- if(len>0)
- printf("i sayed:%s\n",buf);
- }
- if(FD_ISSET(newfd,&rwfd)){
- len=recv(newfd,buf,MAXLENGTH,0) ;
- if(len>0){
- if(strncmp(buf,"quit",4)==0){
- close(newfd);
- }
- }else{
- printf("client says:%s\n",buf);
- }
- }
- }
- }
- }
- return 0;
没什么特别的,只是一个简单的收发server,client客户端连接之后,
需要在server输入字符确认连接,握手才能完成哦。
开启监听,客户端连接后 如下图
我们可以看到 发过来的头内容。
一次握手之后就完成了。。。
虽然websocket非常好用,但是目前来看 很难应用到生产环境中,因为websocket是html5之后才有的,对浏览器的要求较高,上面虽然提到了浏览器支持的版本,但是都还不稳定,websocket目前只属于草案阶段,IE6/7/8甚至9都不支持。
参考文献:
http://en.wikipedia.org/wiki/WebSocket