Backend API. Server-side code: this runs in a Codename One backend, not in the app on the device.
public final class WebSocketSession
- Object
- WebSocketSession
One websocket connection: the decoder that turns bytes into messages, and the handle an application sends through.
Why the decoder is a state machine rather than a loop over whole frames
A frame arrives in as many reads as the network chooses. The header may be split
across two of them, the mask key across four, and a 10MB payload across
thousands – so every piece of state that spans a read boundary lives in a field
here: how much of the current payload is still owed, how far into the mask key
the next byte lands, whether a message is open and which opcode started it, and
how far a UTF-8 character got. pump consumes whatever has arrived and
stops cleanly when it wants more, which is what lets the same decoder be driven
by a blocking loop on a virtual thread and by one reactor turn on a pool worker.
Why this buffer is its own
The HTTP read path borrows a per-host-thread array and parses in place. That is
free for a request, which is read, served and written without stopping – and
exactly wrong for a websocket, which stops between every message by design. A
borrowed buffer held across a park has to be copied at every park, so this class
owns its buffer from the moment of upgrade and never touches Conn.buffer
again. See HttpServer.tryUpgrade.
Why sending takes a lock and checks a flag three times
Broadcast is the normal reason to run a websocket server, so a send can come
from any thread. Two writes to one descriptor would interleave frames, hence
writeLock. Worse, the descriptor can be closed under a sender and its NUMBER
reused immediately – so dead is set before the close and checked on the way
in, after registering as a writer, and again under the lock. A handle whose
connection has gone is inert rather than dangerous.
Methods
Inherited methods
Method details
getId
public long getId()getPath
public String getPath()getQuery
public String getQuery()getHandshakeHeader
public String getHandshakeHeader(String name)A header from the handshake request, case-insensitively.
Snapshotted at upgrade, because the Request it came from stops being valid the moment the handshake finishes.
getSubprotocol
public String getSubprotocol()isOpen
public boolean isOpen()getAttachment
public Object getAttachment()setAttachment
public void setAttachment(Object value)sendText
public void sendText(String value)
throws IOExceptionThrows
sendBinary
public void sendBinary(byte[] value, int offset, int length)
throws IOExceptionThrows
sendBinary
public void sendBinary(byte[] value)
throws IOExceptionThrows
sendPing
public void sendPing(byte[] payload, int offset, int length)
throws IOExceptionThrows
sendPong
public void sendPong(byte[] payload, int offset, int length)
throws IOExceptionThrows
close
public void close()close
public void close(int code, String reason)Starts the closing handshake.
Sends a Close frame and then waits for the peer’s, which is what lets the last message already in flight arrive. A peer that never answers is shed by the connection’s read timeout rather than held forever.
abort
public void abort()