`
东边日出西边雨
  • 浏览: 258073 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

websocket通讯协议(10版本)简介

    博客分类:
  • c
 
阅读更多

前言:

 

工作中用到了websocket 协议10版本的,英文的协议请看这里:

 

http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10

 

这篇文章相当于工作的总结吧。

 

      首先, 你需要简单了解一下为什么会诞生websocket通讯协议,web上的通讯一般都是基于HTTP(超文本传输协议)的通讯,故而没有建立长时间的网络连接的方法,一般的通讯都是这样子的:

 

                                    请求

               浏览器--------------------->服务器

                      <-----------------------

                                     响应

 

      这种连接都是客户端发起的,服务器回复数据后关闭连接。

      就好像你用浏览器访问百度输入www.baidu.com后,浏览器发起请求,百度的服务器将该页面的html超文本传给你的浏览器后关闭连接。

 

      这种连接时间很短的, 而且服务器无法主动传送数据。

 

举几个例子:

      优酷, 土豆这些网站可以在网上播放电影,播放电影需要持续传送数据的,故只能内嵌flash播放器,用flash中的flash socket持续传送数据。

 

      用html, js等编写一个及时聊天软件是很困难的,关键就在于不能建立持续的连接,服务器不能主动传送数据给客户端。只能隔一段时间客户端发起请求主动询问服务器有无数据?

 

websocket可以建立稳定的连接,能解决上述的问题。

 

先说下原理,稍后会把代码文件穿上来给大家下载。

 

websocket通讯过程:

 

1.客户端发起连接请求

websocket客户端首先发起一个连接请求,发送的数据格式如下:

 

 

GET /10.15.1.218:12345/chat?key=value\r\n

HTTP/1.1\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Host: 10.15.1.218:12345\r\n
Sec-WebSocket-Origin: null\r\n
Sec-WebSocket-Key: 4tAjitqO9So2Wu8lkrsq3w==\r\n
Sec-WebSocket-Version: 8\r\n\r\n

 

这是类似于HTTP的头,注意每行数据结尾结束符是"\r\n", 最后的结束符是"\r\n\r\n"。

 

请求头第1行详解:

“GET /”后面是服务器的IP和端口(10.15.1.218:12345)必须有。'/'的后面是你自己字符串,(chat),随便你传什么,这部分是可选的。字符串 ‘?’后面是一些参数(key=value),是什么你自己定义, 这部分也是可选的。像下面这三种都是合法的:

 

                                       GET /10.15.1.218:12345\r\n

                           或者

                                       GET /10.15.1.218:12345/chat\r\n

                           或者

                                       GET /10.15.1.218:12345/chat?key=value\r\n

 

第2, 3, 4, 5, 6行:

 

HTTP/1.1\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Host: 10.15.1.218:12345\r\n
Sec-WebSocket-Origin: null\r\n

 

这些都基本是固定的格式与内容,Host: 后面是服务器(被连接者)的IP和Port。

 

第7行:

 

Sec-WebSocket-Key: 4tAjitqO9So2Wu8lkrsq3w==\r\n

      Sec-WebSocket-Key后面的那一串东西,那一串长度为24的字符串是客户端随机生成的,我们暂时叫他cli_key,服务器必须用它经过一定的运算规则生成服务器端的key,暂时叫做ser_key,然后把ser_key发回去,客户端验证正确后,握手成功!

 

第8行:

 

Sec-WebSocket-Version: 8\r\n\r\n

 

之所以版本为8的原因,我不太清楚。10版本的通讯协议中客户端发出的都是8。

 

chrome 14浏览器中实现了websocket客户端,不用自己实现。可以去下载一个,当websocket客户端用。

 

2.制作服务端的密钥

我们的服务器将key1(长度24)截取出来

 

                           4tAjitqO9So2Wu8lkrsq3w==

 

用它和自定义的一个字符串(长度36):

 

                           258EAFA5-E914-47DA-95CA-C5AB0DC85B11

连接起来,像这样:

 

                           4tAjitqO9So2Wu8lkrsq3w==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

 

然后把这一长串经过SHA-1算法加密,得到长度为20字节的二进制数据,

再将这些数据经过Base64编码,最终得到服务端的密钥,也就是ser_key:

 

                            bEVeGLZrb9fS3Rj8WzExJdCsedg=

 

3.服务端返回密钥

 

然后需要把密钥返回给客户端,完成握手,发送的数据格式如下:

 

HTTP/1.1 101 Switching Protocols\r\n

Upgrade: websocket\r\n

Connection: Upgrade\r\n

Sec-WebSocket-Accept: bEVeGLZrb9fS3Rj8WzExJdCsedg=\r\n\r\n

 

至此,算是握手成功了!

 

4.传输数据(简单的介绍数据长度小于126的数据传输,传输大于等于126字节的数据头部(head)可就不止2个字节了,去看英文文档中介绍头部的部分)

 

                            必须有掩码

           客户端----------------------->服务器

                  <-----------------------

                          掩码(可选)

 

协议中规定客户端发向服务器的数据必须有掩码,比如需要发送一个字符串“Hello”,

 

“Hello“的ascii码:

                         H        e              l            l            o

十六进制         0x48     0x65        0x6c     0x6c     0x6f

十进制             72        101          108      108       111

 

 

 

但是实际发出的数据是这样的:

 

------------------head-----掩码0----1-------2-------3-----H----e-----l------l-------o--------------

                 0x81 0x85   0x37  0xfa   0x21    0x3d 0x7f 0x9f 0x4d 0x51 0x58

 

head头部我不在这里说了,需要很多文字才能说明,自己去英文协议中相关地方查看一下吧,head后是4字节的掩码,随机生成的,再后面是数据了,你可能发现数据变了,这些数据是 hello的ascii码和掩码做异或运算算出来的。

 

运算规则是这样的, 第零个字符'H'和第零个掩码异或, 第一个字符'e'和第一个掩码异或......

 

 

其实就是用c语言表示就是res = str[n]^mask[n%4]

 

 

服务器发出数据可以有掩码, 也可以没有掩码

 

发出一个“Hello”字符串可以发出和客户端一样的数据,也可以发出像下面的无掩码的:

 

                 0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f
                                     H       e       l        l       o

 

这就是头部信息加上原始数据啦。

 

5. 关闭连接

 

这部分很简单,或许你可以去英文协议中找到它看一看。

 

分享到:
评论
4 楼 infollllll 2013-02-04  
package com;

import java.io.*;
import java.net.*;
import java.security.MessageDigest;

import sun.misc.BASE64Encoder;

class ServeOneJabber extends Thread {
  private Socket socket;
  private BufferedReader in;
  private PrintWriter out;
  String ikey;
  public ServeOneJabber(Socket s)
      throws IOException {
    socket = s;
  
    in =
      new BufferedReader(
        new InputStreamReader(
          socket.getInputStream()));
    // Enable auto-flush:
 

    start(); // Calls run()
   
   
   
  }
 
  void send(){
  try {
out =
        new PrintWriter(
          new BufferedWriter(
            new OutputStreamWriter(
              socket.getOutputStream())), true);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
     out.print("HTTP/1.1 101 Switching Protocols\r\n"+
    "Upgrade: websocket\r\n"
     +"Connection: Upgrade\r\n"
     +"Sec-WebSocket-Accept: "+getSecWebSocketAccept(ikey)+"\r\n\r\n");
    
     out.flush();
     out.close();

    
  }
 
  public String getSecWebSocketAccept(String onekey) { 
      String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 
    String key = ""; 
    key = onekey;
    key += guid; 
    try { 
        MessageDigest md = MessageDigest.getInstance("SHA-1"); 
        md.update(key.getBytes("iso-8859-1"), 0, key.length()); 
        byte[] sha1Hash = md.digest(); 
        key = base64Encode(sha1Hash); 
    } catch (Exception e) { 
      
    } 
    System.out.println(onekey+":"+key);
    return key; 
}

public static String base64Encode(byte[] input) { 
    BASE64Encoder encoder = new BASE64Encoder(); 
    String base64 = encoder.encode(input); 
    return base64; 
}

  public void run() {
System.out.println("开始传输==>");
    try {
      while (true) { 
        String str = in.readLine();
        if (str.equals("")) break;
        if(str.indexOf("Sec-WebSocket-Key: ")>-1)       
        ikey=str.substring(19);
        System.out.println(str);
       
      }
      System.out.println("<==传输完毕");
      send();
    } catch (IOException e) {
    }
  }
}




public class SKT { 
  static final int PORT = 1201;
  public static void main(String[] args)
      throws IOException {
    ServerSocket s = new ServerSocket(PORT);
    System.out.println("Server Started");
    try {
      while(true) {
        // Blocks until a connection occurs:
        Socket socket = s.accept();

        try {
          new ServeOneJabber(socket);
        } catch(IOException e) {
          // If it fails, close the socket,
          // otherwise the thread will close it:
          socket.close();
        }
      }
    } finally {
      s.close();
    }
  }
 
 
 

 
 
} ///:~

3 楼 rocksent 2012-04-17  
websocket draft10握手成功了,可是传数据一直失败,高手有php或是C版本没,或是给点指点
加出来的0x81 0x85   0x37  0xfa   0x21    0x3d 0x7f 0x9f 0x4d 0x51 0x58是怎么组成格式被send调用的,英文太差,协议看不懂啊
2 楼 guanbeilang 2011-12-18  
通过你的代码,学会了怎么从客户端接收消息,并改造成了nodejs的版本。
实在是感激不尽啊~~~
1 楼 260081876 2011-11-20  
你也没写回送啊,我能正常接收到,但是发送给客户端一直不成功,一发送客户端就自动断开了。协议看了很多次应该没错啊。郁闷,QQ56729397,有高手看到指导下

相关推荐

Global site tag (gtag.js) - Google Analytics