How to send message to Cowboy websocket Client?

Hi, dear all…

sorry for my english,

i’m using Cowboy websocket part to handle client messages,
here is my WebSocket Handler code…

i start server by this…

def start( _type, _args ) do

	dispatch = :cowboy_router.compile([
		{ :_,
		 [
			{ "/",		Handler_WebSocket, [] },
		 ]
		}
	])

	startResult = :cowboy.start_clear( :server, 1000, [{ :port, 8080 }], %{:env => %{:dispatch => dispatch } } )

		case startResult do
			{ :ok, pid } ->
				Logger.info "[ws] Server Started, ServicePort[#{ @servicePort }], Pid[#{inspect pid}]"
				{ :ok, pid }
			{ :error, errorCode } -> Logger.info "[ws] Server Error, #{ inspect errorCode }"
		end
	end

here is my Handler…

defmodule Handler_WebSocket do
	@behaviour :cowboy_websocket
	
	def init( req, state ) do
		Logger.info "[ws.gate:init] get connection..."
		{ :cowboy_websocket, req, cache, %{ :idle_timeout => LOH.Conn.timeoutOfIdle } }
	end

	def terminate( reason, _req, _state ) do
		Logger.info "[ws.gate:terminate] disconnected, reason:[#{inspect reason}], client[#{inspect _state }]"
		:ok
	end

	def websocket_handle({ :text, message } = _frame, state) do

		Logger.info "[ws.gate] get client send[ #{ inspect _frame} ] c[#{ inspect self() }] state[#{inspect state}]"

		# transfer to module
		{ cache, replyMessage } = MyModule.handleWebSocketBy( state, message )

		Logger.info "[ws.gate] send back to client[#{ state.ip }] message: [#{ replyMessage }]"

		{ :reply, {:text, replyMessage}, cache }
	end

	def websocket_handle(_frame, state) do
		Logger.info "[ws-websocket-handle-2] received Data[ #{ inspect _frame} ] c[#{ inspect self() }] state[#{inspect state}]"
	end

	def websocket_info({_timeout, _ref, _msg}, state) do

		Logger.warn "[ws.gate] get message from websocket_info method, msg[#{inspect _msg}]"
	end

	def websocket_info(_info, state) do
		Logger.warn "[ws.gate] websocket_info"
		{:ok, state}
	end
end

currently the websocket all perfect work,

i create another application to manage what’s message need to send to client,
i know i can store all connect client PID on websocket init, ( use like Cachex )
but when i want send message to all connected clients,
i have no idea how to do it…

anyone can tech me how to send message to client?

please help, thank you very much :smiley:

You might want to use Phoenix PubSub for this. It lets your websocket clients listen to topics, and then from somewhere else you can broadcast a message to such a topic, which is then forwarded to all clients that listen to it.

It is what Phoenix itself uses underwater to build the Phoenix Channels on top of the Cowboy websocket connections :slight_smile: .

1 Like

Hi, thanks for your reply,
current i use pure cowboy websocket module,
because i’m building game server to handle client communication,
i still need way to send message via cowboy websocket…

very thank you for advise :grinning:

Weren’t TCP or UDP better suited for a gameserver than websockets?

Remember, Websockets try to mimick TCP-behaviour over HTTP which is transported over TCP. So you have quite some overhead here.

So unless you really have to use WS I really strongly suggest to use a better suited transport.

I know, this does not answer your question, but may safe you from a lot of trouble because having chosen the “wrong” transport.

1 Like

Hi, thanks for your reply,
because i’m building Game for Html5 :smiley:

1 Like

Yeah, that definitively is in the “you have to” category of websockets. :wink:

1 Like

okay, after many testing, i got solution

just call send #PID<xxx>, "message"

then websocket_info method will catch it, then reply to client

thanks everyone

3 Likes

Remember, Websockets try to mimick TCP-behaviour over HTTP which is transported over TCP. So you have quite some overhead here.

What do you mean by that? Websockets’ only relationship to HTTP is that its handshake is interpreted by HTTP servers as an Upgrade request.

There is very little overhead over plain TCP, just look at how Websockets implemented in Cowboy.

  1. The update request is an overhead during connecting. It costs bandwith and also response time until first usable byte
  2. WS have some package overhead for their own protocol inside of the TCP payload frame, meaning less space for your actual payload
  3. WS are over HTTP, which itself is over TCP. TCP has some overhead compared to UDP to guarantee some things which sometimes aren’t necessary for games. Most games I have seen do not care that much for lost packages or out of order, but with TCP you will always get packages in order and if a package drops, you won’t get subsequent ones into your application

I just can’t understand what you mean by this.

You can’t open a WS without having the HTTP-Update-Dance, this costs time and data!

Yeah, but that doesn’t mean that one is over the other …

Having recently written a WebSocket library, maybe I can provide some insight to this conversation.

First of all, the HTTP negotiation is minimal and provides an easy way to authenticate/authorize through a reverse proxy or something with already written HTTP auth software. This was a really good design decision in my opinion.

Second the overhead of WS is minimal as well. Once the HTTP negotiation is finished, the socket is basically just a TCP socket. As for WebSocket protocol overhead, 14 bytes is the longest header with the most common being 2-6 bytes. If there’s a huge overhead compared to UDP it’s from TCP itself.

Finally, in terms of implementation details. Two-way UDP connections are actually a chore, usually you open up a HTTP/TCP connection to negotiate a two-way UDP connection (this is especially true if you want a secure UDP connection). Then you leave that TCP connection open to test for connection loss, test latency, and notify changes in the UDP connection.

Honestly, UDP is great for a lot of things. But these days, I feel like only a certain type of game really need UDP connections.

3 Likes

Wait! How do you get the pid of the websocket_handle() process? According to the cowboy docs:

Cowboy has separate processes for handling the connection and requests. Because Websocket takes over the connection, the Websocket protocol handling occurs in a different process than the request handling.

This is reflected in the different callbacks Websocket handlers have. The init/2 callback is called from the temporary request process and the websocket_ callbacks from the connection process.

Okay, got it: you need to define both init/2 and websocket_init/2 (in the same module as websocket_handle/2), and inside websocket_init/2 self() will return the pid of the process running all the websocket_* functions. You can push a message to the client by send'ing a message to the websocket pid, and that message will be handled by websocket_info(), which has the same return values as websocket_handle(), e.g. {reply, {text, Text}, State}.

yes, sorry for late reply, that is all

Thanks @7stud.

Sorry I’m reopening this old thread, but I have a short question and I said this is not worth starting a new one.

Related to cowboy_websocket behavior, it is no clear for me (as I don’t speak Erlang & the official doc is somewhat elusive - if this can be used as a web-socket server. Can it be used initiate/generate messages from the back-end, or only to relay from another ws client ?

So, re-phrasing the topic’s title:
How to send message from Cowboy to a websocket Client?

Thanks in advance,
Q.T.