net/tcp(unbuffered): retransmit only one the earliest not acknowledged segment

(according to RFC 6298 (5.4)).
This commit is contained in:
Alexander Lunev 2021-10-12 07:15:54 +03:00 committed by Xiang Xiao
parent bec1b0bc92
commit 1e07b6d528
3 changed files with 52 additions and 5 deletions

View File

@ -172,6 +172,9 @@ struct tcp_conn_s
uint8_t rcvseq[4]; /* The sequence number that we expect to
* receive next */
uint8_t sndseq[4]; /* The sequence number that was last sent by us */
#if !defined(CONFIG_NET_TCP_WRITE_BUFFERS)
uint32_t rexmit_seq; /* The sequence number to be retrasmitted */
#endif
uint8_t crefs; /* Reference counts on this instance */
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
uint8_t domain; /* IP domain: PF_INET or PF_INET6 */

View File

@ -316,7 +316,23 @@ void tcp_rexmit(FAR struct net_driver_s *dev, FAR struct tcp_conn_s *conn,
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
if (dev->d_sndlen > 0)
#else
if (dev->d_sndlen > 0 && conn->tx_unacked > 0)
if ((result & TCP_REXMIT) != 0 &&
dev->d_sndlen > 0 && conn->tx_unacked > 0)
{
uint32_t saveseq;
/* According to RFC 6298 (5.4), retransmit the earliest segment
* that has not been acknowledged by the TCP receiver.
*/
saveseq = tcp_getsequence(conn->sndseq);
tcp_setsequence(conn->sndseq, conn->rexmit_seq);
tcp_send(dev, conn, TCP_ACK | TCP_PSH, dev->d_sndlen + hdrlen);
tcp_setsequence(conn->sndseq, saveseq);
}
else if (dev->d_sndlen > 0 && conn->tx_unacked > 0)
#endif
{
uint32_t seq;

View File

@ -236,13 +236,41 @@ static uint16_t tcpsend_eventhandler(FAR struct net_driver_s *dev,
else if ((flags & TCP_REXMIT) != 0)
{
/* Yes.. in this case, reset the number of bytes that have been sent
* to the number of bytes that have been ACKed.
/* According to RFC 6298 (5.4), retransmit the earliest segment
* that has not been acknowledged by the TCP receiver.
*/
pstate->snd_sent = pstate->snd_acked;
/* Reconstruct the length of the earliest segment to be retransmitted */
/* Fall through to re-send data from the last that was ACKed */
uint32_t sndlen = pstate->snd_buflen - pstate->snd_acked;
if (sndlen > conn->mss)
{
sndlen = conn->mss;
}
conn->rexmit_seq = pstate->snd_isn + pstate->snd_acked;
#ifdef NEED_IPDOMAIN_SUPPORT
/* If both IPv4 and IPv6 support are enabled, then we will need to
* select which one to use when generating the outgoing packet.
* If only one domain is selected, then the setup is already in
* place and we need do nothing.
*/
tcpsend_ipselect(dev, conn);
#endif
/* Then set-up to send that amount of data. (this won't actually
* happen until the polling cycle completes).
*/
devif_send(dev,
&pstate->snd_buffer[pstate->snd_acked],
sndlen);
/* Continue waiting */
return flags;
}
/* Check for a loss of connection */