Web Socket

HTML5 y WebSocket

El protocolo WebSocket fue estandarizado en 2011 con el objetivo original de permitir a los navegadores crear conexiones estables y bidireccionales con un servidor. Antes de eso, los navegadores sólo soportaban HTTPRequests, lo que no es adecuado para la comunicación bidireccional.

El protocolo es bastante simple, basado en mensajes, y una herramienta muy poderosa para enviar notificaciones push a los navegadores, y se ha utilizado para implementar chats, juegos por turnos, etc. Todavía utiliza una conexión TCP, que es buena para la fiabilidad pero no para la latencia, por lo que no es buena para aplicaciones en tiempo real como VoIP y juegos de ritmo rápido (ver WebRTC para esos casos de uso).

Debido a su simplicidad, su amplia compatibilidad, y siendo más fácil de usar que una conexión TCP en bruto, WebSocket pronto comenzó a difundirse fuera de los navegadores, en aplicaciones nativas como un medio para comunicarse con los servidores de la red.

Godot soporta WebSocket tanto en exportaciones nativas como en HTML5.

Usando WebSockets en Godot

WebSocket se implementa en Godot a través de tres clases principales WebSocketClient, WebSocketServer, y WebSocketPeer. La implementación de WebSocket es compatible con el Multijugador de Alto Nivel. Ver la sección high-level multiplayer para más detalles.

Ejemplo mínimo de cliente

Este ejemplo le mostrará cómo crear una conexión WebSocket con un servidor remoto, y cómo enviar y recibir datos.

extends Node

# The URL we will connect to
export var websocket_url = "ws://echo.websocket.org"

# Our WebSocketClient instance
var _client = WebSocketClient.new()

func _ready():
    # Connect base signals to get notified of connection open, close, and errors.
    _client.connect("connection_closed", self, "_closed")
    _client.connect("connection_error", self, "_closed")
    _client.connect("connection_established", self, "_connected")
    # This signal is emitted when not using the Multiplayer API every time
    # a full packet is received.
    # Alternatively, you could check get_peer(1).get_available_packets() in a loop.
    _client.connect("data_received", self, "_on_data")

    # Initiate connection to the given URL.
    var err = _client.connect_to_url(websocket_url)
    if err != OK:
        print("Unable to connect")
        set_process(false)

func _closed(was_clean = false):
    # was_clean will tell you if the disconnection was correctly notified
    # by the remote peer before closing the socket.
    print("Closed, clean: ", was_clean)
    set_process(false)

func _connected(proto = ""):
    # This is called on connection, "proto" will be the selected WebSocket
    # sub-protocol (which is optional)
    print("Connected with protocol: ", proto)
    # You MUST always use get_peer(1).put_packet to send data to server,
    # and not put_packet directly when not using the MultiplayerAPI.
    _client.get_peer(1).put_packet("Test packet".to_utf8())

func _on_data():
    # Print the received packet, you MUST always use get_peer(1).get_packet
    # to receive data from server, and not get_packet directly when not
    # using the MultiplayerAPI.
    print("Got data from server: ", _client.get_peer(1).get_packet().get_string_from_utf8())

func _process(delta):
    # Call this in _process or _physics_process. Data transfer, and signals
    # emission will only happen when calling this function.
    _client.poll()

Esto imprimirá:

Connected with protocol:
Got data from server: Test packet

Ejemplo mínimo de servidor

Este ejemplo le mostrará cómo crear un servidor WebSocket que escuche las conexiones remotas, y cómo enviar y recibir datos.

extends Node

# The port we will listen to
const PORT = 9080
# Our WebSocketServer instance
var _server = WebSocketServer.new()

func _ready():
    # Connect base signals to get notified of new client connections,
    # disconnections, and disconnect requests.
    _server.connect("client_connected", self, "_connected")
    _server.connect("client_disconnected", self, "_disconnected")
    _server.connect("client_close_request", self, "_close_request")
    # This signal is emitted when not using the Multiplayer API every time a
    # full packet is received.
    # Alternatively, you could check get_peer(PEER_ID).get_available_packets()
    # in a loop for each connected peer.
    _server.connect("data_received", self, "_on_data")
    # Start listening on the given port.
    var err = _server.listen(PORT)
    if err != OK:
        print("Unable to start server")
        set_process(false)

func _connected(id, proto):
    # This is called when a new peer connects, "id" will be the assigned peer id,
    # "proto" will be the selected WebSocket sub-protocol (which is optional)
    print("Client %d connected with protocol: %s" % [id, proto])

func _close_request(id, code, reason):
    # This is called when a client notifies that it wishes to close the connection,
    # providing a reason string and close code.
    print("Client %d disconnecting with code: %d, reason: %s" % [id, code, reason])

func _disconnected(id, was_clean = false):
    # This is called when a client disconnects, "id" will be the one of the
    # disconnecting client, "was_clean" will tell you if the disconnection
    # was correctly notified by the remote peer before closing the socket.
    print("Client %d disconnected, clean: %s" % [id, str(was_clean)])

func _on_data(id):
    # Print the received packet, you MUST always use get_peer(id).get_packet to receive data,
    # and not get_packet directly when not using the MultiplayerAPI.
    var pkt = _server.get_peer(id).get_packet()
    print("Got data from client %d: %s ... echoing" % [id, pkt.get_string_from_utf8()])
    _server.get_peer(id).put_packet(pkt)

func _process(delta):
    # Call this in _process or _physics_process.
    # Data transfer, and signals emission will only happen when calling this function.
    _server.poll()

Esto imprimirá (cuando un cliente se conecta) algo similar a esto:

Client 1348090059 connected with protocol: selected-protocol
Got data from client 1348090059: Test packet ... echoing

Demostración avanzada de sala de chat

Una demostración de chat más avanzada que utiliza opcionalmente la abstracción multijugador de nivel medio y una demostración multijugador de alto nivel están disponibles en los proyectos de demostración de Godot en networking/websocket_chat y networking/websocket_multiplayer.