diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2013-07-25 14:00:37 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2013-07-25 14:00:37 +0200 |
commit | 7801148a816a2ab1c2f9437c8992c86722361147 (patch) | |
tree | 1da9e43efdfdee33e9ada4bbf86ddcd551ade133 /docs | |
parent | 688a7e3f0454363e1dfed481e95afcdb818b1c91 (diff) | |
download | busybox-w32-7801148a816a2ab1c2f9437c8992c86722361147.tar.gz busybox-w32-7801148a816a2ab1c2f9437c8992c86722361147.tar.bz2 busybox-w32-7801148a816a2ab1c2f9437c8992c86722361147.zip |
Add notes about TCP programming quirks.
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'docs')
-rw-r--r-- | docs/tcp.txt | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/docs/tcp.txt b/docs/tcp.txt new file mode 100644 index 000000000..7951e1c8b --- /dev/null +++ b/docs/tcp.txt | |||
@@ -0,0 +1,65 @@ | |||
1 | Some less-widely known details of TCP connections. | ||
2 | |||
3 | Properly closing the connection. | ||
4 | |||
5 | After this code sequence: | ||
6 | |||
7 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
8 | connect(sock, &remote, sizeof(remote)); | ||
9 | write(sock, buffer, 1000000); | ||
10 | |||
11 | a large block of data is only buffered by kernel, it can't be sent all at once. | ||
12 | What will happen if we close the socket? | ||
13 | |||
14 | "A host MAY implement a 'half-duplex' TCP close sequence, so that | ||
15 | an application that has called close() cannot continue to read | ||
16 | data from the connection. If such a host issues a close() call | ||
17 | while received data is still pending in TCP, or if new data is | ||
18 | received after close() is called, its TCP SHOULD send a RST | ||
19 | to show that data was lost." | ||
20 | |||
21 | IOW: if we just close(sock) now, kernel can reset the TCP connection, | ||
22 | discarding some not-yet sent data. | ||
23 | |||
24 | What can be done about it? | ||
25 | |||
26 | Solution #1: block until sending is done: | ||
27 | |||
28 | /* When enabled, a close(2) or shutdown(2) will not return until | ||
29 | * all queued messages for the socket have been successfully sent | ||
30 | * or the linger timeout has been reached. | ||
31 | */ | ||
32 | struct linger { | ||
33 | int l_onoff; /* linger active */ | ||
34 | int l_linger; /* how many seconds to linger for */ | ||
35 | } linger; | ||
36 | linger.l_onoff = 1; | ||
37 | linger.l_linger = SOME_NUM; | ||
38 | setsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)); | ||
39 | close(sock); | ||
40 | |||
41 | Solution #2: tell kernel that you are done sending. | ||
42 | This makes kernel send FIN, not RST: | ||
43 | |||
44 | shutdown(sock, SHUT_WR); | ||
45 | close(sock); | ||
46 | |||
47 | |||
48 | Defeating Nagle. | ||
49 | |||
50 | Method #1: manually control whether partial sends are allowed: | ||
51 | |||
52 | This prevents partially filled packets being sent: | ||
53 | |||
54 | int state = 1; | ||
55 | setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); | ||
56 | |||
57 | and this forces last, partially filled packet (if any) to be sent: | ||
58 | |||
59 | int state = 0; | ||
60 | setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); | ||
61 | |||
62 | Method #2: make any write to immediately send data, even if it's partial: | ||
63 | |||
64 | int state = 1; | ||
65 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state)); | ||