Bugfix in tcp-socket: there were a few corner cases when the tcp-socket state would be messed up, which is fixed with this patch

This commit is contained in:
Adam Dunkels 2015-03-24 10:49:55 +01:00
parent df2cdbbd79
commit 0f1f12fdc7
2 changed files with 63 additions and 12 deletions

View file

@ -41,6 +41,8 @@
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b))
static void relisten(struct tcp_socket *s);
LIST(socketlist); LIST(socketlist);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
PROCESS(tcp_socket_process, "TCP socket process"); PROCESS(tcp_socket_process, "TCP socket process");
@ -58,8 +60,8 @@ senddata(struct tcp_socket *s)
{ {
int len = MIN(s->output_data_max_seg, uip_mss()); int len = MIN(s->output_data_max_seg, uip_mss());
if(s->output_data_len > 0) { if(s->output_senddata_len > 0) {
len = MIN(s->output_data_len, len); len = MIN(s->output_senddata_len, len);
s->output_data_send_nxt = len; s->output_data_send_nxt = len;
uip_send(s->output_data_ptr, len); uip_send(s->output_data_ptr, len);
} }
@ -68,7 +70,7 @@ senddata(struct tcp_socket *s)
static void static void
acked(struct tcp_socket *s) acked(struct tcp_socket *s)
{ {
if(s->output_data_len > 0) { if(s->output_senddata_len > 0) {
/* Copy the data in the outputbuf down and update outputbufptr and /* Copy the data in the outputbuf down and update outputbufptr and
outputbuf_lastsent */ outputbuf_lastsent */
@ -81,8 +83,14 @@ acked(struct tcp_socket *s)
printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n", printf("tcp: acked assertion failed s->output_data_len (%d) < s->output_data_send_nxt (%d)\n",
s->output_data_len, s->output_data_len,
s->output_data_send_nxt); s->output_data_send_nxt);
tcp_markconn(uip_conn, NULL);
uip_abort();
call_event(s, TCP_SOCKET_ABORTED);
relisten(s);
return;
} }
s->output_data_len -= s->output_data_send_nxt; s->output_data_len -= s->output_data_send_nxt;
s->output_senddata_len = s->output_data_len;
s->output_data_send_nxt = 0; s->output_data_send_nxt = 0;
call_event(s, TCP_SOCKET_DATA_SENT); call_event(s, TCP_SOCKET_DATA_SENT);
@ -134,6 +142,11 @@ appcall(void *state)
{ {
struct tcp_socket *s = state; struct tcp_socket *s = state;
if(s != NULL && s->c != NULL && s->c != uip_conn) {
/* Safe-guard: this should not happen, as the incoming event relates to
* a previous connection */
return;
}
if(uip_connected()) { if(uip_connected()) {
/* Check if this connection originated in a local listen /* Check if this connection originated in a local listen
socket. We do this by checking the state pointer - if NULL, socket. We do this by checking the state pointer - if NULL,
@ -176,8 +189,10 @@ appcall(void *state)
} }
if(uip_aborted()) { if(uip_aborted()) {
tcp_markconn(uip_conn, NULL);
call_event(s, TCP_SOCKET_ABORTED); call_event(s, TCP_SOCKET_ABORTED);
relisten(s); relisten(s);
} }
if(s == NULL) { if(s == NULL) {
@ -203,13 +218,16 @@ appcall(void *state)
if(s->output_data_len == 0 && s->flags & TCP_SOCKET_FLAGS_CLOSING) { if(s->output_data_len == 0 && s->flags & TCP_SOCKET_FLAGS_CLOSING) {
s->flags &= ~TCP_SOCKET_FLAGS_CLOSING; s->flags &= ~TCP_SOCKET_FLAGS_CLOSING;
uip_close(); uip_close();
s->c = NULL;
tcp_markconn(uip_conn, NULL); tcp_markconn(uip_conn, NULL);
call_event(s, TCP_SOCKET_CLOSED); s->c = NULL;
/*call_event(s, TCP_SOCKET_CLOSED);*/
relisten(s); relisten(s);
} }
if(uip_closed()) { if(uip_closed()) {
tcp_markconn(uip_conn, NULL); tcp_markconn(uip_conn, NULL);
s->c = NULL;
call_event(s, TCP_SOCKET_CLOSED); call_event(s, TCP_SOCKET_CLOSED);
relisten(s); relisten(s);
} }
@ -255,6 +273,7 @@ tcp_socket_register(struct tcp_socket *s, void *ptr,
s->ptr = ptr; s->ptr = ptr;
s->input_data_ptr = input_databuf; s->input_data_ptr = input_databuf;
s->input_data_maxlen = input_databuf_len; s->input_data_maxlen = input_databuf_len;
s->output_data_len = 0;
s->output_data_ptr = output_databuf; s->output_data_ptr = output_databuf;
s->output_data_maxlen = output_databuf_len; s->output_data_maxlen = output_databuf_len;
s->input_callback = input_callback; s->input_callback = input_callback;
@ -268,12 +287,15 @@ tcp_socket_register(struct tcp_socket *s, void *ptr,
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int int
tcp_socket_connect(struct tcp_socket *s, tcp_socket_connect(struct tcp_socket *s,
uip_ipaddr_t *ipaddr, const uip_ipaddr_t *ipaddr,
uint16_t port) uint16_t port)
{ {
if(s == NULL) { if(s == NULL) {
return -1; return -1;
} }
if(s->c != NULL) {
tcp_markconn(s->c, NULL);
}
PROCESS_CONTEXT_BEGIN(&tcp_socket_process); PROCESS_CONTEXT_BEGIN(&tcp_socket_process);
s->c = tcp_connect(ipaddr, uip_htons(port), s); s->c = tcp_connect(ipaddr, uip_htons(port), s);
PROCESS_CONTEXT_END(); PROCESS_CONTEXT_END();
@ -329,6 +351,11 @@ tcp_socket_send(struct tcp_socket *s,
memcpy(&s->output_data_ptr[s->output_data_len], data, len); memcpy(&s->output_data_ptr[s->output_data_len], data, len);
s->output_data_len += len; s->output_data_len += len;
if(s->output_senddata_len == 0) {
s->output_senddata_len = s->output_data_len;
}
return len; return len;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
@ -365,3 +392,9 @@ tcp_socket_unregister(struct tcp_socket *s)
return 1; return 1;
} }
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int
tcp_socket_max_sendlen(struct tcp_socket *s)
{
return s->output_data_maxlen - s->output_data_len;
}
/*---------------------------------------------------------------------------*/

View file

@ -32,6 +32,8 @@
#ifndef TCP_SOCKET_H #ifndef TCP_SOCKET_H
#define TCP_SOCKET_H #define TCP_SOCKET_H
#include "uip.h"
struct tcp_socket; struct tcp_socket;
typedef enum { typedef enum {
@ -95,6 +97,7 @@ struct tcp_socket {
uint16_t output_data_maxlen; uint16_t output_data_maxlen;
uint16_t output_data_len; uint16_t output_data_len;
uint16_t output_data_send_nxt; uint16_t output_data_send_nxt;
uint16_t output_senddata_len;
uint16_t output_data_max_seg; uint16_t output_data_max_seg;
uint8_t flags; uint8_t flags;
@ -170,7 +173,7 @@ int tcp_socket_register(struct tcp_socket *s, void *ptr,
* *
*/ */
int tcp_socket_connect(struct tcp_socket *s, int tcp_socket_connect(struct tcp_socket *s,
uip_ipaddr_t *ipaddr, const uip_ipaddr_t *ipaddr,
uint16_t port); uint16_t port);
/** /**
@ -266,4 +269,19 @@ int tcp_socket_close(struct tcp_socket *s);
* *
*/ */
int tcp_socket_unregister(struct tcp_socket *s); int tcp_socket_unregister(struct tcp_socket *s);
/**
* \brief The maximum amount of data that could currently be sent
* \param s A pointer to a TCP socket
* \return The number of bytes available in the output buffer
*
* This function queries the TCP socket and returns the
* number of bytes available in the output buffer. This
* function is used before calling tcp_socket_send() to
* ensure that one application level message can be held
* in the output buffer.
*
*/
int tcp_socket_max_sendlen(struct tcp_socket *s);
#endif /* TCP_SOCKET_H */ #endif /* TCP_SOCKET_H */