Lua Socket cannot be properly stopped by Ctrl+C - sockets

I have a standalone lua script that uses lua sockets to connect to a server via TCP IP. It uses receive call to receive data from that server. It works, however, when I try to stop it with Ctrl+C, one of the two scenarios is happening:
-If there is currently no traffic and receive is waiting, Ctrl+C will have no effect. The program will continue to run, and will have to be terminated by kill.
-If there is traffic, the program will exit with the below printout and with the socket still open and with the server not accepting another connection:
lua: luaSocketTest.lua:15: interrupted!
stack traceback:
[C]: in function 'receive'
luaSocketTest.lua:15: in function 'doWork'
luaSocketTest.lua:22: in main chunk
[C]: ?
I tried using pcall to solve the second scenario, without success. pcall doesn't return, the process still throws the error.
Sample of my program is below:
local socket = require ("socket")
local ip = "localhost"
local port = 5003
function doWork ()
print ("Starting socket: "..ip..":"..port)
client = assert(socket.connect(ip, port))
print ("Socket Accepted")
client:send("TEST TEST")
while 1 do
local byte, err = client:receive (1)
if not err then
print (byte)
end
end
end
while 1 do
local status = pcall(doWork())
print ("EXITED PCALL WITH STATUS: "..tostring(status))
if not status then client:close() end
end

This would be quite a change, but you could employ lua-ev. It allows to add Signal handlers, which is exactly what is required to react to ctrl-c.
local socket = require'socket'
-- make connect and send in blocking mode
local client = socket.connect(ip,port)
client:send('TEST TEST')
-- make client non-blocking
client:settimeout(0)
ev.IO.new(function()
repeat
local data,err,part = client:receive(10000)
print('received',data or part)
until err
end,client:getfd(),ev.READ):start(ev.Loop.default)
local ev = require'ev'
local SIGINT = 2
ev.Signal.new(function()
print('SIGINT received')
end,SIGINT):start(ev.Loop.default)
ev.Loop.default:loop()

Related

Julia Sockets TCP Connection Refused when running script, not REPL

I am following the simple TCP example found here: https://docs.julialang.org/en/v1/manual/networking-and-streams/#A-simple-TCP-example-1. The code can be seen below (I modified that from the link slightly):
using Sockets
using Printf
# listen will create a server waiting for incoming connections on the specified
# port (in this case it will be localhost::2000)
#async begin
server = listen(2000)
x :: Int = 1
while true
sock = accept(server)
#printf "Connection number %d\n" x
x += 1
end
end
for i = 1:10
connect(2000)
end
When I execute the commands within the REPL it works correctly producing the following output:
julia> #async begin
server = listen(2000)
x :: Int = 1
while true
sock = accept(server)
#printf "Connection number %d\n" x
x += 1
end
end
Task (runnable) #0x00000001169d06d0
julia> for i = 1:10
connect(2000)
end
Connection number 1
Connection number 2
Connection number 3
Connection number 4
Connection number 5
Connection number 6
Connection number 7
Connection number 9
Connection number 10
However, when I try to place these commands in a file:
using Sockets
using Printf
# listen will create a server waiting for incoming connections on the specified
# port (in this case it will be localhost::2000)
#async begin
server = listen(2000)
x :: Int = 1
while true
sock = accept(server)
#printf "Connection number %d\n" x
x += 1
end
end
for i = 1:10
connect(2000)
end
and run using julia <scriptName> or (from within the REPL) include("<scriptName>") I get the following error message:
ERROR: LoadError: IOError: connect: connection refused (ECONNREFUSED)
Stacktrace:
[1] wait_connected(::TCPSocket) at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.3/Sockets/src/Sockets.jl:520
[2] connect at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.3/Sockets/src/Sockets.jl:555 [inlined]
[3] connect at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.3/Sockets/src/Sockets.jl:541 [inlined]
[4] connect at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.3/Sockets/src/Sockets.jl:537 [inlined]
[5] top-level scope at <pathToFile>:18
[6] include at ./boot.jl:328 [inlined]
[7] include_relative(::Module, ::String) at ./loading.jl:1105
[8] include(::Module, ::String) at ./Base.jl:31
[9] include(::String) at ./client.jl:424
[10] top-level scope at REPL[1]:1
in expression starting at <pathToFile>:17
How would I run this program from a script? I am fairly new to Julia and to sockets so apologies if this is an easy question!!
You're getting an error because connect will fail if listen has not yet had time to run. As you discovered, introducing a small pause between your server and client sides fixes the issue; let's try and explain this in more details.
In the REPL-based, interactive version, as soon as the server task is created using #async, control goes back to the REPL, which performs blocking operations (such as waiting for new commands to be entered in the command line). This gives the scheduler an opportunity to yield control to the newly created task. The call to listen appears early in it, ensuring that whenever control reaches the client side again - and calls to connect are executed - the server is listening.
In the script version, the server task gets created and scheduled, but the scheduler does not have any opportunity to actually run it, since the main task does not perform any blocking call before running connect. Therefore no-one is listening to the socket when the connection is performed, and the call fails. However, any blocking call put in this place will give an opportunity for the scheduler to run the server task. This includes sleep (of arbitrarily small amounts of time, as you noted), but also any function performing I/O: a simple println("hello") would have worked too.
I would think a cleaner way to fix things would be to ensure that the call to listen is performed first, by running it synchronously beforehand:
using Sockets
using Printf
# listen will create a server waiting for incoming connections on the specified
# port (in this case it will be localhost::2000)
server = listen(2000)
#async begin
x = 1
while true
sock = accept(server)
#printf "Connection number %d\n" x
x += 1
end
end
for i = 1:10
connect(2000)
end
Now you're left with another problem: when the client loop terminates, all calls to connect have been issued, but not necessarily all corresponding calls to accept have had time to run. This means that you're likely to get truncated output such as:
Connection number 1
Connection number 2
Connection number 3
Connection number
You'd need further coordination between your tasks to correct this, but I suspect this second problem might be only related to the MWE posted here, and might not appear in your real use case.
For example, presumably the server is meant to send something to the client. The read-write operations performed on sockets in this case naturally synchronizes both tasks:
using Sockets
using Printf
# listen will create a server waiting for incoming connections on the specified
# port (in this case it will be localhost::2000)
server = listen(2000)
#async begin
x = 1
while true
sock = accept(server)
msg = "connection number $x\n"
print("Server sent: $msg")
write(sock, msg)
x += 1
end
end
for i = 1:10
sock = connect(2000)
msg = readline(sock)
println("Client received: $msg")
end
The above example correctly yields a complete (untruncated) output:
Server sent: Connection number 1
Client received: Connection number 1
Server sent: Connection number 2
Client received: Connection number 2
...
Server sent: Connection number 10
Client received: Connection number 10
So I've figured this out I believe. The server and client either need to be separated between two files, OR, a pause command (seems to be okay with an arbitrarily small pause command) needs to be in between the server and client side:
#!/Applications/Julia-1.3.app/Contents/Resources/julia/bin/julia
using Sockets
using Printf
# listen will create a server waiting for incoming connections on the specified
# port (in this case it will be localhost::2000)
port = 2000
#async begin
server = listen(IPv6(0),port)
x::Int = 1
while true
sock = accept(server)
#printf "Connection number %d\n" x
x += 1
end
end
sleep(1E-10)
for i = 1:10
connect(port)
end
This is now working as expected!

Simulate a TCP tarpit

I want to simulate test cross-platform connection failures / timeouts, starting with blocking connect()s:
#!/usr/bin/python3
import socket
s = socket.socket()
endpoint = ('localhost', 28813)
s.bind((endpoint))
# listen for connections, accept 0 connections kept waiting (backlog)
# all other connect()s should block indefinitely
s.listen(0)
for i in range(1,1000):
c = socket.socket()
c.connect(endpoint)
# print number of successfully connected sockets
print(i)
On Linux, it prints "1" and hangs indefinitely (i.e. the behavior I want).
On Windows (Server 2012), it prints "1" and aborts with a ConnectionRefusedError.
On macOS, it prints all numbers from 1 to 128 and then hangs indefinitely.
Thus, I could accept the macOS ignores the backlog parameter and just connect enough sockets for clients to block on new connections.
How can I get Windows to also block connect() attempts?
On Windows, the SO_CONDITIONAL_ACCEPT socket option allows the application to have the incoming connections wait until it's accept()ed. The constant (SO_CONDITIONAL_ACCEPT=0x3002) isn't exposed in the Python module, but can be supplied manually:
s.bind(endpoint)
s.setsockopt(socket.SOL_SOCKET, 0x3002, 1)
s.listen(0)
It's so effective that even the first connect is kept waiting.
On macOS, backlog=0 is reset to backlog=SOMAXCONN, backlog=1 keeps all connections except the first waiting.

Why does sending ^D with netcat not trigger an EOF when reading from a Unix socket?

I am trying to write a server that will read from a Unix socket:
use std::io::prelude::*;
use std::os::unix::net::{UnixListener, UnixStream};
fn main() {
let socket_name = "socket";
let listener = match UnixListener::bind(&socket_name) {
Err(err) => panic!("Failed to bind to socket: {}.", err),
Ok(stream) => stream,
};
for mut stream in listener.incoming() {
match stream {
Ok(ref mut stream) => {
let msg = read(stream);
stream.write_all(msg.as_bytes()).expect("Echo");
}
Err(err) => panic!("Error occured when listening from the stream. {}", err),
}
}
fn read(stream: &mut UnixStream) -> String {
let mut s = String::new();
stream.read_to_string(&mut s).unwrap();
s
}
}
(playground)
On the client side I use nc: nc -U socket. I send some data and end it with ^D which should be an EOF. The docs for read_to_string say:
Read all bytes until EOF in this source, appending them to buf
Expected behavior:
After sending ^D on the client side the server responds with echo
Observed behavior:
Server doesn't recognize that EOF was sent and blocks. Only when the client breaks the connection the server prints the message and panics with the broken pipeline.
I send some data and end it with ^D which should be an EOF
It is, to the input of nc. That doesn't mean that the socket itself is closed:
When netcat is faced with an EOF on its standard input, it may or may
not close the sending part of its TCP connection, depending on which
version of netcat it is.
Solved: netcat (nc) doesn’t terminate at end of transmission
ctrl+d which sends an EOF on netcat's stdin: netcat noticed the EOF. It will send no further data through the socket. However, it
continues running and reading from the socket in case the server has
more data to send.
Unix : Epoll, catch ctrl+d and ctrl+c in server
For what it's worth, I cannot reproduce your problem on macOS 10.14.1 with the system-supplied nc.
You might be able to use the -q or -w options for your version of nc:
Assuming that after sending EOF connection will stay idle, you can use -w timeout option, which works for timeout being equal to zero
netcat doesn't terminate when stdin closes
You could also try being non-interactive:
$ echo 'hello' | nc -U socket
hello
See also:
read_to_end method never returns for UnixStream
Reading from a TcpStream with Read::read_to_string hangs until the connection is closed by the remote end
I had a similar issue. Try the nc -N flag.
This flag causes nc to send a shutdown(2) of the TCP socket when the input reaches EOF.
On an older version of Ubuntu (verified on 14.04), it behaved as though -N was always passed (and nc did not support -N).
On newer versions (verified on Ubuntu 20.04), it only exhibits this behavior when the -N flag is set.

NodeMCU crashes when attempting to call net.socket:connect()

I am attempting to send a broadcast packet to a certain port, but it seems that the code gives some weird errors for which I can't find a fix.
I've tried using net.socket:connect() and then calling the "send()" method, which didn't work, then I said that I should use the net.socket:on('connection') and send there, since I suppose net.socket:connect() isn't synchronous. But that gave a weird error too...
For this code:
function sendBroadcastPacket()
bip = wifi.sta.getbroadcast()
srv = net.createConnection(net.UDP,0)
print('Trying to connect on: ', bip)
srv:connect('9001', bip)
srv:send("Broadcast packet from: "..NODE_ID, function(sent)
print("Broadcasted packet! "..sent)
end)
end
I receive the following error:
PANIC: unprotected error in call to Lua API (init.lua:24: attempt to
call method 'connect' (a nil value))
Line 24 is the srv:connect line.
After that I tried listening for the connection event first, to see if it worked that way:
function sendBroadcastPacket()
bip = wifi.sta.getbroadcast()
srv = net.createConnection(net.UDP,0)
print('Trying to connect on: ', bip)
srv:on('connection', function(sck, c)
sck:send("Broadcast packet from: "..NODE_ID, function(sent)
print("Broadcasted packet! "..sent)
end)
end)
srv:connect('9001', bip)
end
I receive the following error:
PANIC: unprotected error in call to Lua API (init.lua:24: invalid
callback name)
Line 24 is the srv:on('connection') line.
What seems to be happening here? There aren't many google results, since a lot of other people use the Arduino-IDE version(btw, is that still nodemcu or do you need a different firmware for the ESP8266?).
I am getting a broadcast IP, and it connects to the AP. I didn't post here the wifi connection part because it works, I've used it to test mqtt and http connections which worked.
The build I'm using, Lua 5.1.4 on SDK 2.1.0(116b762), has the net module included.
Your attempts all failed because you don't seem to consider that UDP is a connection-less protocol. Hence, there's no connect() or on('connection'... for UDP.
The first attempt failed because send() needs to be called on a socket and not on the connection. The second failed because the connection callback name is only available for TCP, not for UDP.
Try something like this:
function sendBroadcastPacket()
local port = 9001
local bip = wifi.sta.getbroadcast()
print(string.format("Broadcasting to %s:%d", bip, port))
net.createUDPSocket():send(port, bip, "foo bar")
end
Documentation: https://nodemcu.readthedocs.io/en/latest/en/modules/net/#netudpsocket-module

how to know if a server is listening at a port

I have created a TCP socket at one end of my application. Say the end is 1. This socket closes after about 10sec. Now the other side of my application (end 2) is allowed to connect to the above created socket. Im coding this socket app in python, so suppose my end 2 is trying to connect to the TCP socket, but the socket no longer exists, my program terminates because of some exception. I dont want that to happen. It's like there is a while loop in my end 2. So if a connection is not available it goes back and wait.
Are you handling the exception correctly ..
try:
s.connect((host,port))
except socket.error, (value,message):
if s:
s.close()
print "Could not open socket: " + message
"""Code to handle a retry"""
On getting an error .. you can retry by doing a bind and listen again.. Also you need to have retry count ..say 5 and then perhaps exit.