Java NIO 实现 WebSocket 协议

本文介绍了WebSocket协议,强调其允许服务端主动向客户端推送数据的优势。详细讲解了WebSocket的连接建立过程,包括HTTP升级请求及响应头的构建。还深入解析了WebSocket数据帧的格式,包括FIN、RSV、OPCODE等字段。最后,展示了使用Java NIO实现WebSocket的代码片段,实现了客户端和服务端的双向通信。
摘要由CSDN通过智能技术生成

文章参考:https://blog.csdn.net/Kurozaki_Kun/article/details/78843783

WebSocket协议

WebSocket是一种在单个TCP连接上进行全双工通信的协议。 WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket协议相比于Http协议来说,最大的特点就是可以实现服务端主动向客户端发送消息。在WebSocket出现之前,如果客户端想实时获取服务端的消息,就需要使用AJAX轮询,查询是否有消息,这样就很消耗服务器资源和带宽。但是用WebSocket就可以实现服务端主动向客户端发送数据,并且只需要占用一个TCP连接,节省了资源和带宽。

WebSocket连接建立过程

为了建立一个WebSocket连接,客户端浏览器首先要向服务器发起一个HTTP请求,这个请求和通常的HTTP请求不同,包含了一些附加的头信息,其中附加头信息“Upgrade: WebSocket” 表明这是一个申请协议升级的HTTP请求。服务器端解析这些附加的信息头,然后生成应答消息返回给客户端,客户端和服务端的WebSocket连接就建立了。之后就可以使用WebSocket协议的格式来双向发送消息。

建立连接时发送的HTTP请求头:

返回的HTTP响应头:

在响应头中的 Sec-WebSocket-Accept 时通过Sec-WebSocket-Key构造出来的。首先在Sec-WebSocket-Key后接上一个258EAFA5-E914-47DA-95CA-C5AB0DC85B11,然后再进行SHA1摘要得到160位数据在,在使用BASE64进行编码,最后得到的就是Sec-WebSocket-Accept。

WebSocket数据发送过程

WebSocket数据发送的帧格式如下所示:

FIN - 1bit

在数据发送的过程中,可能会分片发送,FIN表示是否为最后一个分片。如果发生了分片,则1表示时最后一个分片;不能再分片的情况下,这个标志总是为1。

RSV1 RSV2 RSV3 - 1bit each

用于扩展,不使用扩展时需要为全0;非零时通信双方必须协商好扩展。这里我们用不上。

OPCODE - 4bits

用于表示所传送数据的类型,也就是payload中的数据。

数值 含义
0x0 附加数据帧
0x1 文本数据帧
0x2 二进制数据帧
0x3-0x7 保留
0x8 关闭连接帧
0x9 ping帧
0xA pong帧
0xB-0xF 保留

MASK - 1bit

用于表示payload是否被进行了掩码运算,1表示使用掩码,0表示不使用掩码。从客户端发送向服务端的数据帧必须使用掩码。

Payload length 7 bits,7+16 bits or 7+64 bits

用于表示payload的长度,有以下三种情况:

Payload length 表示的大小 payload的长度
0 - 125 Payload length 大小
126 之后的2个字节表示的无符号整数
127 之后的8个字节表示的无符号整数

Masking-key - 0 or 4 bytes

32 bit长的掩码,如果MASK为1,则帧中就存在这一个字段,在解析payload时,需要进行使用32长掩码进行异或操作,之后才能得到正确结果。

Java NIO 实现

利用Java NIO 来实现一个聊天室。部分代码如下。

NIO的常规代码:

selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
while (it.hasNext()) {
   
    SelectionKey key = it.next();
    it.remove();
    if (key.isAcceptable()) {
   
        handleAccept(key);
    }

    if (key.isReadable()) {
   
        handleRead(key);
    }
}

接受连接:

public void handleAccept(SelectionKey key) {
   
    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
    SocketChannel sc;
    try {
   
        sc = ssc.accept();
        sc.configureBlocking(false);
        sc.register(selector, SelectionKey.OP_READ);
        System.out.println(String.format("[server] -- client %s connected.", sc.getRemoteAddress().toString()));
    } catch</
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值