你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F2xx的tcp_echoserver例程解说

[复制链接]
STMCU小助手 发布时间:2021-12-2 14:20

//=====================================================================
, ]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的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  ; k: z" E2 |. T& N* O
  2. {  + k1 g# \+ b/ k8 c5 a4 \
  3.   //创建一个新的TCP控制块  6 o6 |* q  A+ p0 K" D1 u  D
  4.   tcp_echoserver_pcb = tcp_new();  ; S9 R* j+ t% C
  5.   
    * o( N5 A9 B0 n$ K8 b1 L" f2 Y
  6.   if (tcp_echoserver_pcb != NULL)  
    3 d. l9 K0 h- p* g3 C7 s7 V5 l
  7.   {  3 d% T3 Q# `1 A
  8.     err_t err;  - q0 e9 ?# n9 [* D" s  n. r& u: M
  9.       
      T; G1 m# S5 ^5 R! H/ o9 m
  10.     //绑定到端口7  
    # p- N2 Z8 R" \1 E6 \
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  % _7 H6 N/ Q' V7 I( m; P. R: S) D
  12.       : D% E8 ~/ ]  A6 x, e
  13.     if (err == ERR_OK)  * _4 K% F; h* I! a& x6 a
  14.     {  
    2 C' T# K3 U' d: I; c" C
  15.       //开始监听  " U- |' A+ E. ?1 a: Z
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  
    - I- _0 G6 P; j) }8 v& }
  17.         & Y! Q( A7 G4 W* A  V% J( |8 d  n4 w9 c
  18.       //设置tcp_echoserver_accept为accept的回调函数  8 m8 t" s" Y, v
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  
    ; {/ C& i: [8 r& R  m2 {+ m
  20.      }  
    : h0 L# }# h' D. b$ D* u6 r# k
  21.     else   ; b  H' E# m) `1 I% Y, A; S9 @
  22.     {  
    , I  W0 `0 o) V* b6 ~" h% r
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  " P/ Q3 }& ]3 q5 Z. r
  24.     }  ; k. C; j( d4 x' D  W
  25.   }  6 O8 H( w/ ?0 \/ O
  26.   else  
    ; h% e6 q9 X* I, E1 f
  27.   {    `% H) l9 n/ W1 a! z' N  V3 T5 P
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  
    ' {6 u* S/ K3 E" c, u. d4 y
  29.   }  . w2 x6 Q) m$ n' c
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。
) ]' j/ `7 i$ d+ a, w! G) g7 V  
$ e" z& }1 O" h  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  
    : f( z0 s) h  i' s9 |( c  X
  2. {  6 b! `+ E1 r5 _
  3.   err_t ret_err;  
    $ [" X$ `/ }# P; F" p  h6 S/ k
  4.   struct tcp_echoserver_struct *es;  / T3 y& P2 H6 O& l
  5.   
    ) @) F, ^, s% f" P+ J8 O( w
  6.   LWIP_UNUSED_ARG(arg);  * d) ]8 m" E' S) A
  7.   LWIP_UNUSED_ARG(err);  
    & c' h9 o& u: G5 Q7 m
  8.   2 B( Z& f* Q5 L9 y
  9.   ///给新的连接设置优先级  
    5 e& @) a1 |" _  y+ ~1 ]
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  * H8 V; f- _5 ?* n4 o( h
  11.   4 M7 Z9 y3 P) a0 W% f
  12.   //分配一个结构体空间以保持TCP的连接    \* D0 y1 ^6 f2 b' Z% D- Q
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  3 o; C8 c2 ~- B2 c  t, J3 k
  14.   if (es != NULL)  ( x8 l0 n9 H* p( i
  15.   {  
    ) ~2 y6 F) W1 d' J3 o
  16.     es->state = ES_ACCEPTED;  
    7 m8 ?, F& E6 `' \8 S
  17.     es->pcb = newpcb;  
    5 N! u( q. W- b
  18.     es->p = NULL;  0 q2 C/ m6 L* }2 K0 r/ Q
  19.       3 [1 F2 w$ B8 e9 ?% U+ n( t
  20.     //传递新分配的结构体数据给新的pcb  8 @* W+ T. O2 J. [3 h% J
  21.     tcp_arg(newpcb, es);  + E! r; \1 _! Y9 x, O" `  Q" l
  22.       
    5 M: [% O- o& S% w7 d
  23.     //为新的连接设置receive回调函数   3 [4 w9 {$ J( c8 I1 k
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  
    : ?& w/ p) Y$ B$ ]+ z
  25.       
    & ]1 Z/ F; [) C7 n6 ?
  26.     //为新的连接设置error回调函数  - i$ o9 A6 `# [# \6 B# S: D
  27.     tcp_err(newpcb, tcp_echoserver_error);  
    2 @5 J: ?0 C/ j( M- {/ ?
  28.       2 n/ b* W6 J4 J8 X0 }
  29.     //为新的连接设置poll回调函数  1 j/ \5 M$ x) L
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  
      Z% J. o/ {7 H7 I! B1 `
  31.       
      G" J( d3 ^7 s/ T3 o
  32.     ret_err = ERR_OK;  
    ( i4 ^4 p: b/ C/ I' y  |6 q6 ^4 w
  33.   }  / A$ N/ n8 R8 Y% g. n: g, @
  34.   else  
    # t7 ]9 h: Y% Y: J, r
  35.   {  - c% a" L/ }$ L% s
  36.     /* return memory error */  
    0 C, z$ t$ e; x7 c
  37.     ret_err = ERR_MEM;  + G4 C0 W* z# S( r) Y; @5 i
  38.   }  ; s' c! @% h' y9 |1 h- [
  39.   return ret_err;   
    . z& \6 D9 o% a, m5 J) X
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:' Q) X6 c7 n; d$ [; A$ _

  1. 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. {  
    2 O% x; I8 O* {' O
  3.   struct tcp_echoserver_struct *es;  
    ( d2 D8 ?2 u1 e
  4.   err_t ret_err;  " X+ Z1 M! ?: G- t, `
  5.   " G0 @2 o% s5 a  d, }- N
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  
    : ?: L9 z5 X9 C/ }& q
  7.     * B( Z! X# j/ L# M8 v, j
  8.   es = (struct tcp_echoserver_struct *)arg;  3 W+ z1 \0 ~* S4 {
  9.     n$ b* D7 T5 D5 p$ S
  10.   if (p == NULL)  
    - n4 e/ e, O$ W, s" i% d
  11.   {  . G! W. T# w# i0 W( O; e; Y6 s
  12.    //如果接收到空的帧,则释放连接  
    7 A% G8 L  d& L6 C- H
  13.    ...  
    3 h. p9 @- @! J8 a
  14.   }     
    3 t3 K2 R4 J- W5 T
  15.   else if(err != ERR_OK)  
    * n/ D' M* N5 m$ i& j
  16.   {  
    / f1 d9 m' d, B8 r4 F
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  
    7 q  t, o5 z& I: H- q9 |
  18.    ...  ' z4 v0 s+ s( _: ^! }
  19.   }  
    ) q1 p) t# Q5 r+ N% `9 q2 f/ D
  20.   else if(es->state == ES_ACCEPTED)  
      E/ b1 v0 A* N3 x! z9 v5 o
  21.   {  
    + d9 Q$ C1 y5 P. U: D8 V
  22.    //连接成功,在这里需要设置sent回调函数  . \: t  {3 `, N$ q7 |6 i
  23.    ...  $ A+ ~' u" q' ?
  24.   }  0 _' e2 b1 t, E
  25.   else if (es->state == ES_RECEIVED)  
    - R, n$ c8 c. g9 |! S( V% C! v
  26.   {  
      ^& ~1 K3 h; e4 J4 b9 m+ q5 j
  27.    //从客户端收到数据  ! r4 m* @2 M; S8 U' \
  28.    ...  ( m. s3 m$ Y, v; f9 w  V; c* A
  29.   }  
    6 @3 G  m% i' ?5 E
  30.   else  8 s; U3 z, i# f% l
  31.   {  
    7 A  t3 `5 a( l$ S% ?' a, y- O
  32.    //当连接关闭时,还收到了数据  
    1 _3 X; |$ A: n5 O
  33.    ...  
    & R2 s# F9 S& q/ @" ]
  34.   
    7 l( \7 g! z$ N
  35.   }  
    $ V# C& [; p9 X) e* M) B+ _9 o
  36.    
    8 Q3 |* |2 U; J1 l0 G
  37.   return ret_err;  6 Z/ y: }9 V% b7 v$ h3 O
  38. }  
复制代码
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- |, K
0_1309762874AF1I.png
2 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' ]
0_1309762878a9k1.png
, 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
收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版