mikejm
How do you manually send a client a message through their established Websock?
This is an Elixir question, not a Phoenix question. I am not interested in any PubSub or Channels solution, but rather how to do this with basic Elixir.
If you have a user who has established a WebSock connection as shown here in the EchoServer example or as shown in the MyAppWeb.ConnectionTimer example, how can you utilize this websocket to send them a message from a distinct Elixir process/service?
Both the conn and the WebSock behavior are not processes. They have no process ID. Thus they have no capacity to receive messages. They are just looping on state in reaction to user triggered handle_in stimulus.
1) ELIXIR SERVICE TO CLIENT MESSAGING (EASY)
One can envision a way the conn/Websock could be made to send out messages into Elixir in response to incoming triggers easily enough:
- Create a Genserver on each new connection made and set the PID for this into the state of
conn/WebSockon its initialization. - When a specific
handle_inrequest is received, you can useProcess.sendto send that GenServer a message which it will then receive it underhandle_info
Thus we can easily allow client → separate Elixir process system communication through the WebSock.
2) ELIXIR SERVICE TO CLIENT MESSAGING (UNCLEAR?)
But what about separate Elixir process → client WebSock communication? Where such communication is not triggered by the user’s initiation, but rather our system’s?
Let’s say for whatever reason that GenServer wants to send the client a message through their websock. How can it do so? How can any process do so?
My current guess based on the fact that in this example he is running:
defp schedule_alert, do: Process.send_after(self(), :alert, :timer.seconds(15))
This suggests to me each running conn/WebSock while not a process itself is being run by and is associated with a parent process (direct Supervisor?) that is uniquely associated with it (one-to-one). So somewhere here there is a possible solution.
EXPERIMENT
I just tested debugging out the PID of the main supervisor that starts my router in Application.ex and also debugging out self() in the router itself under get / for simple localhost:port/ accesses. On every reload, a new PID is shown there, which is not the PID of the main application supervisor that started the router.
This suggests a new process is initiated on every new web request to the router. But what is this process, then, and how do we control it? Presumably it has the conn/Websock within its own state (or the conn/WebSock functions are augmenting or overriding its base functions) when the connection is upgraded so if we can control that or override its functions we can perhaps accomplish the goal?
Simpler: If we assume all conn/WebSock’s have their own unique one-to-one Supervisor PID running them given by self() within the WebSock code (which seems to be true), then we can message that PID just like in this timer websock to trigger the handle_info of the WebSock. Then based on the handle_info manually run inside that handle_in which has the capacity to message the user directly through the websock.
I am thinking that is the solution now as I work it through. I will try that next.
GOAL
I am looking for the basic Elixir solution to this. To pre-empt, yes I have tried to solve this already but can’t. I have four books on Elixir sitting on the couch next to me but none explain this. ChatGPT can’t seem to answer without telling me about Phoenix (which I’m not interested in and is not magic in any case and must follow the same laws as base Elixir).
There is obviously some way to do this that systems like Phoenix are utilizing. That’s what I want to understand.
Thanks for any help.
Marked As Solved
mikejm
Ah got it!
Working
def handle_info(msg, state) do
{:push, {:text, "HELLO"}, state}
end
Thanks! And thanks to all in this thread! That’s the end. ![]()
![]()
Also Liked
LostKobrakai
This is not correct as stated. conn is a datastructure and indeed therefore not a process.
But code on the beam cannot run outside a process. There’s always a process running the code. It could be that that you want a distinct process to the one you start out on in certain places for reasons like failure isolation and more, but you cannot run code without there being a process.
You can find the handle_info callback for the WebSock behaviour here: WebSock — WebSock v0.5.3
You can return {:push, messages(), state()} from handle_info to send a message to the websocket client in response to having handled a message. WebSock — WebSock v0.5.3
I’d strongly suggest looking at Registry to register the websock processes under a more descriptive key, which you can then use to send messages to it. This is also what is used within phoenix pubsub and further their channels implementation as described here: Matrjoschka of phoenix communication | Benjamin Milde
benwilson512
Yeah to elaborate on what @LostKobrakai said, the loop I described in the other thread is happening in a process, and that process is NOT the supervisor. It’s the process spawned by the supervisor but it is it’s own process. When you do self() you get the process ID of that specific process.
Notably you’re still using an abstraction here. If you want to actually do this with 100% built in Elixir you’d want something like Playing with Sockets and Processes in Elixir which is using a manual loop and direct management of the :gen_tcp library.
And this is where the history that @LostKobrakai provided I think should explain a lot of the educational material you’re finding. Phoenix was first published in 2013, built on the more “lower level” websocket support provided by the erlang library cowboy. As essentially “first to the field” for websocket support in Elixir, Phoenix has been the way to do websockets for a decade now. If people needed a more raw websocket handling experience Phoenix simply grew to support more control within its framework.
More recently as more modern and Elixir influenced alternatives to cowboy have been developed there’s also been an effort to create some more “mid level” websocket abstractions, but these are actually the new comers to the field.
Process Registration and PubSub
Switching topics, the main architectural piece that you’re missing here is the idea of a process registry, and the pattern of using this registry to “publish” messages out to clients. At a high level, your websocket process should be using eg Registry — Elixir v1.19.5 to give your process a “name” like a user or client ID. Then from some other part of the system if you want to send that user or client a message, you use the registry to lookup the PID of any processes associated with that user / client ID and then send the message to those processes.
If you are not sure about how to “name” your processes I want to note that this is a problem you’d face no matter what system or language you’re using. If you have 10 websocket clients connected and you want to send one of them a message from the command line or from one client to another, you need SOME way to identify and therefore distinguish one client from another.
sodapopcan
It really seems as though you are still conflating modules and processes. Modules are literally just static bags of functions (this isn’t strictly true as they can also carry metadata in the form of persisted attributes, but these are also static). use GenServer doesn’t turn a module into a process, it just adds some callbacks that the GenServer module is aware of. self() returns the pid of whatever process a function is being run in, it has nothing to do with the module itself.
This might make it a bit clearer (if not, sorry I’m trying my best, lol):
# m.ex
defmodule A do
def hi, do: self()
end
defmodule B do
def hi, do: self()
end
defmodule Server do
def start_link, do: spawn_link(&loop/0)
defp loop do
receive do
mod -> apply(mod, :hi, []) |> IO.inspect()
end
loop()
end
end
Here we have a Server that doesn’t hold any state. It receives a module name as a message and assumes it has a function called hi/0 and calls it. A and B both implement hi/0 which just returns self(). Both A.hi() and B.hi() will return the same result when called in the process running the Server.start_link function. Notably, A and B don’t implement any sort of “process behaviour.”
iex(1)> c "m.ex"
[A, B, Server]
iex(2)> pid = Server.start_link()
#PID<0.122.0>
iex(3)> send(pid, A)
#PID<0.122.0>
A
iex(4)> send(pid, B)
#PID<0.122.0>
B
As you can see, all three pids are identical despite being returned from functions in different modules.
EDIT: This is a reply to @mikejm to be clear… it sort looks like I’m responding to @benwilson512 ![]()
LostKobrakai
I also want to address this as well: Before phoenix 1.7 (the latest release at this point) websocket handling wasn’t abstracted, but handled directly through the lower level integration with cowboy. @mtrudel having built bandit was nice enough to build out all the new WebSock abstractions to make it possible to switch all of phoenix to bandit vs using cowboy as well as allowing for any future webserver implemenation to provide adapters for both http and websockets.
Hence probably all books you have would have been written before those abstractions were in place and also generally the lack of more hands on guides around it. People have been using channels for ages vs the WebSock abstraction being very new.
sodapopcan
I realize too that this is poor wording. It’s really more “a Server that starts a process.” It’s confusing because people do indeed talk about modules as if they themselves are processes, but it’d be more proper to say they implement functions to be run in a process as a server.
It could also be written like this:
defmodule Server do
def loop do
receive do
mod -> apply(mod, :hi, []) |> IO.inspect()
end
loop()
end
end
iex(2)> pid = spawn_link(&Server.loop/0)
#PID<0.122.0>
This better shows that spawn_link isn’t connected to Server in any way. spawn_link, self, etc all come from Kernel, they aren’t “methods” on each module. The compiler essentially rewrites these calls to Kernel.spawn_link etc.







