1. Websockets

Making long-lived client-server connections over a TCP.

1.1. Websocket Server (without SSL)

We can consider the sockets to behave a lot like channels - the on_message is the block to execute when a message comes in.

code/crystal/src/web_sockets/server.cr
require "http/server"

# track the open websockets
SOCKETS = [] of HTTP::WebSocket

ws_handler = HTTP::WebSocketHandler.new do |socket|

  # log each new connection opened
  puts "Socket opened by client"

  # keep track of connections to send to
  SOCKETS << socket

  # listen for incoming messages on the socket
  socket.on_message do |message|
    # 'log' the messages on the server side
    puts message

    # send message to all connections
    SOCKETS.each { |socket| socket.send message }
  end

  # Clean up any references
  socket.on_close do
    SOCKETS.delete(socket)
  end

  # `.run` is called on the WebSocket automatically when this block returns
end

# define the server as the ws_handler block defined above
server = HTTP::Server.new([ws_handler])

# connect the websocket server to the TCP stack
address = server.bind_tcp "localhost", 3030

# Listen until '^c' is entered
puts "Listening on ws://#{address}"
server.listen

Run using (make only one):

crystal run crystal/src/web_sockets/server.cr

Now you should see a few messages and no prompt - the server is running (in the foreground).

1.2. Websocket Client (without SSL)

Now we can open 2 (or more) new terminals - ideally arraign these so you can see all 3 at once.

code/crystal/src/web_sockets/client.cr
# allow listen in background and accept new inputs too
require "http/web_socket"

# setup the websocket to connect to
uri    = URI.parse("ws://localhost:3030")
socket = HTTP::WebSocket.new(uri)

# code to processes messages when received from socket
socket.on_message do |message|
  puts message
end

# starts listening to the socket
# spawned so it backgrounds - then we can also enter messages
spawn socket.run

# get user name for identification
print "Name: "
name = gets

# send a notice you logged in
socket.send "Logged in: #{name}"

# loop until user quits with 'q'
loop do
  puts "enter a message or 'q' to end.\n> "
  mesg = gets
  # quit the program if 'q' is entered
  break if mesg == "q" || mesg == "Q"

  # send typed message to the socket for all to see
  socket.send "#{name}: #{mesg}"
end

Run using (in the other terminals):

crystal run crystal/src/web_sockets/client.cr

Now you can start typing messages and they will be shared to other connected sockets.

1.3. Websocket Server (with SSL)

1.4. Websocket Client (with SSL)