mirror of
https://github.com/lwip-tcpip/lwip.git
synced 2024-10-01 04:12:07 +00:00
Task #10088: Correctly implement close() vs. shutdown(). Now the application does not get any more recv callbacks after calling tcp_close(). Added tcp_shutdown().
This commit is contained in:
parent
eae9798276
commit
d9157a71bb
@ -131,6 +131,11 @@ HISTORY
|
|||||||
|
|
||||||
++ Bugfixes:
|
++ Bugfixes:
|
||||||
|
|
||||||
|
2010-02-20: Simon Goldschmidt
|
||||||
|
* tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
|
||||||
|
close() vs. shutdown(). Now the application does not get any more
|
||||||
|
recv callbacks after calling tcp_close(). Added tcp_shutdown().
|
||||||
|
|
||||||
2010-02-19: Simon Goldschmidt
|
2010-02-19: Simon Goldschmidt
|
||||||
* mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
|
* mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
|
||||||
confusion with realloc()
|
confusion with realloc()
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
This file lists major changes between release versions that require
|
This file lists major changes between release versions that require
|
||||||
ports or applications to be changed. Use it to update a port or an
|
ports or applications to be changed. Use it to update a port or an
|
||||||
application written for an older version of lwIP to correctly work
|
application written for an older version of lwIP to correctly work
|
||||||
on a newer version.
|
with newer versions.
|
||||||
|
|
||||||
|
|
||||||
(CVS HEAD)
|
(CVS HEAD)
|
||||||
@ -12,7 +12,12 @@ on a newer version.
|
|||||||
|
|
||||||
* Replaced struct ip_addr by typedef ip_addr_t.
|
* Replaced struct ip_addr by typedef ip_addr_t.
|
||||||
|
|
||||||
* Raw API: when calling tcp_abort() from a raw API TCP callback function,
|
* Raw API: Changed the semantics of tcp_close() (since it was rather a
|
||||||
|
shutdown before): Now the application does *NOT* get any calls to the
|
||||||
|
recv callback after calling tcp_close() - this means that it also does
|
||||||
|
not get the NULL-pbuf which tells it the remote side has closed, too!
|
||||||
|
|
||||||
|
* Raw API: When calling tcp_abort() from a raw API TCP callback function,
|
||||||
make sure you return ERR_ABRT to prevent accessing unallocated memory.
|
make sure you return ERR_ABRT to prevent accessing unallocated memory.
|
||||||
(ERR_ABRT now means the applicaiton has called tcp_abort!)
|
(ERR_ABRT now means the applicaiton has called tcp_abort!)
|
||||||
|
|
||||||
|
@ -112,7 +112,9 @@ tcp_tmr(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the connection held by the PCB.
|
* Closes the TX side of a connection held by the PCB.
|
||||||
|
* For tcp_close(), a RST is sent if the application didn't receive all data
|
||||||
|
* (tcp_recved() not called for all data passed to recv callback).
|
||||||
*
|
*
|
||||||
* Listening pcbs are freed and may not be referenced any more.
|
* Listening pcbs are freed and may not be referenced any more.
|
||||||
* Connection pcbs are freed if not yet connected and may not be referenced
|
* Connection pcbs are freed if not yet connected and may not be referenced
|
||||||
@ -125,15 +127,19 @@ tcp_tmr(void)
|
|||||||
* @return ERR_OK if connection has been closed
|
* @return ERR_OK if connection has been closed
|
||||||
* another err_t if closing failed and pcb is not freed
|
* another err_t if closing failed and pcb is not freed
|
||||||
*/
|
*/
|
||||||
err_t
|
static err_t
|
||||||
tcp_close(struct tcp_pcb *pcb)
|
tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
|
||||||
{
|
{
|
||||||
err_t err;
|
err_t err;
|
||||||
|
|
||||||
#if TCP_DEBUG
|
if (rst_on_unacked_data && (pcb->state != LISTEN)) {
|
||||||
LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
|
if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
|
||||||
tcp_debug_print_state(pcb->state);
|
/* Not all data received by application, send RST to tell the remote
|
||||||
#endif /* TCP_DEBUG */
|
side about this. */
|
||||||
|
tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
|
||||||
|
pcb->local_port, pcb->remote_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (pcb->state) {
|
switch (pcb->state) {
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
@ -204,6 +210,73 @@ tcp_close(struct tcp_pcb *pcb)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the connection held by the PCB.
|
||||||
|
*
|
||||||
|
* Listening pcbs are freed and may not be referenced any more.
|
||||||
|
* Connection pcbs are freed if not yet connected and may not be referenced
|
||||||
|
* any more. If a connection is established (at least SYN received or in
|
||||||
|
* a closing state), the connection is closed, and put in a closing state.
|
||||||
|
* The pcb is then automatically freed in tcp_slowtmr(). It is therefore
|
||||||
|
* unsafe to reference it (unless an error is returned).
|
||||||
|
*
|
||||||
|
* @param pcb the tcp_pcb to close
|
||||||
|
* @return ERR_OK if connection has been closed
|
||||||
|
* another err_t if closing failed and pcb is not freed
|
||||||
|
*/
|
||||||
|
err_t
|
||||||
|
tcp_close(struct tcp_pcb *pcb)
|
||||||
|
{
|
||||||
|
#if TCP_DEBUG
|
||||||
|
LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
|
||||||
|
tcp_debug_print_state(pcb->state);
|
||||||
|
#endif /* TCP_DEBUG */
|
||||||
|
|
||||||
|
/* Set a flag not to receive any more data... */
|
||||||
|
pcb->flags |= TF_RXCLOSED;
|
||||||
|
/* ... and close */
|
||||||
|
return tcp_close_shutdown(pcb, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes all or part of a full-duplex connection of this PCB to be shut down.
|
||||||
|
* This doesn't deallocate the PCB!
|
||||||
|
*
|
||||||
|
* @param pcb PCB to shutdown
|
||||||
|
* @param shut_rx shut down receive side if this is != 0
|
||||||
|
* @param shut_tx shut down send side if this is != 0
|
||||||
|
* @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
|
||||||
|
* another err_t on error.
|
||||||
|
*/
|
||||||
|
err_t
|
||||||
|
tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
|
||||||
|
{
|
||||||
|
if (pcb->state == LISTEN) {
|
||||||
|
return ERR_CONN;
|
||||||
|
}
|
||||||
|
if (shut_rx) {
|
||||||
|
/* shut down the receive side: free buffered data... */
|
||||||
|
if (pcb->refused_data != NULL) {
|
||||||
|
pbuf_free(pcb->refused_data);
|
||||||
|
pcb->refused_data = NULL;
|
||||||
|
}
|
||||||
|
/* ... and set a flag not to receive any more data */
|
||||||
|
pcb->flags |= TF_RXCLOSED;
|
||||||
|
}
|
||||||
|
if (shut_tx) {
|
||||||
|
/* This can't happen twice since if it succeeds, the pcb's state is changed.
|
||||||
|
Only close in these states as the others directly deallocate the PCB */
|
||||||
|
switch (pcb->state) {
|
||||||
|
case SYN_RCVD:
|
||||||
|
case ESTABLISHED:
|
||||||
|
case CLOSE_WAIT:
|
||||||
|
return tcp_close_shutdown(pcb, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* @todo: return another err_t if not in correct state or already shut? */
|
||||||
|
return ERR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abandons a connection and optionally sends a RST to the remote
|
* Abandons a connection and optionally sends a RST to the remote
|
||||||
* host. Deletes the local protocol control block. This is done when
|
* host. Deletes the local protocol control block. This is done when
|
||||||
|
@ -338,6 +338,11 @@ tcp_input(struct pbuf *p, struct netif *inp)
|
|||||||
/* If a FIN segment was received, we call the callback
|
/* If a FIN segment was received, we call the callback
|
||||||
function with a NULL buffer to indicate EOF. */
|
function with a NULL buffer to indicate EOF. */
|
||||||
if (recv_flags & TF_GOT_FIN) {
|
if (recv_flags & TF_GOT_FIN) {
|
||||||
|
/* correct rcv_wnd as the application won't call tcp_recved()
|
||||||
|
for the FIN's seqno */
|
||||||
|
if (pcb->rcv_wnd != TCP_WND) {
|
||||||
|
pcb->rcv_wnd++;
|
||||||
|
}
|
||||||
TCP_EVENT_RECV(pcb, NULL, ERR_OK, err);
|
TCP_EVENT_RECV(pcb, NULL, ERR_OK, err);
|
||||||
if (err == ERR_ABRT) {
|
if (err == ERR_ABRT) {
|
||||||
goto aborted;
|
goto aborted;
|
||||||
@ -576,8 +581,10 @@ tcp_process(struct tcp_pcb *pcb)
|
|||||||
return ERR_OK;
|
return ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the PCB (in)activity timer. */
|
if ((pcb->flags & TF_RXCLOSED) == 0) {
|
||||||
pcb->tmr = tcp_ticks;
|
/* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
|
||||||
|
pcb->tmr = tcp_ticks;
|
||||||
|
}
|
||||||
pcb->keep_cnt_sent = 0;
|
pcb->keep_cnt_sent = 0;
|
||||||
|
|
||||||
tcp_parseopt(pcb);
|
tcp_parseopt(pcb);
|
||||||
@ -676,10 +683,8 @@ tcp_process(struct tcp_pcb *pcb)
|
|||||||
tcp_ack_now(pcb);
|
tcp_ack_now(pcb);
|
||||||
pcb->state = CLOSE_WAIT;
|
pcb->state = CLOSE_WAIT;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
/* incorrect ACK number */
|
/* incorrect ACK number, send RST */
|
||||||
else {
|
|
||||||
/* send RST */
|
|
||||||
tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
|
tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src),
|
||||||
tcphdr->dest, tcphdr->src);
|
tcphdr->dest, tcphdr->src);
|
||||||
}
|
}
|
||||||
|
@ -167,6 +167,7 @@ struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
|
|||||||
void tcp_abandon (struct tcp_pcb *pcb, int reset);
|
void tcp_abandon (struct tcp_pcb *pcb, int reset);
|
||||||
#define tcp_abort(pcb) tcp_abandon((pcb), 1)
|
#define tcp_abort(pcb) tcp_abandon((pcb), 1)
|
||||||
err_t tcp_close (struct tcp_pcb *pcb);
|
err_t tcp_close (struct tcp_pcb *pcb);
|
||||||
|
err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
|
||||||
|
|
||||||
/* Flags for "apiflags" parameter in tcp_write and tcp_enqueue */
|
/* Flags for "apiflags" parameter in tcp_write and tcp_enqueue */
|
||||||
#define TCP_WRITE_FLAG_COPY 0x01
|
#define TCP_WRITE_FLAG_COPY 0x01
|
||||||
@ -370,6 +371,7 @@ struct tcp_pcb {
|
|||||||
#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */
|
#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */
|
||||||
#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */
|
#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */
|
||||||
#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */
|
#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */
|
||||||
|
#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */
|
||||||
#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */
|
#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */
|
||||||
#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */
|
#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */
|
||||||
#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
|
#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
|
||||||
@ -521,13 +523,13 @@ err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
|
|||||||
else (ret) = ERR_OK; \
|
else (ret) = ERR_OK; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TCP_EVENT_RECV(pcb,p,err,ret) \
|
#define TCP_EVENT_RECV(pcb,p,err,ret) \
|
||||||
do { \
|
do { \
|
||||||
if((pcb)->recv != NULL) { \
|
if(((pcb)->recv != NULL) && (!((pcb)->flags & TF_RXCLOSED))) { \
|
||||||
(ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); \
|
(ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err)); \
|
||||||
} else { \
|
} else { \
|
||||||
(ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \
|
(ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define TCP_EVENT_CONNECTED(pcb,err,ret) \
|
#define TCP_EVENT_CONNECTED(pcb,err,ret) \
|
||||||
|
Loading…
Reference in New Issue
Block a user