//=====================================================================
, ]7 G5 o$ L, U; x//TITLE:
; ? A( \7 v. H2 m+ a9 ^9 i$ l# e2 k y" a// STM32F2xx的tcp_echoserver例程解说
, _8 D& y2 B" d/ a. E$ S, e/ y5 b//AUTHOR:
# h; A1 s$ Q' B5 |5 v// norains
" V. b9 `9 o# _* C7 f9 v1 {& X//DATE:; c9 S7 T8 t1 N* A7 B$ p* G: G
// Monday 04-July-2011+ o T% w" N! t: M* @2 N
//Environment:
/ x! ]( C/ J {// Keil MDK 4.29 |5 G; i: w# ]+ c! u
// STM32F207 核心版" |1 f2 ^! J1 t# u3 S
//===================================================================== 最近拿到STM32F207的核心版,板载有网卡芯片,自然要拿过来捣鼓一番。而对于一个从未接触过网络的菜鸟来说,最好的入门方式就是揣测ST公司的例程,所以今天norains也不例外。那么我们就一起来看看这个官方的例程吧! 7 [$ o) C8 @; ?5 |
ST关于TCP的例程分为client和server,根据字面意思,可以知道tcp_echoserver例程是将STM32F2xx作为server来用。而例程的第一步呢,便是初始化,调用的是tcp_echoserver_init()函数。 在tcp_echoserver_init()函数里,主要做了这么几件事情:" f4 ?4 U$ W9 e
1. 创建一个新的TCP协议控制块' i4 g3 A1 Q$ d5 d6 x
2. 绑定地址和端口号(port). D/ t8 h- q, r3 W/ Z. x
3. 开始监听(listen)
# y# Z: |6 V2 m+ E% Y2 ^ 4. 设置accept的回调函数 其完整代码如下: - void tcp_echoserver_init(void) ; k: z" E2 |. T& N* O
- { + k1 g# \+ b/ k8 c5 a4 \
- //创建一个新的TCP控制块 6 o6 |* q A+ p0 K" D1 u D
- tcp_echoserver_pcb = tcp_new(); ; S9 R* j+ t% C
-
* o( N5 A9 B0 n$ K8 b1 L" f2 Y - if (tcp_echoserver_pcb != NULL)
3 d. l9 K0 h- p* g3 C7 s7 V5 l - { 3 d% T3 Q# `1 A
- err_t err; - q0 e9 ?# n9 [* D" s n. r& u: M
-
T; G1 m# S5 ^5 R! H/ o9 m - //绑定到端口7
# p- N2 Z8 R" \1 E6 \ - err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7); % _7 H6 N/ Q' V7 I( m; P. R: S) D
- : D% E8 ~/ ] A6 x, e
- if (err == ERR_OK) * _4 K% F; h* I! a& x6 a
- {
2 C' T# K3 U' d: I; c" C - //开始监听 " U- |' A+ E. ?1 a: Z
- tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
- I- _0 G6 P; j) }8 v& } - & Y! Q( A7 G4 W* A V% J( |8 d n4 w9 c
- //设置tcp_echoserver_accept为accept的回调函数 8 m8 t" s" Y, v
- tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
; {/ C& i: [8 r& R m2 {+ m - }
: h0 L# }# h' D. b$ D* u6 r# k - else ; b H' E# m) `1 I% Y, A; S9 @
- {
, I W0 `0 o) V* b6 ~" h% r - printf("Can not bind pcb\n"); //norains 2011-7-4 comment " P/ Q3 }& ]3 q5 Z. r
- } ; k. C; j( d4 x' D W
- } 6 O8 H( w/ ?0 \/ O
- else
; h% e6 q9 X* I, E1 f - { `% H) l9 n/ W1 a! z' N V3 T5 P
- printf("Can not create new pcb\n"); //norains 2011-7-4 comment
' {6 u* S/ K3 E" c, u. d4 y - } . w2 x6 Q) m$ n' c
- }
复制代码 当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。
) ]' j/ `7 i$ d+ a, w! G) g7 V
$ e" z& }1 O" h tcp_echoserver_accept()代码如下所示: - static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
: f( z0 s) h i' s9 |( c X - { 6 b! `+ E1 r5 _
- err_t ret_err;
$ [" X$ `/ }# P; F" p h6 S/ k - struct tcp_echoserver_struct *es; / T3 y& P2 H6 O& l
-
) @) F, ^, s% f" P+ J8 O( w - LWIP_UNUSED_ARG(arg); * d) ]8 m" E' S) A
- LWIP_UNUSED_ARG(err);
& c' h9 o& u: G5 Q7 m - 2 B( Z& f* Q5 L9 y
- ///给新的连接设置优先级
5 e& @) a1 |" _ y+ ~1 ] - tcp_setprio(newpcb, TCP_PRIO_MIN); * H8 V; f- _5 ?* n4 o( h
- 4 M7 Z9 y3 P) a0 W% f
- //分配一个结构体空间以保持TCP的连接 \* D0 y1 ^6 f2 b' Z% D- Q
- es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct)); 3 o; C8 c2 ~- B2 c t, J3 k
- if (es != NULL) ( x8 l0 n9 H* p( i
- {
) ~2 y6 F) W1 d' J3 o - es->state = ES_ACCEPTED;
7 m8 ?, F& E6 `' \8 S - es->pcb = newpcb;
5 N! u( q. W- b - es->p = NULL; 0 q2 C/ m6 L* }2 K0 r/ Q
- 3 [1 F2 w$ B8 e9 ?% U+ n( t
- //传递新分配的结构体数据给新的pcb 8 @* W+ T. O2 J. [3 h% J
- tcp_arg(newpcb, es); + E! r; \1 _! Y9 x, O" ` Q" l
-
5 M: [% O- o& S% w7 d - //为新的连接设置receive回调函数 3 [4 w9 {$ J( c8 I1 k
- tcp_recv(newpcb, tcp_echoserver_recv);
: ?& w/ p) Y$ B$ ]+ z -
& ]1 Z/ F; [) C7 n6 ? - //为新的连接设置error回调函数 - i$ o9 A6 `# [# \6 B# S: D
- tcp_err(newpcb, tcp_echoserver_error);
2 @5 J: ?0 C/ j( M- {/ ? - 2 n/ b* W6 J4 J8 X0 }
- //为新的连接设置poll回调函数 1 j/ \5 M$ x) L
- tcp_poll(newpcb, tcp_echoserver_poll, 1);
Z% J. o/ {7 H7 I! B1 ` -
G" J( d3 ^7 s/ T3 o - ret_err = ERR_OK;
( i4 ^4 p: b/ C/ I' y |6 q6 ^4 w - } / A$ N/ n8 R8 Y% g. n: g, @
- else
# t7 ]9 h: Y% Y: J, r - { - c% a" L/ }$ L% s
- /* return memory error */
0 C, z$ t$ e; x7 c - ret_err = ERR_MEM; + G4 C0 W* z# S( r) Y; @5 i
- } ; s' c! @% h' y9 |1 h- [
- return ret_err;
. z& \6 D9 o% a, m5 J) X - }
复制代码 接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:' Q) X6 c7 n; d$ [; A$ _
- static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) . Y& @8 o+ d6 m% Q9 j* d' L
- {
2 O% x; I8 O* {' O - struct tcp_echoserver_struct *es;
( d2 D8 ?2 u1 e - err_t ret_err; " X+ Z1 M! ?: G- t, `
- " G0 @2 o% s5 a d, }- N
- LWIP_ASSERT("arg != NULL",arg != NULL);
: ?: L9 z5 X9 C/ }& q - * B( Z! X# j/ L# M8 v, j
- es = (struct tcp_echoserver_struct *)arg; 3 W+ z1 \0 ~* S4 {
- n$ b* D7 T5 D5 p$ S
- if (p == NULL)
- n4 e/ e, O$ W, s" i% d - { . G! W. T# w# i0 W( O; e; Y6 s
- //如果接收到空的帧,则释放连接
7 A% G8 L d& L6 C- H - ...
3 h. p9 @- @! J8 a - }
3 t3 K2 R4 J- W5 T - else if(err != ERR_OK)
* n/ D' M* N5 m$ i& j - {
/ f1 d9 m' d, B8 r4 F - //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存
7 q t, o5 z& I: H- q9 | - ... ' z4 v0 s+ s( _: ^! }
- }
) q1 p) t# Q5 r+ N% `9 q2 f/ D - else if(es->state == ES_ACCEPTED)
E/ b1 v0 A* N3 x! z9 v5 o - {
+ d9 Q$ C1 y5 P. U: D8 V - //连接成功,在这里需要设置sent回调函数 . \: t {3 `, N$ q7 |6 i
- ... $ A+ ~' u" q' ?
- } 0 _' e2 b1 t, E
- else if (es->state == ES_RECEIVED)
- R, n$ c8 c. g9 |! S( V% C! v - {
^& ~1 K3 h; e4 J4 b9 m+ q5 j - //从客户端收到数据 ! r4 m* @2 M; S8 U' \
- ... ( m. s3 m$ Y, v; f9 w V; c* A
- }
6 @3 G m% i' ?5 E - else 8 s; U3 z, i# f% l
- {
7 A t3 `5 a( l$ S% ?' a, y- O - //当连接关闭时,还收到了数据
1 _3 X; |$ A: n5 O - ...
& R2 s# F9 S& q/ @" ] -
7 l( \7 g! z$ N - }
$ V# C& [; p9 X) e* M) B+ _9 o -
8 Q3 |* |2 U; J1 l0 G - return ret_err; 6 Z/ y: }9 V% b7 v$ h3 O
- }
复制代码 STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:0 U" Y9 F; i! S- \. T8 a3 x# e V
5 ]/ |" q6 L" }* C$ ], Y3 u- |, K2 H: M& y1 X/ U0 A
4 ~' z* e2 a/ ]
如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:4 t/ Z# X, a+ A |' ^1 P6 o3 U
echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
4 o: o& u7 \& D# f1 t# T+ c* q 6 T$ M$ E) S" L$ i. D
如果网络联通的话,测试成功将如下如下的画面,如图:7 b3 b$ |$ F! u7 u' ~( h9 P) O
. a9 B. h& r' F- E# Y: t) R' ], i2 X, S$ U/ @( w
( T+ I8 I. z: [4 h4 a
7 o# J# M2 `+ b4 k1 ]6 ^
1 f1 g6 t& Q0 R1 b/ Z8 n/ D" Y' b- r6 J1 K9 A5 {: h
|