All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkale.net.http.WebSocketRunner Maven / Gradle / Ivy

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkale.net.http;

import org.redkale.net.AsyncConnection;
import static org.redkale.net.http.WebSocket.*;
import org.redkale.net.http.WebSocketPacket.FrameType;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
import java.util.logging.*;

/**
 * WebSocket的消息接收发送器, 一个WebSocket对应一个WebSocketRunner
 *
 * 

* 详情见: https://redkale.org * * @author zhangjx */ class WebSocketRunner implements Runnable { private final WebSocketEngine engine; private final AsyncConnection channel; private final WebSocket webSocket; protected final HttpContext context; private ByteBuffer readBuffer; volatile boolean closed = false; private final BiConsumer restMessageConsumer; //主要供RestWebSocket使用 protected long lastSendTime; protected long lastReadTime; WebSocketRunner(HttpContext context, WebSocket webSocket, BiConsumer messageConsumer, AsyncConnection channel) { this.context = context; this.engine = webSocket._engine; this.webSocket = webSocket; this.restMessageConsumer = messageConsumer; this.channel = channel; this.readBuffer = context.pollBuffer(); } @Override public void run() { final boolean debug = context.getLogger().isLoggable(Level.FINEST); try { webSocket.onConnected(); channel.setReadTimeoutSeconds(300); //读取超时5分钟 if (channel.isOpen()) { final int wsmaxbody = webSocket._engine.wsmaxbody; channel.read(readBuffer, null, new CompletionHandler() { //尚未解析完的数据包 private WebSocketPacket unfinishPacket; //当接收的数据流长度大于ByteBuffer长度时, 则需要额外的ByteBuffer 辅助; private final List exBuffers = new ArrayList<>(); private final SimpleEntry halfBytes = new SimpleEntry("", null); @Override public void completed(Integer count, Void attachment1) { if (count < 1) { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner(userid=" + webSocket.getUserid() + ") abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds"); closeRunner(CLOSECODE_ILLPACKET, "read buffer count is " + count); return; } try { ByteBuffer readBuf = readBuffer; if (readBuf == null) return; //关闭后readBuffer为null lastReadTime = System.currentTimeMillis(); readBuf.flip(); WebSocketPacket onePacket = null; if (unfinishPacket != null) { if (unfinishPacket.receiveBody(webSocket, readBuf)) { //已经接收完毕 onePacket = unfinishPacket; unfinishPacket = null; for (ByteBuffer b : exBuffers) { context.offerBuffer(b); } exBuffers.clear(); } else { //需要继续接收 readBuf = context.pollBuffer(); readBuffer = readBuf; channel.read(readBuf, null, this); return; } } final List packets = new ArrayList<>(); if (onePacket != null) packets.add(onePacket); try { while (true) { WebSocketPacket packet = new WebSocketPacket().decode(context.getLogger(), webSocket, wsmaxbody, halfBytes, readBuf); if (packet == WebSocketPacket.NONE) break; //解析完毕但是buffer有多余字节 if (packet != null && !packet.isReceiveFinished()) { unfinishPacket = packet; if (readBuf.hasRemaining()) { exBuffers.add(readBuf); readBuf = context.pollBuffer(); readBuffer = readBuf; } break; } packets.add(packet); if (packet == null || !readBuf.hasRemaining()) break; } } catch (Exception e) { context.getLogger().log(Level.SEVERE, "WebSocket parse message error", e); webSocket.onOccurException(e, null); } //继续监听消息 readBuf.clear(); if (halfBytes.getValue() != null) { readBuf.put(halfBytes.getValue()); halfBytes.setValue(null); } channel.read(readBuf, null, this); //消息处理 for (final WebSocketPacket packet : packets) { if (packet == null) { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on decode WebSocketPacket, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds"); failed(null, attachment1); return; } if (packet.type == FrameType.TEXT) { try { if (packet.receiveType == WebSocketPacket.MessageType.STRING) { webSocket.onMessage((String) packet.receiveMessage, packet.last); } else { if (restMessageConsumer != null) { //主要供RestWebSocket使用 restMessageConsumer.accept(webSocket, packet.receiveMessage); } else { webSocket.onMessage(packet.receiveMessage, packet.last); } } } catch (Throwable e) { context.getLogger().log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e); } } else if (packet.type == FrameType.BINARY) { try { if (packet.receiveType == WebSocketPacket.MessageType.BYTES) { webSocket.onMessage((byte[]) packet.receiveMessage, packet.last); } else { if (restMessageConsumer != null) { //主要供RestWebSocket使用 restMessageConsumer.accept(webSocket, packet.receiveMessage); } else { webSocket.onMessage(packet.receiveMessage, packet.last); } } } catch (Throwable e) { context.getLogger().log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e); } } else if (packet.type == FrameType.PING) { try { webSocket.onPing((byte[]) packet.receiveMessage); } catch (Exception e) { context.getLogger().log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e); } } else if (packet.type == FrameType.PONG) { try { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by PONG FrameType : " + packet); webSocket.onPong((byte[]) packet.receiveMessage); } catch (Exception e) { context.getLogger().log(Level.SEVERE, "WebSocket onPong error (" + packet + ")", e); } } else if (packet.type == FrameType.CLOSE) { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner onMessage by CLOSE FrameType : " + packet); closeRunner(CLOSECODE_CLIENTCLOSE, "received CLOSE frame-type message"); return; } else { context.getLogger().log(Level.WARNING, "WebSocketRunner onMessage by unknown FrameType : " + packet); closeRunner(CLOSECODE_ILLPACKET, "received unknown frame-type message"); return; } } } catch (Exception e) { context.getLogger().log(Level.WARNING, "WebSocketRunner(userid=" + webSocket.getUserid() + ") onMessage by received error", e); closeRunner(CLOSECODE_WSEXCEPTION, "websocket-received error"); } } @Override public void failed(Throwable exc, Void attachment2) { if (exc != null) { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner read WebSocketPacket failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc); closeRunner(CLOSECODE_WSEXCEPTION, "read websocket-packet failed"); } else { closeRunner(CLOSECODE_WSEXCEPTION, "decode websocket-packet error"); } } }); } else { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort by AsyncConnection closed"); closeRunner(RETCODE_WSOCKET_CLOSED, "webSocket channel is not opened"); } } catch (Throwable e) { if (debug) context.getLogger().log(Level.FINEST, "WebSocketRunner abort on read bytes from channel, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e); closeRunner(CLOSECODE_WSEXCEPTION, "read bytes from channel error"); } } public CompletableFuture sendMessage(WebSocketPacket packet) { if (packet == null) return CompletableFuture.completedFuture(RETCODE_SEND_ILLPACKET); if (closed) return CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED); boolean debug = context.getLogger().isLoggable(Level.FINEST); //System.out.println("推送消息"); final CompletableFuture futureResult = new CompletableFuture<>(); try { ByteBuffer[] buffers = packet.sendBuffers != null ? packet.duplicateSendBuffers() : packet.encode(this.context.getBufferSupplier(), this.context.getBufferConsumer(), webSocket._engine.cryptor); //if (debug) context.getLogger().log(Level.FINEST, "wsrunner.sending websocket message: " + packet); this.lastSendTime = System.currentTimeMillis(); channel.write(buffers, buffers, new CompletionHandler() { private CompletableFuture future = futureResult; @Override public void completed(Integer result, ByteBuffer[] attachments) { if (attachments == null || closed) { if (future != null) { future.complete(RETCODE_WSOCKET_CLOSED); future = null; if (attachments != null) { for (ByteBuffer buf : attachments) { context.offerBuffer(buf); } } } return; } try { int index = -1; for (int i = 0; i < attachments.length; i++) { if (attachments[i].hasRemaining()) { index = i; break; } } if (index >= 0) { //ByteBuffer[]统一回收的可以采用此写法 channel.write(attachments, index, attachments.length - index, attachments, this); return; } if (future != null) { future.complete(0); future = null; if (attachments != null) { for (ByteBuffer buf : attachments) { context.offerBuffer(buf); } } } } catch (Exception e) { future.complete(RETCODE_SENDEXCEPTION); closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on rewrite"); context.getLogger().log(Level.WARNING, "WebSocket sendMessage abort on rewrite, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", e); } } @Override public void failed(Throwable exc, ByteBuffer[] attachments) { future.complete(RETCODE_SENDEXCEPTION); closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler"); if (exc != null) { context.getLogger().log(Level.FINE, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc); } } }); } catch (Exception t) { futureResult.complete(RETCODE_SENDEXCEPTION); closeRunner(RETCODE_SENDEXCEPTION, "websocket send message failed on channel.write"); context.getLogger().log(Level.FINE, "WebSocket sendMessage abort, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", t); } return futureResult; } public boolean isClosed() { return closed; } public CompletableFuture closeRunner(int code, String reason) { if (closed) return null; synchronized (this) { if (closed) return null; closed = true; channel.dispose(); context.offerBuffer(readBuffer); readBuffer = null; CompletableFuture future = engine.removeThenClose(webSocket); webSocket.onClose(code, reason); return future; } } private static final class QueueEntry { public final CompletableFuture future; public final WebSocketPacket packet; public QueueEntry(CompletableFuture future, WebSocketPacket packet) { this.future = future; this.packet = packet; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy