1 |
/* |
/** |
2 |
* Copyright (c) 2001-2003 Swedish Institute of Computer Science. |
* @file |
3 |
* All rights reserved. |
* TCP echo server example using raw API. |
|
* |
|
|
* Redistribution and use in source and binary forms, with or without modification, |
|
|
* are permitted provided that the following conditions are met: |
|
|
* |
|
|
* 1. Redistributions of source code must retain the above copyright notice, |
|
|
* this list of conditions and the following disclaimer. |
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, |
|
|
* this list of conditions and the following disclaimer in the documentation |
|
|
* and/or other materials provided with the distribution. |
|
|
* 3. The name of the author may not be used to endorse or promote products |
|
|
* derived from this software without specific prior written permission. |
|
|
* |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
|
|
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
|
|
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
|
|
* OF SUCH DAMAGE. |
|
|
* |
|
|
* This file is part of the lwIP TCP/IP stack. |
|
|
* |
|
|
* Author: Adam Dunkels <adam@sics.se> |
|
4 |
* |
* |
5 |
|
* Echos all bytes sent by connecting client, |
6 |
|
* and passively closes when client is done. |
7 |
*/ |
*/ |
8 |
|
|
9 |
#include "lwip/debug.h" |
#include "lwip/debug.h" |
10 |
#include "lwip/stats.h" |
#include "lwip/stats.h" |
11 |
#include "lwip/tcp.h" |
#include "lwip/tcp.h" |
12 |
|
|
13 |
struct echo_state { |
static struct tcp_pcb *echo_pcb; |
14 |
|
|
15 |
|
enum echo_states |
16 |
|
{ |
17 |
|
ES_NONE = 0, |
18 |
|
ES_ACCEPTED, |
19 |
|
ES_RECEIVED, |
20 |
|
ES_CLOSING, |
21 |
|
}; |
22 |
|
|
23 |
|
struct echo_state |
24 |
|
{ |
25 |
|
u8_t state; |
26 |
|
u8_t retries; |
27 |
|
struct tcp_pcb *pcb; |
28 |
|
/* pbuf (chain) to recycle */ |
29 |
struct pbuf *p; |
struct pbuf *p; |
|
u8_t failed; |
|
|
#define FAILED_MAX 8 |
|
30 |
}; |
}; |
31 |
/*-----------------------------------------------------------------------------------*/ |
|
32 |
static void |
err_t echo_accept(void *arg, struct tcp_pcb *newpcb, err_t err); |
33 |
echo_err(void *arg, err_t err) |
err_t echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); |
34 |
{ |
void echo_error(void *arg, err_t err); |
35 |
struct echo_state *es = arg; |
err_t echo_poll(void *arg, struct tcp_pcb *tpcb); |
36 |
|
err_t echo_sent(void *arg, struct tcp_pcb *tpcb, u16_t len); |
37 |
if (arg != NULL) { |
void echo_send(struct tcp_pcb *tpcb, struct echo_state *es); |
38 |
LWIP_ASSERT("es->p != NULL", es->p != NULL); |
void echo_close(struct tcp_pcb *tpcb, struct echo_state *es); |
39 |
pbuf_free(es->p); |
|
40 |
mem_free(arg); |
void |
41 |
} |
echo_init(void) |
42 |
} |
{ |
43 |
/*-----------------------------------------------------------------------------------*/ |
echo_pcb = tcp_new(); |
44 |
static void |
if (echo_pcb != NULL) |
45 |
close_conn(struct tcp_pcb *pcb, struct echo_state *es) |
{ |
46 |
{ |
err_t err; |
47 |
tcp_arg(pcb, NULL); |
|
48 |
#if 0 |
err = tcp_bind(echo_pcb, IP_ADDR_ANY, 7); |
49 |
tcp_sent(pcb, NULL); |
if (err == ERR_OK) |
50 |
tcp_recv(pcb, NULL); |
{ |
51 |
#endif /* 0 */ |
echo_pcb = tcp_listen(echo_pcb); |
52 |
if (es != NULL) { |
tcp_accept(echo_pcb, echo_accept); |
|
if (es->p != NULL) { |
|
|
pbuf_free(es->p); |
|
53 |
} |
} |
54 |
mem_free(es); |
else |
55 |
|
{ |
56 |
|
/* abort? output diagnostic? */ |
57 |
|
} |
58 |
|
} |
59 |
|
else |
60 |
|
{ |
61 |
|
/* abort? output diagnostic? */ |
62 |
} |
} |
|
tcp_close(pcb); |
|
63 |
} |
} |
64 |
/*-----------------------------------------------------------------------------------*/ |
|
65 |
static void |
|
66 |
send_buf(struct tcp_pcb *pcb, struct echo_state *es) |
err_t |
67 |
{ |
echo_accept(void *arg, struct tcp_pcb *newpcb, err_t err) |
|
struct pbuf *q; |
|
|
|
|
|
do { |
|
|
q = es->p; |
|
|
es->p = pbuf_dechain(q); |
|
|
if (tcp_write(pcb, q->payload, q->len, 1) == ERR_MEM) { |
|
|
pbuf_chain(q, es->p); |
|
|
es->p = q; |
|
|
return; |
|
|
} |
|
|
tcp_recved(pcb, q->len); |
|
|
LWIP_ASSERT("q != NULL", q != NULL); |
|
|
pbuf_free(q); |
|
|
} while (es->p != NULL); |
|
|
} |
|
|
/*-----------------------------------------------------------------------------------*/ |
|
|
static err_t |
|
|
echo_poll(void *arg, struct tcp_pcb *pcb) |
|
68 |
{ |
{ |
69 |
|
err_t ret_err; |
70 |
struct echo_state *es; |
struct echo_state *es; |
71 |
|
|
72 |
if (arg == NULL) { |
/* commonly observed practive to call tcp_setprio(), why? */ |
73 |
return tcp_close(pcb); |
tcp_setprio(newpcb, TCP_PRIO_MIN); |
|
} |
|
|
|
|
|
es = arg; |
|
74 |
|
|
75 |
if (es->failed >= FAILED_MAX) { |
es = mem_malloc(sizeof(struct echo_state)); |
76 |
close_conn(pcb, es); |
if (es != NULL) |
77 |
tcp_abort(pcb); |
{ |
78 |
return ERR_ABRT; |
es->state = ES_ACCEPTED; |
79 |
|
es->pcb = newpcb; |
80 |
|
es->retries = 0; |
81 |
|
es->p = NULL; |
82 |
|
/* pass newly allocated es to our callbacks */ |
83 |
|
tcp_arg(newpcb, es); |
84 |
|
tcp_recv(newpcb, echo_recv); |
85 |
|
tcp_err(newpcb, echo_error); |
86 |
|
tcp_poll(newpcb, echo_poll, 1); |
87 |
|
ret_err = ERR_OK; |
88 |
} |
} |
89 |
|
else |
90 |
if (es->p != NULL) { |
{ |
91 |
++es->failed; |
ret_err = ERR_MEM; |
|
send_buf(pcb, es); |
|
92 |
} |
} |
93 |
return ERR_OK; |
return ret_err; |
94 |
} |
} |
95 |
/*-----------------------------------------------------------------------------------*/ |
|
96 |
static err_t |
err_t |
97 |
echo_sent(void *arg, struct tcp_pcb *pcb, u16_t len) |
echo_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) |
98 |
{ |
{ |
99 |
struct echo_state *es; |
struct echo_state *es; |
100 |
|
err_t ret_err; |
|
es = arg; |
|
101 |
|
|
102 |
if (es != NULL && es->p != NULL) { |
LWIP_ASSERT("arg != NULL",arg != NULL); |
103 |
send_buf(pcb, es); |
es = arg; |
104 |
|
if (p == NULL) |
105 |
|
{ |
106 |
|
/* remote host closed connection */ |
107 |
|
es->state = ES_CLOSING; |
108 |
|
if(es->p == NULL) |
109 |
|
{ |
110 |
|
/* we're done sending, close it */ |
111 |
|
echo_close(tpcb, es); |
112 |
|
} |
113 |
|
else |
114 |
|
{ |
115 |
|
/* we're not done yet */ |
116 |
|
tcp_sent(tpcb, echo_sent); |
117 |
|
echo_send(tpcb, es); |
118 |
|
} |
119 |
|
ret_err = ERR_OK; |
120 |
} |
} |
121 |
return ERR_OK; |
else if(err != ERR_OK) |
122 |
|
{ |
123 |
|
/* cleanup, for unkown reason */ |
124 |
|
if (p != NULL) |
125 |
|
{ |
126 |
|
es->p = NULL; |
127 |
|
pbuf_free(p); |
128 |
|
} |
129 |
|
ret_err = err; |
130 |
|
} |
131 |
|
else if(es->state == ES_ACCEPTED) |
132 |
|
{ |
133 |
|
/* first data chunk in p->payload */ |
134 |
|
es->state = ES_RECEIVED; |
135 |
|
/* store reference to incoming pbuf (chain) */ |
136 |
|
es->p = p; |
137 |
|
/* install send completion notifier */ |
138 |
|
tcp_sent(tpcb, echo_sent); |
139 |
|
echo_send(tpcb, es); |
140 |
|
ret_err = ERR_OK; |
141 |
|
} |
142 |
|
else if (es->state == ES_RECEIVED) |
143 |
|
{ |
144 |
|
/* read some more data */ |
145 |
|
if(es->p == NULL) |
146 |
|
{ |
147 |
|
es->p = p; |
148 |
|
tcp_sent(tpcb, echo_sent); |
149 |
|
echo_send(tpcb, es); |
150 |
|
} |
151 |
|
else |
152 |
|
{ |
153 |
|
struct pbuf *ptr; |
154 |
|
|
155 |
|
/* chain pbufs to the end of what we recv'ed previously */ |
156 |
|
ptr = es->p; |
157 |
|
pbuf_chain(ptr,p); |
158 |
|
} |
159 |
|
ret_err = ERR_OK; |
160 |
|
} |
161 |
|
else if(es->state == ES_CLOSING) |
162 |
|
{ |
163 |
|
/* odd case, remote side closing twice, trash data */ |
164 |
|
tcp_recved(tpcb, p->tot_len); |
165 |
|
es->p = NULL; |
166 |
|
pbuf_free(p); |
167 |
|
ret_err = ERR_OK; |
168 |
|
} |
169 |
|
else |
170 |
|
{ |
171 |
|
/* unkown es->state, trash data */ |
172 |
|
tcp_recved(tpcb, p->tot_len); |
173 |
|
es->p = NULL; |
174 |
|
pbuf_free(p); |
175 |
|
ret_err = ERR_OK; |
176 |
|
} |
177 |
|
return ret_err; |
178 |
} |
} |
179 |
/*-----------------------------------------------------------------------------------*/ |
|
180 |
static err_t |
void |
181 |
echo_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) |
echo_error(void *arg, err_t err) |
182 |
{ |
{ |
183 |
struct echo_state *es; |
struct echo_state *es; |
184 |
|
|
185 |
es = arg; |
es = arg; |
186 |
|
if (es != NULL) |
187 |
|
{ |
188 |
|
mem_free(es); |
189 |
|
} |
190 |
|
} |
191 |
|
|
192 |
|
err_t |
193 |
|
echo_poll(void *arg, struct tcp_pcb *tpcb) |
194 |
|
{ |
195 |
|
err_t ret_err; |
196 |
|
struct echo_state *es; |
197 |
|
|
198 |
if ((es == NULL) || (p == NULL)) { |
es = arg; |
199 |
close_conn(pcb, es); |
if (es != NULL) |
200 |
return ERR_OK; |
{ |
201 |
|
if (es->p != NULL) |
202 |
|
{ |
203 |
|
/* there is a remaining pbuf (chain) */ |
204 |
|
tcp_sent(tpcb, echo_sent); |
205 |
|
echo_send(tpcb, es); |
206 |
|
} |
207 |
|
else |
208 |
|
{ |
209 |
|
/* no remaining pbuf (chain) */ |
210 |
|
if(es->state == ES_CLOSING) |
211 |
|
{ |
212 |
|
echo_close(tpcb, es); |
213 |
|
} |
214 |
|
} |
215 |
|
ret_err = ERR_OK; |
216 |
} |
} |
217 |
|
else |
218 |
if (es->p != NULL) { |
{ |
219 |
pbuf_chain(es->p, p); |
/* nothing to be done */ |
220 |
} else { |
tcp_abort(tpcb); |
221 |
es->p = p; |
ret_err = ERR_ABRT; |
222 |
} |
} |
223 |
|
return ret_err; |
|
send_buf(pcb, es); |
|
|
|
|
|
return ERR_OK; |
|
224 |
} |
} |
225 |
/*-----------------------------------------------------------------------------------*/ |
|
226 |
static err_t |
err_t |
227 |
echo_accept(void *arg, struct tcp_pcb *pcb, err_t err) |
echo_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) |
228 |
{ |
{ |
229 |
struct echo_state *es; |
struct echo_state *es; |
230 |
|
|
231 |
tcp_setprio(pcb, TCP_PRIO_MIN); |
es = arg; |
232 |
|
es->retries = 0; |
233 |
|
|
234 |
/* Allocate memory for the structure that holds the state of the |
if(es->p != NULL) |
235 |
connection. */ |
{ |
236 |
es = mem_malloc(sizeof(struct echo_state)); |
/* still got pbufs to send */ |
237 |
|
tcp_sent(tpcb, echo_sent); |
238 |
if (es == NULL) { |
echo_send(tpcb, es); |
239 |
return ERR_MEM; |
} |
240 |
|
else |
241 |
|
{ |
242 |
|
/* no more pbufs to send */ |
243 |
|
if(es->state == ES_CLOSING) |
244 |
|
{ |
245 |
|
echo_close(tpcb, es); |
246 |
|
} |
247 |
} |
} |
|
|
|
|
/* Initialize the structure. */ |
|
|
es->p = NULL; |
|
|
es->failed = 0; |
|
|
|
|
|
/* Tell TCP that this is the structure we wish to be passed for our |
|
|
callbacks. */ |
|
|
tcp_arg(pcb, es); |
|
|
|
|
|
/* Tell TCP that we wish to be informed of incoming data by a call |
|
|
to the http_recv() function. */ |
|
|
#if 0 |
|
|
tcp_recv(pcb, echo_recv); |
|
|
|
|
|
tcp_err(pcb, echo_err); |
|
|
#endif /* 0 */ |
|
|
|
|
|
tcp_poll(pcb, echo_poll, 2); |
|
|
|
|
248 |
return ERR_OK; |
return ERR_OK; |
249 |
} |
} |
250 |
/*-----------------------------------------------------------------------------------*/ |
|
251 |
void |
void |
252 |
echo_init(void) |
echo_send(struct tcp_pcb *tpcb, struct echo_state *es) |
253 |
{ |
{ |
254 |
struct tcp_pcb *pcb; |
struct pbuf *ptr; |
255 |
|
err_t wr_err = ERR_OK; |
256 |
pcb = tcp_new(); |
|
257 |
tcp_bind(pcb, IP_ADDR_ANY, 7); |
ptr = es->p; |
258 |
pcb = tcp_listen(pcb); |
|
259 |
#if 0 |
/* enqueue data for transmission */ |
260 |
tcp_accept(pcb, echo_accept); |
wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1); |
261 |
#endif /* 0 */ |
if (wr_err == ERR_OK) |
262 |
|
{ |
263 |
|
u16_t plen; |
264 |
|
|
265 |
|
plen = ptr->len; |
266 |
|
/* continue with next pbuf in chain (if any) */ |
267 |
|
es->p = ptr->next; |
268 |
|
if(es->p != NULL) |
269 |
|
{ |
270 |
|
/* new reference! */ |
271 |
|
pbuf_ref(es->p); |
272 |
|
} |
273 |
|
/* chop first pbuf from chain */ |
274 |
|
pbuf_free(ptr); |
275 |
|
|
276 |
|
/* we can read more data now */ |
277 |
|
tcp_recved(tpcb, plen); |
278 |
|
} |
279 |
|
else if(wr_err == ERR_MEM) |
280 |
|
{ |
281 |
|
/* we are low on memory, try later / harder ? */ |
282 |
|
es->p = ptr; |
283 |
|
} |
284 |
|
else |
285 |
|
{ |
286 |
|
/* other problem ?? */ |
287 |
|
} |
288 |
} |
} |
289 |
/*-----------------------------------------------------------------------------------*/ |
|
290 |
err_t |
void |
291 |
lwip_tcp_event(void *arg, struct tcp_pcb *pcb, |
echo_close(struct tcp_pcb *tpcb, struct echo_state *es) |
292 |
enum lwip_event ev, struct pbuf *p, |
{ |
293 |
u16_t size, err_t err) |
tcp_arg(tpcb, NULL); |
294 |
{ |
tcp_sent(tpcb, NULL); |
295 |
switch (ev) { |
tcp_recv(tpcb, NULL); |
296 |
case LWIP_EVENT_ACCEPT: |
tcp_err(tpcb, NULL); |
297 |
return echo_accept(arg, pcb, err); |
tcp_poll(tpcb, NULL, 0); |
|
case LWIP_EVENT_SENT: |
|
|
return echo_sent(arg, pcb, size); |
|
|
case LWIP_EVENT_RECV: |
|
|
return echo_recv(arg, pcb, p, err); |
|
|
case LWIP_EVENT_ERR: |
|
|
echo_err(arg, err); |
|
|
break; |
|
|
case LWIP_EVENT_POLL: |
|
|
return echo_poll(arg, pcb); |
|
|
default: |
|
|
break; |
|
|
} |
|
298 |
|
|
299 |
return ERR_OK; |
if (es != NULL) |
300 |
|
{ |
301 |
|
mem_free(es); |
302 |
|
} |
303 |
|
tcp_close(tpcb); |
304 |
} |
} |
|
/*-----------------------------------------------------------------------------------*/ |
|
|
|
|