基于http请求以拉的方式去做服务器的推送,无论是实时性和有效字节都是差强人意的效果。

公司的im系统在与客户端的交互上实际上借助了websocket来实现服务器与客户端的事实消息推送,今天就来简单了解下这个协议,并且自己实现对websocket的响应。

可以看到在理解了tcp和http之后,websocket的设计其实并不复杂,再最开始建立链接的时候客户端实际上会进行一次http请求,只不过请求头的内容有些特别,这里我们来看下:

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: http://example.com

Sec-WebSocket-Protool: chat,superchat

Sec-WebSocket-Version:13

可以看到这个报文里包含了一些附加头信息。其中附加头信息"Upgrade: WebSocket" ,表明这是一个申请协议升级的http请求。"Sec-WebSocket-Key"是随机的,服务端会用这些数据构造出

一个SHA-1的信息摘要,把"Sec-WebSocket-Key"加上一个魔幻字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"。使用SHA-1加密,然后进行BASE-64编码,将结果作为"Sec-WebSocket-Accept"头的值,返回给客户端:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protool: chat

实现对websocket请求的响应:

public class WebsocketServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket=new ServerSocket(8080);
Socket socket= serverSocket.accept();
new Thread(new Handle(socket)).start();
}
}
public class Handle implements Runnable{
private Socket socket; Handle(Socket socket){
this.socket=socket;
} @Override
public void run() {
try {
BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
OutputStreamWriter streamWriter=new OutputStreamWriter(socket.getOutputStream());
BufferedWriter bufferedWriter=new BufferedWriter(streamWriter);
String key=null;
//读报文
while (true){
String s= reader.readLine();
System.out.println(s);
if (s.equals("")){
break;
}else{
if (s.contains("Sec-WebSocket-Key")){
String keyValue[]=s.split(":");
key=keyValue[1].trim()+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
}
}
}
//写报文
MessageDigest messageDigest=DigestUtils.getSha1Digest();
byte[] digest=messageDigest.digest(key.getBytes());
Base64 base64 = new Base64();
String finalKey=base64.encodeToString(digest);
bufferedWriter.write("HTTP/1.1 101 Switching Protocols\r\n");
bufferedWriter.write("Upgrade: websocket\r\n");
bufferedWriter.write("Connection: Upgrade\r\n");
bufferedWriter.write("Sec-WebSocket-Accept: "+finalKey+"\r\n");
bufferedWriter.write("Sec-WebSocket-Protool: chat\r\n");
bufferedWriter.write("\r\n");
bufferedWriter.write("test");
bufferedWriter.write("\r\n");
bufferedWriter.flush();
//接收数据
System.out.println("响应报文已经发送");
while (true){
String s=reader.readLine();
System.out.println(s==null);
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} }
}

建立连接的时候要注意 Sec-WebSocket-Key在做sha-1哈希时,取得时摘要 然后拿着摘要去做base64遍嘛得到Sec-WebSocket-Accept,上面代码还有问题就是连接建立之后 BufferedReader的readLine()方法在遇到\r,\r\n,\n之前会等到填满缓冲区才会被唤醒,找的很多测试用的客户端都会把换行符去掉导致线程在填满缓冲区8KB之前一直阻塞,另一个问题就是字符流的编码问题。这里更多的关注连接建立过程,连接建立之后其实就是直接用tcp传输数据了,这里不多做赘述

最新文章

  1. Leetcode: Range Addition
  2. AC日记——单词的长度 openjudge 1.7 24
  3. 解决Eclipse项目图标上的红色感叹号,红叉
  4. python反转字符串(简单方法)及简单的文件操作示例
  5. VIP - virtual IP address
  6. Java面试题-Java中的锁
  7. URAL 1306 - Sequence Median 小内存求中位数
  8. ASP.Net MVC与WebForm的区别
  9. AlarmReceiver 与IntentService的使用
  10. LLinux系统编程(10)——进程间通信之管道
  11. 程序启动缓慢-原来是hbm.xml doctype的原因
  12. Github+Hexo搭建静态博客
  13. iOS Storyboard创建APP 的国际化操作
  14. RabbitMQ --- Routing(路由)
  15. [USACO 09FEB]Fair Shuttle
  16. js或jquery实现点击某个按钮或元素显示div,点击页面其他任何地方隐藏div
  17. ASP.NET MVC5+EF6+EasyUI 后台管理系统-WebApi的用法与调试
  18. java界面设计(swing)
  19. javascript 获取节点元素的封装
  20. MAC下调试JSON接口的工具(HTTP抓包工具)

热门文章

  1. adb命令如何获取appPackage和appActivity的信息
  2. linux添加自动清空缓存
  3. Centos中部署NetCore项目(一)
  4. OpenCV计算机视觉学习(10)——图像变换(傅里叶变换,高通滤波,低通滤波)
  5. 2.1获取Git仓库-2.2记录每次更新到仓库
  6. 1.使用javax.mail, spring的JavaMailSender,springboot发送邮件
  7. flex与bison的学习
  8. python爬虫03 Urllib库
  9. 斯福赛特:中了.Devos勒索病毒。所有文件被加密了,如何解密解决?
  10. IP 层收发报文简要剖析3--ip输入报文分片重组