I have the following code:
(ns alephtest.core
(:use lamina.core aleph.tcp aleph.formats))
(defn connection-established [socket]
(println "Socket connection established")
(on-closed socket #(println "closed"))
(doseq [line (line-seq (java.io.BufferedReader. *in*))]
(enqueue socket line)))
(defn -main []
(on-realized (tcp-client {:host "localhost" :port 9000})
connection-established
#(println "error:" %)))
All it does right now is connects to a local server and then passes data from stdin through to the server. This works fine, except for the (on-closed socket #(println "closed")) portion. If I kill the server while the client is still going I should get a message saying "closed" to the console, but I never do.
My end-goal here is to create some kind of recovery code, so that if the server goes down the client will queue up messages while it waits for the server to come back, and when the server does come back it'll reconnect and send everything that was queued.
It looks like you're blocking the thread that would notify you of the socket closing with your (doseq ...) loop. Wrap that in a (future ...), and see if that doesn't fix it.
Related
I have near no knowledge/experience with sockets of any kind so my question may just end up being due to my ignorance.
Guile doesn't have any concept of the protocol but I figure, since websockets are just HTTP requests, I should be able to at least make a request out using Guile's web modules.
I'm using wss://node2.wsninja.io from https://wsninja.io/#demo to practice on.
I wrote up
(let ([s (socket PF_INET SOCK_STREAM 0)]
[d (vector-ref (addrinfo:addr (car (getaddrinfo "echo.websocket.org" "https"))) 1)])
(connect s AF_INET d 443))
to attempt connecting. While getaddrinfo asks for a protocol (and Guile doesn't recognize "wss" or "ws"), I only really need the IP address as an integer and that's all it's really doing – extracting the integer representation; the result matches what I got verifying against a few other services on the web so it seems right.
I used PF_INET and AF_INET since, if I'm not mistaken, IPv4 makes sense for a websocket call. I went with SOCK_STREAM since a websocket'll be streaming data back and forth, once opened.
I based the above example off of the client example given by Guile here: https://www.gnu.org/software/guile/manual/html_node/Internet-Socket-Examples.html#Internet-Socket-Examples.
I suspect it's the next portion that I've got wrong somehow but I'm not sure how.
I assumed, after connecting, the next step was to initiate the handshake. Given the Guile example, it seemed this could be accomplished by displaying to the now (in theory) opened port so I did this:
(let ([s (socket PF_INET SOCK_STREAM 0)]
[d (vector-ref (addrinfo:addr (car (getaddrinfo "echo.websocket.org" "https"))) 1)])
(connect s AF_INET d 443)
(display "GET / HTTP/1.1\r\n" s)
(display "Host: localhost\r\n" s)
(display "User-Agent: Mozilla/5.0\r\n" s)
(display "Upgrade: websocket\r\n" s)
(display "\r\n" s)
(do ([line (read-line s) (read-line s)])
((eof-object? line))
(display line)
(newline)))
I also tried this with the handshake sent as one String, as well. The do loop is pretty much lifted directly from the Guile example.
While I've seen mention that the handshake should be sent as a header for Websockets, I figured it wouldn't be terribly different from HTTP and the Guile example has it sent via display but maybe that's a source of my error.
Needless to say, this doesn't work. Nothing gets displayed and the loop is exited, immediately. But it does seem like I'm making a connection because removing the displays to the socket results in it just hanging (what I assume is an unending loop because no eof-object gets sent because the socket is waiting on the handshake).
But no affirmation or even error is sent if I do send something, which I would have expected. So I'm confounded and a little out of my depth and thought I'd ask.
So I have some code like
(let ((file (open cur-fifo :if-does-not-exist :create)))
(format t "~A~%" (read-line file nil))
(close file))
Which as far as I can tell works fine, except that this will block indefinitely if no data was written to cur-fifo. I want the read to time out and return NIL if no data could be read in ~0.1 sec or so.
Running on SBCL 1.1.18, on 64-bit Gentoo Linux
FIFO model
When you open a fifo special device (for reading), the system call blocks unless either
the fifo is already opened by (another) process for writing OR
you pass O_ASYNC to open(2) - which you might not be able to do in your implementation unless you use a low level package sb-posix
When you successfully opened the fifo, your read(2) call will block until your counterparty (that opened the fifo for writing, which could be the same lisp process) writes something there.
Common Lisp
What you are looking for is listen (see also with-open-file):
(with-open-file (fifo "my-fifo" :if-does-not-exist :create)
(when (or (listen fifo)
(progn (sleep 0.1)
(listen fifo)))
(format t "Read [~A]~%" (read-line fifo))))
Debugging
Please note that special device handling is not necessarily equally well supported by all CL vendors.
If the above does not work, please do some experiments with the REPL: open the fifo, see what listen returns, write something there, see what listen reports now, &c.
If listen still returns nil even though you already wrote something into the pipe, this might mean that your CL does not recognize the file as a special device. You might have to pass some implementation-specific arguments to open, e.g., :buffering nil or something (try (describe 'open)).
You have a process tree you want to kill, so you send an exit(PID, shutdown) to the supervisor. There's other stuff you need to do, but it can't be done until this process tree is shutdown. For instance, let's say this process tree writes to a database. You want to shut everything down cleanly. You want to shut down the database, but obviously you need to shut down the process tree first, else the tree could be in the middle of a write to the database.
My question is, when I send the exit signal, is it synchronous or asynchronous? If it is synchronous, it seems I have no worries, but if it is asynchronous, I will need to do something like establish a process monitor and check whether the tree shut down before I proceed with database shutdown, correct?
Thanks.
Short answer: OTP shutdown is synchronous. exit/2 is a single asynchronous message.
Long answer: All messages in Erlang are asynchronous. The shutdown message is no different. However, there is more to shutdown than just sending a message. The supervisor listens for {'DOWN', ...} messages after sending the exit signal. Only after it receives a 'DOWN' message or times out does it proceed, so in effect it is synchronous. Checkout the supervisor source code. On line 894 is where the functions that actually makes the exit call is defined:
shutdown(Pid, Time) ->
case monitor_child(Pid) of
ok ->
exit(Pid, shutdown), %% Try to shutdown gracefully
receive
{'DOWN', _MRef, process, Pid, shutdown} ->
ok;
{'DOWN', _MRef, process, Pid, OtherReason} ->
{error, OtherReason}
after Time ->
exit(Pid, kill), %% Force termination.
receive
{'DOWN', _MRef, process, Pid, OtherReason} ->
{error, OtherReason}
end
end;
{error, Reason} ->
{error, Reason}
end.
The source code can be viewed on GitHub here: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/supervisor.erl#L894
erlang:exit/2 calls on the other hand is simply an asynchronous exit signal
If you need to manage this yourself, do your own monitoring:
sneak_attack(BankGuard) ->
monitor(process, BankGuard),
exit(BankGuard, kill),
Cash = receive {'DOWN', _, process, BankGuard, _} -> rob_bank() end,
send_to_bahamas(Cash).
In this example rob_bank() and anything after is blocked waiting on the 'DOWN' message from BankGuard.
Also, note that this is a much more general concept than just shutting something down. All messages in Erlang are asynchronous but unlike UDP, ordering (between two processes) and delivery (so long as the destination is alive) is guaranteed. So synchronous messaging is simply monitoring the target, sending a tagged message, and blocking on receipt of the return message.
I'am trying to use gen_tcp module.
There is example of server-side code, which I have troubles.
%% First, I bind server port and wait for peer connection
{ok, Sock} = gen_tcp:listen(7890, [{active, false}]),
{ok, Peer} = gen_tcp:accept(Sock),
%% Here client calls `gen_tcp:close/1` on socket and goes away.
%% After that I am tryin' send some message to client
SendResult = gen_server:send(Peer, <<"HELLO">>),
%% Now I guess that send is failed with {error, closed}, but...
ok = SendResult.
When I call gen_tcp:send/2 again, second call wil return {error, closed} as expected. But I want to understand, why first call succeeded? Am I missing some tcp-specific details?
This strange (for me) behavior is only for {active, false} connection.
In short, the reason for this is that there's no activity on the socket that can determine that the other end has closed. The first send appears to work because it operates on a socket that, for all intents and purposes, appears to be connected and operational. But that write activity determines that the other end is closed, which is why the second send fails as expected.
If you were to first read or recv from the socket, you'd quickly learn the other end was closed. Alternatively, if the socket were in an Erlang active mode, then you'd also learn of the other end closing because active modes poll the socket.
Aside from whether the socket is in an active mode or not, this has nothing to do with Erlang. If you were to write C code directly to the sockets API, for example, you'd see the same behavior.
If a tcp server and client are connected, I'd like to determine when the client is no longer connected. I thought I can simply do this by attempting to send a message to the client and once send() returns with a -1, I can then tear down the socket. This implementation works find on Windows but the minute I try doing this on Linux with BSD sockets, the call to send() on the server side app causes my server app to crash if the client is no longer connected. It doesn't even return a -1...just terminates the program.
Please explain why this is happening. Thanks in advance!
This is caused by the SIGPIPE signal. See send(2):
The send() function shall fail if:
[EPIPE]
The socket is shut down for writing, or the socket is connection-mode and is no longer connected. In the latter case, and if the socket is of type SOCK_STREAM or SOCK_SEQPACKET and the MSG_NOSIGNAL flag is not set, the SIGPIPE signal is generated to the calling thread.
You can avoid this by using the MSG_NOSIGNAL flag on the send() call, or by ignoring the SIGPIPE signal with signal(SIGPIPE, SIG_IGN) at the beginning of your program. Then the send() function will return -1 and set errno to EPIPE in this situation.
You need to ignore the SIGPIPE signal. If a write error happens on a socket, your process with get SIGPIPE, and the default behavior of that signal is to kill your process.
Writing networking code on *nix you usually want:
signal(SIGPIPE,SIG_IGN);