Denys Vlasenko | 7801148 | 2013-07-25 14:00:37 +0200 | [diff] [blame] | 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. |
Denys Vlasenko | ad546ec | 2013-07-27 14:35:51 +0200 | [diff] [blame^] | 42 | This makes kernel send FIN after all data is written: |
Denys Vlasenko | 7801148 | 2013-07-25 14:00:37 +0200 | [diff] [blame] | 43 | |
| 44 | shutdown(sock, SHUT_WR); |
| 45 | close(sock); |
| 46 | |
Denys Vlasenko | ad546ec | 2013-07-27 14:35:51 +0200 | [diff] [blame^] | 47 | However, experiments on Linux 3.9.4 show that kernel can return from |
| 48 | shutdown() and from close() before all data is sent, |
| 49 | and if peer sends any data to us after this, kernel stll responds with |
| 50 | RST before all our data is sent. |
| 51 | |
| 52 | In practice the protocol in use often does not allow peer to send |
| 53 | such data to us, in which case this solution is acceptable. |
| 54 | |
| 55 | If you know that peer is going to close its end after it sees our FIN |
| 56 | (as EOF), it might be a good idea to perform a read after shutdown(). |
| 57 | When read finishes with 0-sized result, we conclude that peer received all |
| 58 | the data, saw EOF, and closed its end. |
| 59 | |
| 60 | However, this incurs small performance penalty (we run for a longer time) |
| 61 | and requires safeguards (nonblocking reads, timeouts etc) against |
| 62 | malicious peers which don't close the connection. |
| 63 | |
Denys Vlasenko | 7801148 | 2013-07-25 14:00:37 +0200 | [diff] [blame] | 64 | |
| 65 | Defeating Nagle. |
| 66 | |
| 67 | Method #1: manually control whether partial sends are allowed: |
| 68 | |
| 69 | This prevents partially filled packets being sent: |
| 70 | |
| 71 | int state = 1; |
| 72 | setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); |
| 73 | |
| 74 | and this forces last, partially filled packet (if any) to be sent: |
| 75 | |
| 76 | int state = 0; |
| 77 | setsockopt(fd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state)); |
| 78 | |
| 79 | Method #2: make any write to immediately send data, even if it's partial: |
| 80 | |
| 81 | int state = 1; |
| 82 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &state, sizeof(state)); |