[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: How to know /dev/tcp/host/port is closed?
From: |
Leonid Isaev (ifax) |
Subject: |
Re: How to know /dev/tcp/host/port is closed? |
Date: |
Thu, 1 Apr 2021 01:06:48 +0000 |
On Wed, Mar 31, 2021 at 04:17:01PM -0500, Peng Yu wrote:
> See the following code. When the first printf is finished, the
> connection should have been closed. But it seems that bash does not
> know it (see the exit status code of { true >&"$fd"; } 2>/dev/null).
> Is it a bug?
No, it's not a bug, but a feature, called "half-closed" TCP sockets.
Let's pick the client-server conversation off-wire with tshark(1) (starting
from the end of your {...}:
> } {fd}<>/dev/tcp/httpbin.org/80
Here we have a TCP handshake:
-----8<-----
25 2 192.168.122.43 -> 54.166.163.67 TCP 74 59392 > http [SYN] Seq=0
Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=1043835330 TSecr=0 WS=64
26 2 54.166.163.67 -> 192.168.122.43 TCP 74 http > 59392 [SYN, ACK]
Seq=0 Ack=1 Win=26847 Len=0 MSS=1460 SACK_PERM=1 TSval=781007956
TSecr=1043835330 WS=256
27 2 192.168.122.43 -> 54.166.163.67 TCP 66 59392 > http [ACK] Seq=1
Ack=1 Win=29248 Len=0 TSval=1043835340 TSecr=781007956
----->8-----
(54.166.163.67 is one of IPs httpbin.org resolves to, and 192.168.122.43 is
my test VM)
> $ {
> header=(
> 'GET /get HTTP/1.1'
> 'Host: httpbin.org'
> 'Connection: close'
> )
> builtin printf '%s\r\n' "${header[@]}" '' >&"$fd"
Now, we send data, and the server replies (frame 38):
-----8<-----
30 2 192.168.122.43 -> 54.166.163.67 TCP 85 [TCP segment of a
reassembled PDU]
35 2 54.166.163.67 -> 192.168.122.43 TCP 66 http > 59392 [ACK] Seq=1
Ack=20 Win=26880 Len=0 TSval=781007960 TSecr=1043835346
36 2 192.168.122.43 -> 54.166.163.67 HTTP 106 GET /get HTTP/1.1
37 2 54.166.163.67 -> 192.168.122.43 TCP 66 http > 59392 [ACK] Seq=1
Ack=60 Win=26880 Len=0 TSval=781007961 TSecr=1043835353
38 2 54.166.163.67 -> 192.168.122.43 HTTP 490 HTTP/1.1 200 OK
(application/json)
----->8-----
But since we sent "Connection: close", the server closes it's part of TCP
connection with a FIN+ACK:
-----8<-----
39 2 54.166.163.67 -> 192.168.122.43 TCP 66 http > 59392 [FIN, ACK]
Seq=425 Ack=60 Win=26880 Len=0 TSval=781007962 TSecr=1043835353
40 2 192.168.122.43 -> 54.166.163.67 TCP 66 59392 > http [ACK] Seq=60
Ack=425 Win=30272 Len=0 TSval=1043835362 TSecr=781007962
----->8-----
At this point, it won't send you anymore data, but you can still write to
socket.
> IFS= read -d '' -r -t 3 response <&"$fd"
> builtin printf '>>>%s\n' "$response"
We read json returned from server.
> { true >&"$fd"; } 2>/dev/null
> echo ">>>$?"
This will succeed because you keep writing to the socket.
> builtin printf '%s\r\n' "${header[@]}" '' >&"$fd"
This too.
> IFS= read -d '' -r -t 3 response <&"$fd"
This returns nothing because EOF is reached. In fact, this is your indication
that the other side closed connection. If this wasn't the case, read (or
cat(1)) would hang.
> builtin printf '>>>%s\n' "$response"
Nothing is printf'ed.
> Since the connection is closed in the middle of the code block, is {
> ... } {fd}<>/dev/tcp/httpbin.org/80 not appropriate to be used in this
> case?
The connection is in half-closed state. You can still write to the socket, but
don't expect to hear anything back.
HTH,
--
Leonid Isaev