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

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

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

//=====================================================================5 O' B6 o/ v0 ]" k0 l' M8 Z
//TITLE:
8 ^& L) r' U0 i7 Q//    STM32F2xx的tcp_echoserver例程解说8 W3 g- [0 z2 X0 z  {8 N% ~% }: Y
//AUTHOR:1 ^7 n0 \* d! `+ r+ k6 g
//    norains
1 {% g. q; U' o//DATE:3 r- @$ P/ A9 u  E2 v3 t5 Y
//    Monday  04-July-2011
4 c: n/ {: h# H% F! M3 W1 l- ?//Environment:
8 g0 b+ N0 ~8 e$ T8 L$ b% F8 G//    Keil MDK 4.2
" \; v( ~+ m( Q3 f6 O//    STM32F207 核心版% f7 e# z  V2 z, N
//=====================================================================

     最近拿到STM32F207的核心版,板载有网卡芯片,自然要拿过来捣鼓一番。而对于一个从未接触过网络的菜鸟来说,最好的入门方式就是揣测ST公司的例程,所以今天norains也不例外。那么我们就一起来看看这个官方的例程吧!


+ Y" c/ x7 g3 Y# i/ \" d: O( Z

    ST关于TCP的例程分为client和server,根据字面意思,可以知道tcp_echoserver例程是将STM32F2xx作为server来用。而例程的第一步呢,便是初始化,调用的是tcp_echoserver_init()函数。

    在tcp_echoserver_init()函数里,主要做了这么几件事情:
; j. P2 y# ?% z, U% _     1. 创建一个新的TCP协议控制块) n. v9 @6 D) g6 ]
     2. 绑定地址和端口号(port)
' H; j) N% X( E9 O     3. 开始监听(listen)
* g) ^, y9 ], l' V+ `. q     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  
    " k3 q. i0 x& |% y+ K9 j4 q* r
  2. {  ( l& P( M" B: [$ I2 B
  3.   //创建一个新的TCP控制块  
    - \9 K2 z4 H# k& J& z$ E
  4.   tcp_echoserver_pcb = tcp_new();  
    . P% F6 |8 d( H" p- p0 T$ w
  5.   ( a% D$ O3 U/ b
  6.   if (tcp_echoserver_pcb != NULL)  ; A' a$ G7 X- R' m4 Q  l
  7.   {  
    6 e9 C' E. h/ n# r# F6 Y$ {
  8.     err_t err;  7 o& U1 H" t' w6 K  I9 m5 W( U' |3 @
  9.       
    ; [7 r# E2 p3 o1 S
  10.     //绑定到端口7  3 y* ?% ]7 h+ C5 ~% p2 m
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    8 ^3 c( \7 Q/ l1 g8 @* D
  12.       
    0 @7 Z9 F: D. T' v
  13.     if (err == ERR_OK)  & ]6 w" E- c6 F. Y' x; ^  N
  14.     {  
    ! `' R: b1 S1 }
  15.       //开始监听  ! X, D  r! e) ?, |
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  
    & s+ d) \' ]# U; `1 K) k' [
  17.         6 A) w: ~) O' k6 k
  18.       //设置tcp_echoserver_accept为accept的回调函数  4 O% e4 `* Z, u' f# Z
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  ; }( v  m/ Z+ Y7 j7 s
  20.      }  5 |, Y$ v' W- h$ y+ E" ?) B0 c
  21.     else   
    ' N$ g4 P* c) c9 U. X" u
  22.     {  1 m" I) N: ^, f3 l' Z6 ]
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  8 @; q+ J! [/ T. z; m
  24.     }  
    , V: Z; T4 o6 L) @; |" L% s
  25.   }  * S& e6 J( o2 K
  26.   else  
    4 `) D; j8 v7 O4 u% }. B
  27.   {  / [1 ], e6 s7 e
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  
    $ ~& V, k) a7 _4 X3 t: {
  29.   }  
    1 D  ~$ v- x" h+ L- P! o
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。2 N! ^% V# p0 ~9 ]5 q8 S
  % M+ }6 p; ~4 e  Z6 X' ^
  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  
    4 H8 N) y- [) p* [  q* R
  2. {  & m* [+ |& |+ R$ ^
  3.   err_t ret_err;  
    # |; k2 D; c- z7 G
  4.   struct tcp_echoserver_struct *es;  
    + p( A; ]7 }% F& j' ^0 O9 z
  5.   
    : e- U1 I: S$ e8 t2 ~5 a
  6.   LWIP_UNUSED_ARG(arg);  ( T' k) }8 l, A+ i& b0 _+ X# {
  7.   LWIP_UNUSED_ARG(err);  
    8 R) P7 ~* r* S: I( G; n1 r
  8.   # t6 N9 c9 g9 e% D
  9.   ///给新的连接设置优先级  
    % f* J& }& N( T: j  A, E% d
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  
    + c0 L. L  R+ q0 N
  11.   
    % A6 V  j/ G+ H. p, F8 V$ ~
  12.   //分配一个结构体空间以保持TCP的连接  
    ! @+ B. W" H) ~# k7 c, Z0 |5 P+ }
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  
    ) n' W. `. |6 P( l1 X5 }- n
  14.   if (es != NULL)  1 p/ Z+ }/ `9 |5 O3 t; _! X
  15.   {  / i3 `' J3 P* q
  16.     es->state = ES_ACCEPTED;  ! z9 f' m- `$ p+ d" x+ P0 e) V! @
  17.     es->pcb = newpcb;  + |$ m  u/ M& I' C- H9 s
  18.     es->p = NULL;  1 N9 f9 k. `9 `
  19.       # u  g) c$ s; S! d# q, S& j4 @/ g. U
  20.     //传递新分配的结构体数据给新的pcb  ' f9 s0 |/ Z- k6 o. t% L
  21.     tcp_arg(newpcb, es);  
    . D/ L: t  `- ]
  22.       : t: o. I' m1 t2 S
  23.     //为新的连接设置receive回调函数   
      P: p0 J& G5 G( |3 u
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  
    * p+ o& L" V  G
  25.       1 t# Y1 [- }0 l+ K) x
  26.     //为新的连接设置error回调函数  9 W! l) I" m" x7 l: z
  27.     tcp_err(newpcb, tcp_echoserver_error);  # K, F7 K; g* `# N- e, q8 `( \
  28.       6 W, G. c1 \& l- o) m9 ]  w2 l1 s; U
  29.     //为新的连接设置poll回调函数  
    0 }3 }* ]$ F, g/ V: ^" }8 Z
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  , O: p& f+ N/ L- i+ X1 p( M, v
  31.       
    5 j- X/ ^, e4 C" `' r' c
  32.     ret_err = ERR_OK;  
    ' p( m& ^2 N2 i0 N8 d! }
  33.   }  " j+ Q/ y  ]; l+ c5 M
  34.   else  3 W) n# y3 ?  b
  35.   {  ) R' e) k6 r. {! P4 p
  36.     /* return memory error */  
    5 ]  D1 x3 j" \3 y# v# k8 I  |" y
  37.     ret_err = ERR_MEM;  5 P  s% J! E: Q7 p
  38.   }  - P( w9 p) Y7 {  D5 b( O0 R
  39.   return ret_err;    $ D$ V: C3 R6 e# i! s+ }
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:: r0 H1 _  p4 V9 z, y

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  . F  m5 t) w' Q3 M, L
  2. {  
      W. E  N& n, G: I
  3.   struct tcp_echoserver_struct *es;  
    ) s( {) r; ?/ x2 D
  4.   err_t ret_err;  3 u6 {2 r8 l& }0 z1 x: {
  5.   5 K8 _: @' U2 B/ r1 y
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  
    / \) X' X: ?7 c& G% n
  7.    
    . D9 \& R2 r0 {5 u$ C& K( T
  8.   es = (struct tcp_echoserver_struct *)arg;  4 H4 G% m: E$ c! F7 |/ X& v8 ~' C
  9.   
    & v7 n0 b0 B, ?/ @! h4 G
  10.   if (p == NULL)  
    # M( b* M, R$ l' l# q+ {1 r
  11.   {  
    4 }) L( N0 ^8 k" G
  12.    //如果接收到空的帧,则释放连接  
      e7 s% O' m, F' A  s% N7 s6 c
  13.    ...  / q! y+ @5 f+ |. O* H) y9 C5 J: f
  14.   }     % u8 o6 Q" h5 z" m( ?
  15.   else if(err != ERR_OK)  % D! |; g$ d1 Y% P. h
  16.   {  
    9 Q2 `9 I* c/ y  I1 z! e
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  4 I) z( O3 d% J) l& C
  18.    ...  
    % G) Q( h( K' Y
  19.   }  * v6 T, O" d4 U
  20.   else if(es->state == ES_ACCEPTED)  
    ' |4 Y, W- g$ z4 P) ?9 z) q1 o; Q' q
  21.   {  
    - |" \8 p' n/ S2 i
  22.    //连接成功,在这里需要设置sent回调函数  
    ( {" f# I/ d) `7 D4 d0 q
  23.    ...  
    # J( I& \. q( F/ F4 p0 h" k( _
  24.   }  & W/ N' z8 E/ B1 h
  25.   else if (es->state == ES_RECEIVED)  
    + t" h* Y0 M1 J6 Q3 g
  26.   {  
    5 e7 ~! \7 L8 y; i7 B* t: ]& x5 {
  27.    //从客户端收到数据  / P, n* j* ]8 V( C5 a
  28.    ...  
    2 d. d' g* w0 N; V. b8 H
  29.   }  $ \# X5 E$ s1 U
  30.   else  2 D( g' H6 ^% M; B( S. T2 u4 ?
  31.   {  ( p/ m) t( g  V# L% ^
  32.    //当连接关闭时,还收到了数据  
    7 m8 T/ f/ o6 |, C3 A# m% ^
  33.    ...  ; G/ O" j# |3 G! J/ [
  34.   ! j  u9 V" C* |$ ^4 N
  35.   }  4 I# f4 L+ B, O% }; p
  36.    
    8 P' O7 G" d# a; A4 B! P' B4 O  e
  37.   return ret_err;  * i. I8 l: [0 l7 x  Y4 q
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:6 f4 F! r' g' @2 x4 y, c9 T% V

' N; ~; y4 T+ s! K
0_1309762874AF1I.png
+ P* l' {8 M% G
! H5 c2 F8 B+ |& d( i
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:
$ Q0 d: T; `/ _. b- i( |# s6 Y7 z% G   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
) I7 s+ O9 [  b: N0 I7 N6 K  5 ^- k) X% I$ G% h
  如果网络联通的话,测试成功将如下如下的画面,如图:
4 \+ I( t0 a( A% ]
! s# h- S- c+ F( s
0_1309762878a9k1.png

3 }1 i1 D8 m0 ~- t  W2 Z) L) T. U+ c
5 L/ t( z9 t9 w, \! l! O
8 |- L9 R" h  F' V& j
# B" o& A+ k0 P; ~
赞 收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版