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

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

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

//=====================================================================) Y& x% X+ j: K: n6 j( P
//TITLE:  M! d" M0 A9 W. c; X7 L
//    STM32F2xx的tcp_echoserver例程解说
+ s! T% Z8 S% ^' N( \' \3 V//AUTHOR:8 w, C% o3 [1 `3 ?/ d0 ?
//    norains; U% X& U# H1 N5 i/ M
//DATE:
# T% T+ X1 Y4 ~/ q1 w2 d% \//    Monday  04-July-2011! k! u4 F8 {6 P  }& f  R7 m
//Environment:
. b* Q: P( e) G//    Keil MDK 4.2
4 E, L, _  A9 C+ N* I, x//    STM32F207 核心版  E2 L  _( }' F4 @% N; U$ H8 p
//=====================================================================

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

$ j& g0 j1 K  v% x; _( f1 w! y

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

    在tcp_echoserver_init()函数里,主要做了这么几件事情:
4 A( Q8 K" X2 E( E& O     1. 创建一个新的TCP协议控制块
8 T4 v5 v7 F; }9 }  h     2. 绑定地址和端口号(port)2 R8 @6 s2 w1 v5 E# h
     3. 开始监听(listen)2 |$ O2 j1 ^0 }% G" k
     4. 设置accept的回调函数

    其完整代码如下:

  1. void tcp_echoserver_init(void)  
    / F' T$ S$ t! _/ E7 Z
  2. {  & z: G6 X. v: h* H' m& m
  3.   //创建一个新的TCP控制块  + y1 ]$ ^* C3 T3 |
  4.   tcp_echoserver_pcb = tcp_new();  
    1 H& Z0 u9 i4 k
  5.   0 k- F/ Q0 G( t
  6.   if (tcp_echoserver_pcb != NULL)  
    2 u& f" Y% B- u% A4 e; g4 Y
  7.   {  6 f/ {5 F/ c/ O) K* b2 S
  8.     err_t err;  ) p) h$ A4 M" j4 c
  9.       
    7 a& T( y0 m/ F6 a
  10.     //绑定到端口7  . v" U2 `  G1 J. w
  11.     err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);  
    3 W7 _8 i+ E! O  x: Y; C
  12.       9 ?( U2 e: o: G3 O
  13.     if (err == ERR_OK)  
    8 d, S9 p$ i0 j# u, J3 F7 E. ]
  14.     {  
    ; a- p+ x1 t/ H; j  Y% N  v. V1 `
  15.       //开始监听  
    & s5 `+ a+ h  L
  16.       tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);  
    # P- Y$ t3 P4 }  a8 x4 a8 E6 o
  17.         ( l- v7 w# V9 M- X8 _5 v, O, @
  18.       //设置tcp_echoserver_accept为accept的回调函数  $ C/ t" a: }. b3 |6 U- e, Q
  19.        tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);  6 c  E3 }' k$ o
  20.      }  # J; I) I' x, x9 w" V
  21.     else   + P! q* d% K8 V2 r- f
  22.     {  1 ?9 Z0 m. L7 w' I) `
  23.         printf("Can not bind pcb\n");  //norains 2011-7-4 comment  9 }, W$ L" a$ Q) s% s: l
  24.     }  
    8 K$ f1 j/ \& A8 ^( h
  25.   }  
    ' T5 H6 e# m) V: o# h6 @3 k
  26.   else  
    6 H( K& K: ~$ C4 H( W' y: s6 o# F, V
  27.   {  2 [' @' i! f- w4 j+ M/ p5 F
  28.     printf("Can not create new pcb\n");   //norains 2011-7-4 comment  / P# y  F2 r, @
  29.   }  ( o% a8 P6 |" \4 l: T* N4 G
  30. }
复制代码

    当客户端开始连接之后,那么被设置的tcp_echoserver_accept()回调函数就会被调用。该函数主要是创建一个新的数据结构,并且将该数据结构传递给底层的TCP,最后分别是设置receive,error和poll这三个回调函数。: ~8 @) R+ ]9 v  @9 D  T! j7 d
  1 C( D9 X  i5 o7 b; b3 w+ G
  tcp_echoserver_accept()代码如下所示:

  1. static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)  
    * N) m5 g8 ~: h3 J
  2. {  
    9 r- H! p' I: X) |# c
  3.   err_t ret_err;  4 ^$ R* j- K- i3 D# e0 e
  4.   struct tcp_echoserver_struct *es;  
    7 {4 p. b2 D" t  y9 S/ v2 a. {2 I
  5.   + X# E2 u! H% w! n- `
  6.   LWIP_UNUSED_ARG(arg);  
    6 c  y2 O8 r* T
  7.   LWIP_UNUSED_ARG(err);  
    8 N) [# E  Y/ }7 [( U; M+ H' s3 S3 |
  8.     @  r3 `" `/ n; s/ L+ K9 D5 P
  9.   ///给新的连接设置优先级    q+ V  Z- s: i) K
  10.   tcp_setprio(newpcb, TCP_PRIO_MIN);  % q6 H1 Z* e. ?: S6 m- u
  11.   
    $ p1 G/ L  N1 M6 @5 D1 E
  12.   //分配一个结构体空间以保持TCP的连接  + _; y+ V9 o* T# p
  13.   es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));  
    : m* s' C) t9 O% g
  14.   if (es != NULL)  , D1 X+ E! O- A" d
  15.   {  7 W- w1 D: u/ c# r  Q5 h/ C* j2 y
  16.     es->state = ES_ACCEPTED;  
    / S8 r( w  c' l+ e8 v
  17.     es->pcb = newpcb;  
    1 f/ N, f! M4 V- Q; M9 l
  18.     es->p = NULL;  / V' g: C) U* b4 {$ z; h
  19.       ; y( j1 O# t5 w/ r: s
  20.     //传递新分配的结构体数据给新的pcb  
    * g9 q( x0 Y+ _/ `+ ]  p! t0 c) V  v5 A
  21.     tcp_arg(newpcb, es);  ' C# G# i9 o" d( Y
  22.       ' J- I2 a# R3 g$ h: j
  23.     //为新的连接设置receive回调函数   ; r, N1 |  a8 b6 k6 l) N: T
  24.     tcp_recv(newpcb, tcp_echoserver_recv);  . m1 R% E6 f3 O+ Y3 ]: U
  25.       
    3 V7 j1 \6 A! {2 f+ O6 j7 o
  26.     //为新的连接设置error回调函数  $ R/ R! {3 e: d* \) b( @4 y- Y
  27.     tcp_err(newpcb, tcp_echoserver_error);  
    9 k6 J+ Q9 B* G/ ~
  28.       0 s2 j! S2 C. G/ ^6 n
  29.     //为新的连接设置poll回调函数  : D- ]3 o: U' x% p5 t
  30.     tcp_poll(newpcb, tcp_echoserver_poll, 1);  & b) ^3 e! K2 @
  31.       ( e- |4 M8 f7 b/ u3 |( d/ c
  32.     ret_err = ERR_OK;  $ \5 q4 N( c& g. ]
  33.   }  
    # H1 J5 b0 M! B# X
  34.   else  
    2 B, s7 V2 T8 A( Z* @( h6 q
  35.   {  
    # w( K( r; A- m* K( T
  36.     /* return memory error */  4 n9 _  Z3 h/ j1 P2 Y% V2 O7 M
  37.     ret_err = ERR_MEM;  
    , f, @5 W) W+ x8 u
  38.   }  
    , _$ [4 N( `2 W$ ^: U7 y
  39.   return ret_err;   
    ; B$ S5 V. c. q
  40. }
复制代码

  接下来便是tcp_echoserver_recv()这个回调函数,因为该函数比较大,这里就不再全部罗列代码了。对于使用者来说,只需要知道相应的判定条件来代表什么意思就足够了,如:* U5 U; V' }! x/ l- a) M

  1. static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)  # `, ?& q( y# N+ Z
  2. {  
    " t4 k& |. M' L2 B2 S
  3.   struct tcp_echoserver_struct *es;  
    ) m! o& d4 ~2 G& f. w
  4.   err_t ret_err;  
    * U! c2 G! H  @! b; L0 {
  5.   ; a" Y1 L. Z8 w) w: N1 W. K1 l
  6.   LWIP_ASSERT("arg != NULL",arg != NULL);  
    ) A. K' [$ [/ C% I1 O' Z' r
  7.     $ O8 ~$ T+ Z. r# g% ^! o4 T* E
  8.   es = (struct tcp_echoserver_struct *)arg;  ' c3 L5 ]1 w/ Y2 _! N
  9.   
    % W+ }, B- O9 J% M' b) w
  10.   if (p == NULL)  
    9 D1 t6 ]8 d' n' i7 N) o
  11.   {  # a* K4 M, [2 o6 w
  12.    //如果接收到空的帧,则释放连接  8 d) m& W) h8 [/ v& v- ^
  13.    ...  # ]; d- o5 D3 a- J; `" e/ y0 i/ S
  14.   }     , n& j% R1 c9 K# l6 ]; }
  15.   else if(err != ERR_OK)  
    3 q, D; ~3 P& Y# a7 x
  16.   {  
    . i) f  K5 P5 c' d
  17.    //接收到一个非空的帧,但可能某些原因出错,导致返回值不为ERR_OK,故在此释放缓存  
    0 Y6 [$ y) p5 r% G
  18.    ...  * I; q1 ~) ?: r. \/ j# S" j) U1 p
  19.   }  
    2 G- a- U$ e" C% @
  20.   else if(es->state == ES_ACCEPTED)  
    ( x0 D! w+ M; g! m  i8 @
  21.   {  7 G# ^5 i8 Q5 R( W; y
  22.    //连接成功,在这里需要设置sent回调函数  
    0 F* R9 r2 C3 a; M- n& H# ?
  23.    ...  
    & O6 W, y# h+ F. ~" V- i3 H
  24.   }  
      g& E* x5 Q4 T: R) U
  25.   else if (es->state == ES_RECEIVED)  
    / ?9 A3 _: P$ d% s
  26.   {  
    : a! B+ y/ [( V
  27.    //从客户端收到数据  
    , y) L" z; k3 s# H1 @
  28.    ...  
    4 l1 S! I& X2 [4 M
  29.   }  
    " ]6 M: y: @" s: q
  30.   else  2 C7 p, L2 c" k" m, M  V
  31.   {  ; W( r* F8 S5 c, Y2 A9 v% c- p
  32.    //当连接关闭时,还收到了数据    c6 F2 V+ P6 F7 L- y
  33.    ...  ; Y% O" q8 L4 ]4 T
  34.   , s$ C  R; g) j4 e4 z& f1 J
  35.   }  8 I0 E' k8 g3 ?; n# X
  36.     # ]1 y( p/ X5 C7 R1 t0 D" M
  37.   return ret_err;  ( W8 V/ l6 h; o2 X* A
  38. }  
复制代码
STM32F207的代码部分就暂时说到这里,现在的问题是,如何 测试这代码的正确性呢?这就必须用到ST提供的echotool.exe程序了。该程序位于stm32f2x7_eth_lwip的PC_Software文件夹中。该程序必须在命令行打开,其大致参数如下所示:/ t6 S% j0 K; j' X; {# S

2 h% w+ [' Q+ Q8 E
0_1309762874AF1I.png
. p- O0 ?% }7 V0 U# z9 `& }9 |

; o/ z$ k! s5 o6 `$ @# w: @8 e
 如果我们的serverip地址为192.168.0.8,那么可以输入如下命令进行测试:
7 n- `; N% `' }1 B. M8 S4 H5 U4 L  a6 w   echotool.exe 192.168.0.8 /p tcp /r 7 /n 15 /t 2 /d Testing LwIP TCP echo server
% j# f! d( H/ k- X+ u  
& G6 w3 r9 y. }- v( Y; B! T# z2 B  如果网络联通的话,测试成功将如下如下的画面,如图:# b2 b# J# R2 Y2 n$ b0 ~

) k& M! g( k# J# p0 `- w
0_1309762878a9k1.png

% V+ \* G: R$ B+ d. d. q
& |3 |* m: q3 T* Q, e; ~
$ S; R& N6 W2 k' T- }$ G
3 q0 n) a5 m% {6 U5 E4 ^. t+ ?  g2 Y2 w: s6 Z& L- B6 A+ C
收藏 评论0 发布时间:2021-12-2 14:20

举报

0个回答

所属标签

相似技术帖

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