|
//=====================================================================) Y& x% X+ j: K: n6 j( P
//TITLE: M! d" M0 A9 W. c; X7 L
// STM32F2xx的tcp_echoserver例程解说
+ s! T% Z8 S% ^' N( \' \3 V//AUTHOR:8 w, C% o3 [1 `3 ?/ d0 ?
// norains; U% X& U# H1 N5 i/ M
//DATE:
# T% T+ X1 Y4 ~/ q1 w2 d% \// Monday 04-July-2011! k! u4 F8 {6 P }& f R7 m
//Environment:
. b* Q: P( e) G// Keil MDK 4.2
4 E, L, _ A9 C+ N* I, x// STM32F207 核心版 E2 L _( }' F4 @% N; U$ H8 p
//===================================================================== 最近拿到STM32F207的核心版,板载有网卡芯片,自然要拿过来捣鼓一番。而对于一个从未接触过网络的菜鸟来说,最好的入门方式就是揣测ST公司的例程,所以今天norains也不例外。那么我们就一起来看看这个官方的例程吧! $ j& g0 j1 K v% x; _( f1 w! y
ST关于TCP的例程分为client和server,根据字面意思,可以知道tcp_echoserver例程是将STM32F2xx作为server来用。而例程的第一步呢,便是初始化,调用的是tcp_echoserver_init()函数。 在tcp_echoserver_init()函数里,主要做了这么几件事情:
4 A( Q8 K" X2 E( E& O 1. 创建一个新的TCP协议控制块
8 T4 v5 v7 F; }9 } h 2. 绑定地址和端口号(port)2 R8 @6 s2 w1 v5 E# h
3. 开始监听(listen)2 |$ O2 j1 ^0 }% G" k
4. 设置accept的回调函数 其完整代码如下: - void tcp_echoserver_init(void)
/ F' T$ S$ t! _/ E7 Z - { & z: G6 X. v: h* H' m& m
- //创建一个新的TCP控制块 + y1 ]$ ^* C3 T3 |
- tcp_echoserver_pcb = tcp_new();
1 H& Z0 u9 i4 k - 0 k- F/ Q0 G( t
- if (tcp_echoserver_pcb != NULL)
2 u& f" Y% B- u% A4 e; g4 Y - { 6 f/ {5 F/ c/ O) K* b2 S
- err_t err; ) p) h$ A4 M" j4 c
-
7 a& T( y0 m/ F6 a - //绑定到端口7 . v" U2 ` G1 J. w
- err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);
3 W7 _8 i+ E! O x: Y; C - 9 ?( U2 e: o: G3 O
- if (err == ERR_OK)
8 d, S9 p$ i0 j# u, J3 F7 E. ] - {
; a- p+ x1 t/ H; j Y% N v. V1 ` - //开始监听
& s5 `+ a+ h L - tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
# P- Y$ t3 P4 } a8 x4 a8 E6 o - ( l- v7 w# V9 M- X8 _5 v, O, @
- //设置tcp_echoserver_accept为accept的回调函数 $ C/ t" a: }. b3 |6 U- e, Q
- tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept); 6 c E3 }' k$ o
- } # J; I) I' x, x9 w" V
- else + P! q* d% K8 V2 r- f
- { 1 ?9 Z0 m. L7 w' I) `
- printf("Can not bind pcb\n"); //norains 2011-7-4 comment 9 }, W$ L" a$ Q) s% s: l
- }
8 K$ f1 j/ \& A8 ^( h - }
' T5 H6 e# m) V: o# h6 @3 k - else
6 H( K& K: ~$ C4 H( W' y: s6 o# F, V - { 2 [' @' i! f- w4 j+ M/ p5 F
- printf("Can not create new pcb\n"); //norains 2011-7-4 comment / P# y F2 r, @
- } ( o% a8 P6 |" \4 l: T* N4 G
- }
复制代码 当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。: ~8 @) R+ ]9 v @9 D T! j7 d
1 C( D9 X i5 o7 b; b3 w+ G
tcp_echoserver_accept()代码如下所示: - static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
* N) m5 g8 ~: h3 J - {
9 r- H! p' I: X) |# c - err_t ret_err; 4 ^$ R* j- K- i3 D# e0 e
- struct tcp_echoserver_struct *es;
7 {4 p. b2 D" t y9 S/ v2 a. {2 I - + X# E2 u! H% w! n- `
- LWIP_UNUSED_ARG(arg);
6 c y2 O8 r* T - LWIP_UNUSED_ARG(err);
8 N) [# E Y/ }7 [( U; M+ H' s3 S3 | - @ r3 `" `/ n; s/ L+ K9 D5 P
- ///给新的连接设置优先级 q+ V Z- s: i) K
- tcp_setprio(newpcb, TCP_PRIO_MIN); % q6 H1 Z* e. ?: S6 m- u
-
$ p1 G/ L N1 M6 @5 D1 E - //分配一个结构体空间以保持TCP的连接 + _; y+ V9 o* T# p
- es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));
: m* s' C) t9 O% g - if (es != NULL) , D1 X+ E! O- A" d
- { 7 W- w1 D: u/ c# r Q5 h/ C* j2 y
- es->state = ES_ACCEPTED;
/ S8 r( w c' l+ e8 v - es->pcb = newpcb;
1 f/ N, f! M4 V- Q; M9 l - es->p = NULL; / V' g: C) U* b4 {$ z; h
- ; y( j1 O# t5 w/ r: s
- //传递新分配的结构体数据给新的pcb
* g9 q( x0 Y+ _/ `+ ] p! t0 c) V v5 A - tcp_arg(newpcb, es); ' C# G# i9 o" d( Y
- ' J- I2 a# R3 g$ h: j
- //为新的连接设置receive回调函数 ; r, N1 | a8 b6 k6 l) N: T
- tcp_recv(newpcb, tcp_echoserver_recv); . m1 R% E6 f3 O+ Y3 ]: U
-
3 V7 j1 \6 A! {2 f+ O6 j7 o - //为新的连接设置error回调函数 $ R/ R! {3 e: d* \) b( @4 y- Y
- tcp_err(newpcb, tcp_echoserver_error);
9 k6 J+ Q9 B* G/ ~ - 0 s2 j! S2 C. G/ ^6 n
- //为新的连接设置poll回调函数 : D- ]3 o: U' x% p5 t
- tcp_poll(newpcb, tcp_echoserver_poll, 1); & b) ^3 e! K2 @
- ( e- |4 M8 f7 b/ u3 |( d/ c
- ret_err = ERR_OK; $ \5 q4 N( c& g. ]
- }
# H1 J5 b0 M! B# X - else
2 B, s7 V2 T8 A( Z* @( h6 q - {
# w( K( r; A- m* K( T - /* return memory error */ 4 n9 _ Z3 h/ j1 P2 Y% V2 O7 M
- ret_err = ERR_MEM;
, f, @5 W) W+ x8 u - }
, _$ [4 N( `2 W$ ^: U7 y - return ret_err;
; B$ S5 V. c. q - }
复制代码 接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:* U5 U; V' }! x/ l- a) M
- static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) # `, ?& q( y# N+ Z
- {
" t4 k& |. M' L2 B2 S - struct tcp_echoserver_struct *es;
) m! o& d4 ~2 G& f. w - err_t ret_err;
* U! c2 G! H @! b; L0 { - ; a" Y1 L. Z8 w) w: N1 W. K1 l
- LWIP_ASSERT("arg != NULL",arg != NULL);
) A. K' [$ [/ C% I1 O' Z' r - $ O8 ~$ T+ Z. r# g% ^! o4 T* E
- es = (struct tcp_echoserver_struct *)arg; ' c3 L5 ]1 w/ Y2 _! N
-
% W+ }, B- O9 J% M' b) w - if (p == NULL)
9 D1 t6 ]8 d' n' i7 N) o - { # a* K4 M, [2 o6 w
- //如果接收到空的帧,则释放连接 8 d) m& W) h8 [/ v& v- ^
- ... # ]; d- o5 D3 a- J; `" e/ y0 i/ S
- } , n& j% R1 c9 K# l6 ]; }
- else if(err != ERR_OK)
3 q, D; ~3 P& Y# a7 x - {
. i) f K5 P5 c' d - //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存
0 Y6 [$ y) p5 r% G - ... * I; q1 ~) ?: r. \/ j# S" j) U1 p
- }
2 G- a- U$ e" C% @ - else if(es->state == ES_ACCEPTED)
( x0 D! w+ M; g! m i8 @ - { 7 G# ^5 i8 Q5 R( W; y
- //连接成功,在这里需要设置sent回调函数
0 F* R9 r2 C3 a; M- n& H# ? - ...
& O6 W, y# h+ F. ~" V- i3 H - }
g& E* x5 Q4 T: R) U - else if (es->state == ES_RECEIVED)
/ ?9 A3 _: P$ d% s - {
: a! B+ y/ [( V - //从客户端收到数据
, y) L" z; k3 s# H1 @ - ...
4 l1 S! I& X2 [4 M - }
" ]6 M: y: @" s: q - else 2 C7 p, L2 c" k" m, M V
- { ; W( r* F8 S5 c, Y2 A9 v% c- p
- //当连接关闭时,还收到了数据 c6 F2 V+ P6 F7 L- y
- ... ; Y% O" q8 L4 ]4 T
- , s$ C R; g) j4 e4 z& f1 J
- } 8 I0 E' k8 g3 ?; n# X
- # ]1 y( p/ X5 C7 R1 t0 D" M
- return ret_err; ( W8 V/ l6 h; o2 X* A
- }
复制代码 STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:/ t6 S% j0 K; j' X; {# S
2 h% w+ [' Q+ Q8 E. p- O0 ?% }7 V0 U# z9 `& }9 |
; o/ z$ k! s5 o6 `$ @# w: @8 e 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:
7 n- `; N% `' }1 B. M8 S4 H5 U4 L a6 w echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
% j# f! d( H/ k- X+ u
& G6 w3 r9 y. }- v( Y; B! T# z2 B 如果网络联通的话,测试成功将如下如下的画面,如图:# b2 b# J# R2 Y2 n$ b0 ~
) k& M! g( k# J# p0 `- w
% V+ \* G: R$ B+ d. d. q
& |3 |* m: q3 T* Q, e; ~
$ S; R& N6 W2 k' T- }$ G
3 q0 n) a5 m% {6 U5 E4 ^. t+ ? g2 Y2 w: s6 Z& L- B6 A+ C
|