//=====================================================================2 f. D7 E( l7 l, x
//TITLE:: j: E2 o# Q7 B/ c
// STM32F2xx的tcp_echoserver例程解说
7 p% i; L) X3 |) w) s- L; g2 c5 @ z4 i//AUTHOR:, M$ e' Y% m1 k+ @ R: K- P5 B1 e
// norains6 W) i* M- v! l" a" M3 J4 @3 p
//DATE:
0 i! M6 C8 y0 \// Monday 04-July-20117 m$ c% c& D- i, h' ^# o/ L/ o% X% ?
//Environment:
2 S0 e+ {& c" F& o! `// Keil MDK 4.2
2 ^, h0 S Z& z: L// STM32F207 核心版
( I: V. |' {+ v$ J3 \2 Z7 r//===================================================================== 最近拿到STM32F207的核心版,板载有网卡芯片,自然要拿过来捣鼓一番。而对于一个从未接触过网络的菜鸟来说,最好的入门方式就是揣测ST公司的例程,所以今天norains也不例外。那么我们就一起来看看这个官方的例程吧!
( e6 u1 J: A) v$ x6 q0 @0 o, ~ ST关于TCP的例程分为client和server,根据字面意思,可以知道tcp_echoserver例程是将STM32F2xx作为server来用。而例程的第一步呢,便是初始化,调用的是tcp_echoserver_init()函数。 在tcp_echoserver_init()函数里,主要做了这么几件事情:8 m; J: A4 |: S+ O7 y d3 h
1. 创建一个新的TCP协议控制块8 o% e8 M" l% F- p9 }2 ^. n9 Z
2. 绑定地址和端口号(port)) I: V: D4 }/ O8 j/ W8 O
3. 开始监听(listen)
( W2 ~' l" I0 r. t! z 4. 设置accept的回调函数 其完整代码如下: - void tcp_echoserver_init(void)
! o7 _) L* Q3 q+ d( E! ^8 y - {
5 L$ J A8 \8 _5 ^$ o4 ^ - //创建一个新的TCP控制块 : w& M u; ~' L* C: a4 `9 A/ v
- tcp_echoserver_pcb = tcp_new();
9 j; F0 h- z r3 t( I9 ^, @ -
" H- } o, F( w - if (tcp_echoserver_pcb != NULL) ; G5 L3 Q1 u. ?( _& [
- { " |) S! ?4 X+ z8 ~
- err_t err; 6 V: f: V" z9 b2 F2 }6 j! C
- 3 q& u) U) d, T, V: c
- //绑定到端口7
! I3 Z- L: Z& t* ~2 @# v - err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);
% w$ X/ s4 `1 z8 t -
5 q- P) d; T8 I, r# L0 n- s" \ - if (err == ERR_OK) & Q7 P6 p/ f$ _8 Z) L
- {
+ q$ ^' ]# H1 j& T* o+ g9 C - //开始监听 . n, b1 C4 p4 `) a
- tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb); 1 J. A1 W1 T( _' s. Z5 m+ c$ O6 r
-
- S% k+ ~$ `. u% o+ y/ V& a/ b7 R3 M - //设置tcp_echoserver_accept为accept的回调函数
2 _, I$ o- G$ z0 [ - tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
2 R Q, X& C' |- F3 I* l, l" z9 [8 w - } # ^- {9 ^! s4 Y
- else
! ?) F% C& v% q! Y* f! l - { & h T, I, E* N g
- printf("Can not bind pcb\n"); //norains 2011-7-4 comment & w1 r; k- u0 }! ^$ W" J4 G
- } $ j8 ~$ J" d" [% t0 p
- } 0 T" I+ H" m; H% O9 n
- else 0 ?) s" o+ I: x) M0 i
- {
4 _- t; \1 W8 ^' u/ P. m - printf("Can not create new pcb\n"); //norains 2011-7-4 comment / T& T% q6 b" @3 |, d
- }
4 U- l$ G+ F+ L5 h' ^ - }
复制代码 当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。( ` R- ]% h, ]: }, n5 W( A1 p
: f6 x4 ^- c! D3 s
tcp_echoserver_accept()代码如下所示: - static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err) 1 g7 \! S7 K, [7 V! _' j
- {
( k( ]1 V8 F8 D- A1 c9 U! l - err_t ret_err; & R6 |# z0 [+ e4 z
- struct tcp_echoserver_struct *es; " o, t0 A; ^; q+ O. A$ `
- ' }4 B/ t: S" V }6 k6 @7 m
- LWIP_UNUSED_ARG(arg); + A% J8 n) Q R4 G8 k4 P; Y
- LWIP_UNUSED_ARG(err);
# c- A8 j. F0 {. ?. u -
: ?; Y5 C5 b& K6 _& ] - ///给新的连接设置优先级 4 r- c: v) _) H, w7 R
- tcp_setprio(newpcb, TCP_PRIO_MIN);
3 Q+ j+ o0 f! ~) F' s - 3 \0 c' L+ Q7 N8 k
- //分配一个结构体空间以保持TCP的连接
7 ]5 o, r, U! q. m( T. @ - es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct)); 2 i/ R; C8 j9 l, H
- if (es != NULL)
2 C9 Y" k2 y% T( C5 J" i" ~ - {
6 @/ Y3 x- i u3 \ - es->state = ES_ACCEPTED;
, L4 }, _/ c1 |$ h - es->pcb = newpcb;
f3 r9 `, ~' v' B$ y - es->p = NULL; ' V9 l U' a' Y- [, g8 Y; p
- 3 Y# N( {! ~: u& O4 O0 z
- //传递新分配的结构体数据给新的pcb
( V9 c4 d6 f# \+ S - tcp_arg(newpcb, es); % C& i$ Z8 P1 @1 ~& A2 D. g
- + r) j" p- j: o% [
- //为新的连接设置receive回调函数
6 U$ W' Z U# W+ z" c# N8 s - tcp_recv(newpcb, tcp_echoserver_recv);
# k% G4 A: l# s+ M8 R. n# ^3 q -
, k/ ?* r5 A) o I$ S - //为新的连接设置error回调函数 5 w7 `$ N1 \, P( g2 i4 p
- tcp_err(newpcb, tcp_echoserver_error);
; y" q4 ~% S! ~, d6 s Z - 7 [3 T( h3 B, c( H$ P$ Z1 |0 B: N4 i
- //为新的连接设置poll回调函数
6 [& t# X4 k# E - tcp_poll(newpcb, tcp_echoserver_poll, 1); - s$ v/ {; P: f/ M( p+ K4 z, B4 @
-
( D4 k) | L0 _* ?& e; O6 e0 a" v - ret_err = ERR_OK;
- v* F* }' p* _6 k5 k - } : B; M8 m5 Q0 s/ ?
- else e) B/ O6 w( f6 A
- { ) ?" u; Z; H# S4 p+ R, N
- /* return memory error */
) o9 ? D$ N! O, x. a; ~ - ret_err = ERR_MEM; ; _$ j! t3 I9 t+ H
- }
% A5 P+ y! t1 u' j/ `5 O( ?, f - return ret_err;
' Q0 p/ {1 T2 b9 f - }
复制代码 接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:8 f) V. n4 Q, }$ o, ~( R1 g
- static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
" k, V% H) D5 d" U! c8 N - { , ~0 K- R C( F M& x" P
- struct tcp_echoserver_struct *es;
8 N7 |3 O- x* s2 R# K - err_t ret_err;
3 {$ J g* Z4 w - + ]* \8 ]: V' Q' U( @
- LWIP_ASSERT("arg != NULL",arg != NULL); 5 F2 _0 O" ~% P( O, e# C5 A
-
8 i/ g4 N) m, Q$ m* I1 q; q - es = (struct tcp_echoserver_struct *)arg; / k( \ a* P* o, y- G) d" K
- : q8 v& [9 p( }' v6 o
- if (p == NULL)
4 K$ s# K! V1 u+ \; l9 A - { , L" l- w# u0 P: z
- //如果接收到空的帧,则释放连接
) n9 X' `' y. g$ t - ... 5 K$ U3 w6 z9 l; ^1 W P8 ] Z& ]1 I
- }
' u. m6 c3 o' }1 y - else if(err != ERR_OK)
6 m% K- g, ?. {, `' L Z& S% s - {
! _- D3 P; p& B0 Z4 |2 W( ~! ` - //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存 ) K2 S5 U4 h$ V+ m
- ...
8 G- H7 c0 [2 x$ C; o/ F% k2 c# h - } 0 t5 u. @6 ~0 W2 n9 w2 D5 R/ P, R
- else if(es->state == ES_ACCEPTED)
) p( S6 T5 H+ T# Q - {
; q; }6 I) v% a [3 [ @ - //连接成功,在这里需要设置sent回调函数 . z0 A& v, j5 v: F' ?) x2 ]
- ... ) f7 G- f9 I' z& V& f! K
- }
% m/ W, m* x' o% D9 V# t% p - else if (es->state == ES_RECEIVED)
+ D1 k+ k U/ g8 b. Y* B& _& @ - {
% L( {. z3 G: H1 C - //从客户端收到数据 . \8 D- H2 ?3 t; y1 G
- ... 0 q8 e) w' a3 T$ g! Z- e& D
- } / y$ r7 q P m, |0 a! W
- else
$ r7 ~* f/ a( O" u - { ( x0 h1 G) r3 T1 u8 n
- //当连接关闭时,还收到了数据 ' x, O' ]$ f5 _. g5 m
- ... ; ?7 l; h6 s8 ^; ~( j
-
' J7 L: Q2 |( a) @ - }
6 n) w: @: c. M. c0 Y) r& }/ {: m% Q - . l' D7 P1 w/ o' K8 p* {
- return ret_err; 3 X3 u8 W: ?# j& l7 c% v$ ^
- }
复制代码 STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:
3 e H5 G6 i' d$ t
# {* k W% K) R* J4 i! s# k+ z
0 K" a9 m4 b m4 \. h
5 [4 p3 U3 Q7 X& v 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:) z8 ]6 P) Z) b8 k/ x( [0 t
echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server6 b! X. n1 M: H7 a" S, {' R5 p$ c8 z
4 `1 B* I0 N; _4 b
如果网络联通的话,测试成功将如下如下的画面,如图:
5 V' K6 M, z' k4 C4 G0 g) h0 {: ?7 s: ~5 e6 M4 A, b% u G; o
8 q. L% a+ c" V0 d. a _3 c
# d% p0 G* R6 e0 t0 c$ B2 X/ ^0 y
$ x; C) l# w0 o+ p/ G/ B: o& J
2 v: e' {* P* r( s+ F: \& H
# g1 e6 e1 [, L& y4 r/ `! J |