tl;dr
See conclusions at the very end.
Preamble
I can explain your results. I use Kubuntu, not Debian, still I believe the main points are common. To fully understand what happens it's good to analyze TCP case, then compare to UDP.
If you want to replicate my research, do not terminate nor kill nc
unless I say so or it terminates by itself. Kill nc
s when I say. It's very important. After reading the full answer you will understand the point of this.
This analysis requires multiple terminals (or tmux
, or screen
).
TCP case
At first get rid of all previous nc
processes.
killall nc
Run a listening process:
nc -l 6900
Examine the output of lsof -i :6900
. Your nc
is listed as a listening process there.
Run a connecting process:
nc localhost 6900
Examine the output of lsof -i :6900
. The two nc
s are the ends of the established connection.
Because your first nc
is not listening any more, you can run a second listening process:
nc -l 6900
and a second connecting process:
nc localhost 6900
Type something in every console where nc
runs (don't forget to hit Enter every time). You will notice there are two separate connections. You can establish a third one if you wish.
Check lsof -i :6900
again. Although the two (formerly) listening nc
s use the same port 6900
, the two connections don't mix because nc
s on the other ends use different ports. When a packet comes to the 6900
port, the kernel checks this other port and decides which of the (formerly) listening nc
s should receive it.
You can run additional (unpaired) connecting nc
:
nc localhost 6900 || echo fail
and it will fail immediately.
You still have two separate connections established. Terminate one nc
of every pair with Ctrl + C or Ctrl + D and you will notice the corresponding ends will terminate also. This is because TCP is connection-oriented. When a connection is gracefully terminated from any of its ends, the other end is notified so it can react accordingly – in this case nc
just exits.
UDP case
Important:
killall nc
Run the following commands in sequence, each in a separate console. Also check lsof -i :6900
after each one to see what happens. The commands are:
nc -ul 6900
nc -u localhost 6900
Type something in the first ("listening") nc
(hit Enter); check lsof -i :6900
; type something in the second nc
(hit Enter); check lsof -i :6900
again and note the change.
Then prepare another pair (in separate consoles):
nc -ul 6900
nc -u localhost 6900
Pass some strings back and forth to see if it works. Terminate one nc
with Ctrl + C (Ctrl + D won't work) and notice the other nc
(on the corresponding end) still runs. The other side of the "connection" doesn't know when the "connection" is "terminated". As you could see with lsof
it doesn't know the "connection" is "established" until the data starts flowing.
I quote some words here because UDP is connectionless and this is the main difference.
What can get complicated?
So far everything should have worked. It's time to start explaining your previous results when things didn't work.
Important:
killall nc
Prepare a "listening" nc
:
nc -ul 6900
Check lsof -i :6900
. The output will be like:
… UDP *:6900
Prepare a "connecting" nc
:
nc -u localhost 6900
Check lsof -i :6900
. Example output (your 54766
may vary):
… UDP *:6900
… UDP localhost:54766->localhost:6900
Pass some data from the second nc
to the first. Check lsof -i :6900
:
… UDP localhost:6900->localhost:54766
… UDP localhost:54766->localhost:6900
Terminate the second nc
with Ctrl + C. Again lsof -i :6900
:
… UDP localhost:6900->localhost:54766
This means the first nc
"talks" only to the port 54766
on the other end of the "connection". When you try to connect with a third nc
:
nc -u localhost 6900
It would most probably choose a different random port on its side. Try it. You will be able to "pass" (about) two lines, just like you did in your question, before this nc
terminates.
(Note: this nc
terminates due to ICMP packet, see this; you can capture the packet with wireshark
like I did.)
However you can force the proper port (change 54766
to fit your lsof
output):
nc -up 54766 localhost 6900
and this fourth nc
will be able to pass data! The first nc
won't see the difference. It will be as if the second nc
was never terminated.
Terminate the fourth nc
before you proceed. Leave the first one running.
The -v
option
The last mystery is: why did nc -v -u localhost 6900
exit immediately?
In the above example we had four nc
s:
- the first – "listening" one;
- the second – successfully connected, then terminated;
- the third – unable to connect due to wrong local port;
- the fourth – able to connect thanks to right (forced) local port.
In my Kubuntu nc -uv …
, if it's the second one, transmits few X
characters, probably to probe the connection. For this reason, if it's the third one, it doesn't need (about) two lines of external input to fail, it fails immediately. Having the first nc
still running, try:
nc -uv localhost 6900
It should fail. Again, you can force the right port and it will work just like the fourth nc
in the above example.
nc -uvp 54766 localhost 6900
When I invoke this, I can see X
-s printed in the console of the first nc
.
Cleaning
killall nc
Conclusions
It looks to me you had (the "listening") nc
which was already associated with a certain port on the other side of the "connection", most certainly because of another nc
(or something else) which had successfully transmitted at least one packet. Your other nc
s tried to use other ports on their side and couldn't communicate with this first one.
If I replace localhost
with 127.0.0.1
, there is progress
Irrelevant. I guess in this case you just started clean, like we did after killall nc
.