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

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

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

//=====================================================================
5 l+ B" g$ U! A//TITLE:
3 Y) j$ D5 {2 k% r1 G$ [//    STM32F2xx的tcp_echoserver例程解说
2 u8 o: E* T) v//AUTHOR:; f( y1 U3 v  x. x
//    norains
) v! Q) |$ g2 w- S+ \8 |/ c7 v% z//DATE:9 O7 h) a( H# d) K- A0 Z0 n  y
//    Monday  04-July-2011
# p9 u6 b: z0 g, j1 ?" V% I//Environment:2 t6 d* \- C: ^. ^& s7 ?8 R. f
//    Keil MDK 4.2. ^9 g  h) M/ O# X! i
//    STM32F207 核心版; Q" s5 w/ _+ L' W+ U! L
//=====================================================================

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


( j9 ~4 o4 ]( d5 p

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

    在tcp_echoserver_init()函数里,主要做了这么几件事情:
. I6 q1 c$ M9 n8 a- R     1. 创建一个新的TCP协议控制块) W4 _6 W  M$ C! r9 H. \7 U- z7 S+ R
     2. 绑定地址和端口号(port)
8 j7 N& j5 v: V! J; o     3. 开始监听(listen)
) |- x3 w- ]  O* @) g2 L1 o$ G: Q     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  7 Z' @" B# W7 S
  2. {  
    , Z2 H( G! z+ C
  3.   //创建一个新的TCP控制块  
    4 l7 i* X/ L9 W2 m
  4.   tcp_echoserver_pcb = tcp_new();  
    0 i0 ~7 }9 Y+ l: w; n7 X: Q& ?: I
  5.   ; h) [. L' h' I: W
  6.   if (tcp_echoserver_pcb != NULL)  
    ! T) Q* l3 ]7 }" s
  7.   {  ) `. X' N4 Z# F/ y* N5 c% K
  8.     err_t err;  * b* i5 A2 k! U  h+ G6 v: U! P) V
  9.       
    4 D4 z- S: ]- y* P. ?
  10.     //绑定到端口7  
    + Q/ u0 R5 F& U! b1 W$ C
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    / g4 V5 o+ }) X3 i
  12.       
    ! X. E& f" g( J
  13.     if (err == ERR_OK)  
    2 v+ Y$ {" `6 ?! E# P  q8 D* i; g
  14.     {  
    ; d8 L. z8 m( W1 V
  15.       //开始监听  
    6 g- w* u) h+ j* T
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  
    + b  M9 H6 N' y5 E
  17.         % F; t, k  G: E% f; O% S+ ^6 B* D
  18.       //设置tcp_echoserver_accept为accept的回调函数  & L/ q) a3 J6 v: k0 v1 f; T/ [
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  
    ) {/ L. d: X, E" k
  20.      }  % v/ Y1 D4 Z4 ^+ J
  21.     else   2 ]( _0 K8 ?3 m
  22.     {  4 w. h/ E# i2 ]( X1 O, m1 A
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  
    ' @, P- o$ T! s7 X7 T7 M/ Y, i
  24.     }  
    6 B; E' ^5 M4 m( ]
  25.   }  
    8 j  g& ?% `% Q: A9 a: ^+ j) H" n2 e
  26.   else  
    7 P7 I# d8 T0 o- d8 \$ i
  27.   {  
    0 h" D4 G- a& p1 M% h, E; h, V
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  ! a/ _! h' n  t6 J
  29.   }  * M5 [1 t( l& K- b: ^& @% M! O& Z7 b
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。
! A, J& n2 D; O+ U4 O) I  
0 l3 M9 d2 u/ T2 n2 E  ?  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  2 y: l% X: n" ~
  2. {  
      e2 W% j# O& H. G7 e# _: Y! O
  3.   err_t ret_err;  
    ; J. G! F4 F2 ~" @- a( H' R' z
  4.   struct tcp_echoserver_struct *es;  ( n, j0 ~* z8 I9 T* D% z
  5.   0 z# P$ R; M  ~9 g9 V
  6.   LWIP_UNUSED_ARG(arg);  
    / p' X1 s9 N6 j) o0 m+ C9 f
  7.   LWIP_UNUSED_ARG(err);  
    ; u" U9 B: G  g$ u
  8.   / k; Y# }* G: N
  9.   ///给新的连接设置优先级  
    3 C+ B9 A# _5 N) [
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  
    3 @" p2 u3 B- T0 @* f
  11.   " }5 ^' |* y; W% ?) ]3 D8 V6 Z( A
  12.   //分配一个结构体空间以保持TCP的连接  2 S# M7 f8 \+ z. i3 @* b2 ~" @
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  
    / V3 ?6 `1 t' ~* X' Q- v' q6 @
  14.   if (es != NULL)  , e% r. T6 O4 Q
  15.   {  ) X  q0 j2 S) K
  16.     es->state = ES_ACCEPTED;  
    . O/ k0 L: B, K8 y/ ~
  17.     es->pcb = newpcb;  
      W5 x; t4 n$ N- Q  V
  18.     es->p = NULL;  1 N; L8 N: V5 b) ?) _6 i/ ^" C
  19.       # p2 y( K& _- N$ o8 T. S" P5 m
  20.     //传递新分配的结构体数据给新的pcb  ! C. G: w5 N# j" A' I
  21.     tcp_arg(newpcb, es);  
    / Z. G6 Q8 E1 W1 l& r
  22.       6 h: q: @) o+ F% e4 \# R; z2 B+ O- h, s
  23.     //为新的连接设置receive回调函数   
    : m( p2 P1 w: ]% A
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  % e& l$ c" N$ v( j, ]
  25.       
    # D" f+ H0 l) D. N. t' o; g- b
  26.     //为新的连接设置error回调函数  3 K7 N; \+ ?: G. O
  27.     tcp_err(newpcb, tcp_echoserver_error);  
    $ [6 w; b8 t. a4 I/ }# L# c
  28.       
    ' N8 ~* W% Y% D' p5 f8 ~1 h, b5 U* `
  29.     //为新的连接设置poll回调函数  
    9 I2 x) j3 W: i" s2 ]4 b; T: y. _
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  + w. ~& {' Z  ?, [) e
  31.       1 ]' S9 ^8 P- K4 @! U8 C  o; G
  32.     ret_err = ERR_OK;  : d) I3 Y" G# A4 c
  33.   }  - w6 H! ~3 G  ]3 [0 g# G* @
  34.   else  
    + ]9 B; [+ _3 l8 U/ x
  35.   {  
    . S- x1 k0 V% p: f( i9 d* G6 b
  36.     /* return memory error */  
    ) s  G  Q! F% E3 A6 _1 U
  37.     ret_err = ERR_MEM;  1 H! A& a/ X6 o, ~! ]' J  e
  38.   }  ! C! u3 L, W8 z
  39.   return ret_err;    ) Y! c3 A0 h2 Q2 E
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:
, }$ z3 m% O/ h# O; |. T

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  / d; o; p7 [2 Z9 p9 l, I4 u& t! M" T
  2. {  
    6 J8 C; |# p$ G5 g
  3.   struct tcp_echoserver_struct *es;  
    4 K& Y# Y2 V* D
  4.   err_t ret_err;  2 R1 ]* O% N# M& `& ^
  5.   2 l) y2 D: J4 C  B1 h8 q
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  
    7 ]# u1 D. z- E( [& C4 k+ R
  7.     - x0 V: z9 N3 l* k+ f. Y3 b
  8.   es = (struct tcp_echoserver_struct *)arg;  
    * `; r; S; O4 D( L7 O- f+ a0 g
  9.   
    - _0 }# g: J5 ?0 u) ?2 M' J
  10.   if (p == NULL)  
    : `' \2 V' g5 o  M
  11.   {  
    7 E% l  |. O  [: {$ j" f. ^; q$ F! u2 x6 _
  12.    //如果接收到空的帧,则释放连接  0 v# x* V9 i; F
  13.    ...  
    & k: ?" n" R: G+ m+ K
  14.   }     
    / w+ D/ P! Y$ w# N+ Y
  15.   else if(err != ERR_OK)  
    ; H7 U# b$ d5 u& h/ M' K4 C! L
  16.   {  2 A# p2 d% j3 B) r7 M4 W# _
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  
    0 S2 o. ^. s% |$ M' O
  18.    ...  
    9 |7 V' m3 |5 ], V
  19.   }  
    5 \' }( K& J4 @# g2 j4 ^6 `
  20.   else if(es->state == ES_ACCEPTED)  
    - o% }) r- U; f  y
  21.   {  9 A/ u, o! t) g
  22.    //连接成功,在这里需要设置sent回调函数  
    3 J- `8 Z  n$ a6 K# E0 ^& [) C
  23.    ...  $ V% {7 f7 [" E) T. e
  24.   }  
    0 G6 C% {/ z. i. F) e
  25.   else if (es->state == ES_RECEIVED)  
    1 x' u+ |, \4 B$ D% V% z" F
  26.   {  
    - V& |/ [) U/ @2 C
  27.    //从客户端收到数据  3 E+ T$ V* e. W. @( l
  28.    ...  
    ) D. o/ u, X; `% B/ p9 F
  29.   }  # C/ i$ [' {9 d, }' |9 a
  30.   else  " J5 Q/ S: G# A% T# r
  31.   {  0 n- ]  w9 `" |) e8 V; K" |0 _) `
  32.    //当连接关闭时,还收到了数据  " W: _6 u  O! A+ c5 g
  33.    ...  
    9 A8 v* |- ^; [& V, q
  34.   0 y9 ^. W1 @5 T4 o$ \
  35.   }  
    - U3 U7 a% ?1 q
  36.    
    $ G7 {' P, K+ g2 p7 v1 w2 D
  37.   return ret_err;  
    ! {/ L& S7 `" _
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:
7 j/ ~& Y! ]8 v/ h; Y* i) M# S1 O% b: E: R: ~5 n& N( k3 \) ^
0_1309762874AF1I.png

+ z6 u7 w2 [8 C- w* D% R9 V: d! H) w
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:
" ?+ }3 p* t6 c   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
0 f1 e0 f4 h5 H3 X5 Y) U; i4 y6 [  ; ^3 k& \: E2 X0 x& l
  如果网络联通的话,测试成功将如下如下的画面,如图:& O; V6 x5 U4 H

( @* G  i2 z4 [% N, ^0 j
0_1309762878a9k1.png
; D3 ]7 C& J3 q* B+ Y6 k) P
$ c- |% I- ^% ~( F" `7 h
3 d! F* E& r% V0 ^) J1 t# x

. g- E# [" v$ X- }; K* c0 n# Q0 x& g& J+ Y' D) V% F
收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版