read
, write
, connect
, bind
, listen
and accept
. In addition, there is also send
and recv
, but in our examples they will behave like read
and write
.poll
in this article. Although many systems use something more efficient, such as kqueue
or epoll
, within the scope of our task all these tools will be equivalent. As for applications that use blocking operations, and not any of these mechanisms: once you understand how TCP errors affect poll
, it will be easier for you to guess what effect they will have on any blocking operation.nc(1)
on SmartOS, and I do not believe that any of the reproducible problems will be specific to a particular OS. To track system calls and collect rough information about timings, I used the truss (1) utility from the illumos project. You can get this information using dtruss (1m) under OS X or strace (1) under GNU / Linux.nc(1)
very simple program. We will use it in two modes:accept
and block until a connection is established.nc
will create a socket and establish a connection with a remote server.poll
to wait for standard input or connect a socket that has ready-to-read data. Input data are output to the terminal. The data that you enter into the terminal is sent through a socket. When you press CTRL-C, the socket closes and the process stops.kang
, and the server will be kodos
.kodos
: [root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080 Base time stamp: 1464310423.7650 [ Fri May 27 00:53:43 UTC 2016 ] 0.0027 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0 0.0028 listen(3, 1, SOV_DEFAULT) = 0 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) (sleeping...)
truss
to display the system calls that nc
makes. Time information is displayed using the - d
flag, and -t
allows you to choose which of the calls we want to see.)kang
: [root@kang ~]# truss -d -t connect,pollsys,read,write,close nc 10.88.88.140 8080 Base time stamp: 1464310447.6295 [ Fri May 27 00:54:07 UTC 2016 ] ... 0.0062 connect(3, 0x08066DD8, 16, SOV_DEFAULT) = 0 pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
kodos
we see: 23.8934 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) = 4 pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
poll
. We can see this on every system using netstat
: [root@kodos ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.140.8080 10.88.88.139.33226 1049792 0 1049800 0 ESTABLISHED ...
[root@kang ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.139.33226 10.88.88.140.8080 32806 0 1049800 0 ESTABLISHED ...
kodos
: pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...) ^C127.6307 Received signal #2, SIGINT, in pollsys() [default]
kang
: pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...) 126.1771 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1 126.1774 read(3, 0x08043670, 1024) = 0 126.1776 close(3) = 0 [root@kang ~]#
ESTABLISHED
socket is closed, the TCP stack on the kodos
sends over the FIN connection and enters the FIN_WAIT_1
.kang
receives a FIN packet, switches its own connection to the CLOSE_WAIT
state and sends an ACK in response. While the nc
client is blocking the socket - it is ready to read, the kernel wakes this thread with a POLLIN
.nc
client sees the POLLIN
for the socket and calls read
, which immediately returns 0. This means the end of the connection. nc
decides that we are done with the socket, and closes it.kodos
receives an ACK and transitions to the FIN_WAIT_2
.nc
client on kang closes its socket, the TCP stack on kang
sends FIN to kodos
. Connection to kang
goes to LAST_ACK
state.kodos
receives FIN, the connection goes to the TIME_WAIT
state, and the stack on kodos
confirms FIN.kang
receives an ACK for FIN and completely removes the connection.kodos
closes, and the stack completely removes the connection.kang
can pass through the CLOSING
state instead of FIN_WAIT_2
. [root@kodos ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.140.8080 10.88.88.139.33226 1049792 0 1049800 0 TIME_WAIT
kang
for this connection.kang
immediately discovered that the connection was closed on kodos
- it was woken from poll
, and returning zero read
signaled the end of the transmission stream. At this point, kang
decided to close the socket, which led to the closure of the connection with kodos
. We will come back to this later and see what happens if kang
does not close the socket in this situation.kodos
console kodos
the server using CTRL-C. But what happens if in the previous example we just turn off the power for kodos
? In the end, kang
will find out about this, right? [root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080 Base time stamp: 1464312528.4308 [ Fri May 27 01:28:48 UTC 2016 ] 0.0036 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0 0.0036 listen(3, 1, SOV_DEFAULT) = 0 0.2518 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) = 4 pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
[root@kang ~]# truss -d -t open,connect,pollsys,read,write,close nc 10.88.88.140 8080 Base time stamp: 1464312535.7634 [ Fri May 27 01:28:55 UTC 2016 ] ... 0.0055 connect(3, 0x08066DD8, 16, SOV_DEFAULT) = 0 pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
kang
still in the same condition: pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
kodos
shows no connection to kang
, but at the same time kang
will show a fully working connection to kodos
: [root@kang ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.139.50277 10.88.88.140.8080 32806 0 1049800 0 ESTABLISHED ...
kang
will never know that kodos
been reset.kang
trying to send data to kodos
. What will happen? pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...) kodos, are you there? 3872.6918 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1 3872.6920 read(0, " kodos , are y".., 1024) = 22 3872.6924 write(3, " kodos , are y".., 22) = 22 3872.6932 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1 3872.6932 read(3, 0x08043670, 1024) Err#131 ECONNRESET 3872.6933 close(3) = 0 [root@kang ~]#
kodos
wakes up, reads the message from stdin and sends it through the socket. Call write
successfully completed ! nc
returns to poll
, waiting for the next event, and eventually finds that the socket cannot be read without blocking, after which it calls read
. This time read
drops with ECONNRESET status. What does it mean? The read (2) documentation tells us: [ECONNRESET] , .
ECONNRESET filedes . . / filedes.
read
call. It only says that the socket has been disconnected. For this reason, most socket operations will result in an error.nc
tried to send data to kang
, the TCP stack still did not know that the connection was already dead. kang
sent a data packet to kodos
, who answered the RST because he knew nothing about the connection. kang
saw RST and cut the connection. The socket file descriptor cannot be closed, file descriptors do not work that way, but subsequent operations will fail with ECONNRESET status until nc
closes the file descriptor.kang
still found out about the disappearance of the remote side is that it sent the data and received a response indicating that there is no connection.kodos
for some reason does not respond to sending data?nc
: [root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080 Base time stamp: 1464385399.1661 [ Fri May 27 21:43:19 UTC 2016 ] 0.0030 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0 0.0031 listen(3, 1, SOV_DEFAULT) = 0 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) (sleeping...) 6.5491 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) = 4 pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
[root@kang ~]# truss -d -t open,connect,pollsys,read,write,close nc 10.88.88.140 8080 Base time stamp: 1464330881.0984 [ Fri May 27 06:34:41 UTC 2016 ] ... 0.0057 connect(3, 0x08066DD8, 16, SOV_DEFAULT) = 0 pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
kodos
and try to send data with kang
: pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...) 114.4971 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1 114.4974 read(0, "\n", 1024) = 1 114.4975 write(3, "\n", 1) = 1 pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
write
call ends normally and I see nothing for a long time. Only five minutes later appears: pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...) 425.5664 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1 425.5665 read(3, 0x08043670, 1024) Err#145 ETIMEDOUT 425.5666 close(3) = 0
read
timeout. We would see the same error with other socket operations. This is because the socket enters a state when the connection has timed out. The reason for this is that the remote side did not confirm the data packet for too long - 5 minutes, in accordance with the settings of this system.kang
may believe that it is connected to kodos
, but kodos
does not know about it. Is it possible for kang
be connected to kodos
so that kodos
does not know about it for an indefinite period of time (i.e., the problem will not be solved by itself), and there would be no power cut or restart, no other error of the kodos
operating system or network equipment?kodos
closed the socket. We said that nc on kang
read 0 (pointer to the end of the transfer) and closed the socket. Suppose the socket is left open. Obviously, it would be impossible to read from it. But nothing is said about TCP that you cannot send additional data to the party that sent you the FIN. FIN only means closing the data stream in the direction FIN was sent.nc
on kang
, because it automatically closes the socket after receiving 0. Therefore, I wrote a demo version of nc
, called dnc , that skips this moment. Also, dnc explicitly outputs the system calls it makes. This will give us a chance to track the state of TCP. [root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080 Base time stamp: 1464392924.7841 [ Fri May 27 23:48:44 UTC 2016 ] 0.0028 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0 0.0028 listen(3, 1, SOV_DEFAULT) = 0 accept(3, 0x08047B2C, 0x08047C2C, SOV_DEFAULT, 0) (sleeping...) 1.9356 accept(3, 0x08047B2C, 0x08047C2C, SOV_DEFAULT, 0) = 4 pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
[root@kang ~]# dnc 10.88.88.140 8080 2016-05-27T08:40:02Z: establishing connection 2016-05-27T08:40:02Z: connected 2016-05-27T08:40:02Z: entering poll()
[root@kodos ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.140.8080 10.88.88.139.37259 1049792 0 1049800 0 ESTABLISHED
[root@kang ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.139.37259 10.88.88.140.8080 32806 0 1049800 0 ESTABLISHED
kodos
use CTRL-C for the nc
process: pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...) ^C[root@kodos ~]#
kang
: 2016-05-27T08:40:12Z: poll returned events 0x0/0x1 2016-05-27T08:40:12Z: reading from socket 2016-05-27T08:40:12Z: read end-of-stream from socket 2016-05-27T08:40:12Z: read 0 bytes from socket 2016-05-27T08:40:12Z: entering poll()
[root@kodos ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.140.8080 10.88.88.139.37259 1049792 0 1049800 0 FIN_WAIT_2
[root@kang ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.139.37259 10.88.88.140.8080 1049792 0 1049800 0 CLOSE_WAIT
kang
. FIN_WAIT_2
indicates that kodos
received an ACK from kang
in response to the FIN sent to them, and CLOSE_WAIT
indicates that kang
received a FIN, but did not send FIN in response . This is a completely normal TCP connection state that can last indefinitely. Imagine that kodos
sent a kang
request and did not plan to send anything else; kang
can spend hours happily sending data back. Only in our case, the kodos
actually closed the socket .kodos
out that the connection completely disappears on kodos
, but still exists on kang
: [root@kang ~]# netstat -f inet -P tcp -n TCP: IPv4 Local Address Remote Address Swind Send-Q Rwind Recv-Q State ββββββββββββββββββββ ββββββββββββββββββββ βββββ βββββ- βββββ βββββ- ββββββββββ- 10.88.88.139.37259 10.88.88.140.8080 1049792 0 1049800 0 CLOSE_WAIT
kang
sends data to kodos
? Do not forget, kang
still believes that the connection is open, although on the kodos
side it is already completed. 2016-05-27T08:40:12Z: entering poll() kodos, are you there? 2016-05-27T08:41:34Z: poll returned events 0x1/0x0 2016-05-27T08:41:34Z: reading from stdin 2016-05-27T08:41:34Z: writing 22 bytes read from stdin to socket 2016-05-27T08:41:34Z: entering poll() 2016-05-27T08:41:34Z: poll returned events 0x0/0x10 2016-05-27T08:41:34Z: reading from socket dnc: read: Connection reset by peer
write()
succeeds because the TCP stack does not yet know that the connection is closed. But then comes the RST, which wakes up the thread in the poll()
, and the subsequent read()
request returns ECONNRESET.kang
does not know whether kodos
expecting to receive data from kang
, or kodos
closed the socket and is not listening on it (at least not without sending a packet). Therefore, it is not necessary to design a system that, under normal operating conditions, will use sockets in such half-open states for a long time.read()
, write()
, . - , .read()
, write()
. , RST.read()
, write()
. , , . , . , FIN KeepAlive.Source: https://habr.com/ru/post/316128/
All Articles