lwip下TCP客户端的实现

stm32f107vc lwip tcp客户端 服务器数据传输
第一篇        TCP客户端模式简单数据收发
             ----控制开发板LED灯
概要 
  建立LWIP客户端模式,科星F107开发板做为客户端去连接PC测试软件模拟的服务器,实现简单的数据收发,通过上位机控制板子的LED灯
一 打开工程《科星F107开发板网络应用篇之TCP客户端》
   打开MAIN.C主文件
int main(void)

  System_Setup();
  LwIP_Init();
  //Client_init();
while (1)
  {    
        
     LwIP_Periodic_Handle(LocalTime);
  }
}
上面和前面讲到的服务器模式,区别只在于Client_init();函数,别的和前面都一样,这里不再讲解,我们主要看一下Client_init();函数里的内容 看看客户端和服务器的区别。
void Client_init(void)
{
  struct tcp_pcb *pcb;        
  struct ip_addr ipaddr;
  IP4_ADDR(&ipaddr,192,168,0,22); 远程服务器IP 写进全局变量
  
  pcb = tcp_new();           建立一个新的tcp 链接                                     
  tcp_bind(pcb, IP_ADDR_ANY, 23);   绑定开发板IP和端口号 这里端口号可以是0 也就是随机,因为是客户端 不必知道端口号。    
  tcp_connect(pcb, &ipaddr, PEER_PORT, tcp_Client_connected);        主要是这里和服务器不太一样,服务器模式是监听listen,客户端的话 就要主动去连接服务器 我们看一下 参数的含义
PCB 就是我们前面建立的tcp端口
IPADDR 要连接的远程服务器IP 这里设置的是测试的电脑的IP
         这里设置的是IP4_ADDR(&ipaddr,192,168,0,22);
PEER_PORT 远程端口号 我们要连接的端口号 这里我们设置的是5000
#define PEER_PORT    5000        
看一下我们测试软件和电脑的设置截图 主要是电脑的IP和上位机的本地端口号 别的没用
  
tcp_Client_connected 连接到远程服务器成功后的回调函数 连接成功后会调用这个函数,我们看一下这个过程是怎么到这个函数的
进入函数:        tcp_connect(pcb,&ipaddr, PEER_PORT, tcp_Client_connected);
  下面第四个参数* connected就是回调函数 在程序里找到这个
err_t
tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port,
      err_t (* connected)(void *arg, struct tcp_pcb *tpcb, err_t err))
{
  err_t ret;
  u32_t iss;
  LWIP_ERROR("tcp_connect: can only connected from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
  LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
  if (ipaddr != NULL) {
    pcb->remote_ip = *ipaddr; 远程IP
  } else {
    return ERR_VAL;
  }
  pcb->remote_port = port; 端口号
  if (pcb->local_port == 0) {
    pcb->local_port = tcp_new_port(); 本地端口号
  }
  iss = tcp_next_iss();
  pcb->rcv_nxt = 0;
  pcb->snd_nxt = iss;
  pcb->lastack = iss - 1;
  pcb->snd_lbb = iss - 1;
  pcb->rcv_wnd = TCP_WND;
  pcb->rcv_ann_wnd = TCP_WND;
  pcb->rcv_ann_right_edge = pcb->rcv_nxt;
  pcb->snd_wnd = TCP_WND;
  /* As initial send MSS, we use TCP_MSS but limit it to 536.
     The send MSS is updated when an MSS option is received. */
  pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
  pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
  pcb->cwnd = 1;
  pcb->ssthresh = pcb->mss * 10;
  pcb->state = SYN_SENT;
#if LWIP_CALLBACK_API  
  pcb->connected = connected; 回调函数传递给我们网络端口connected
   既然是连接成功后回调这个函数 那么在哪里检测是不是连接成功呢?
  如果调用的话肯定是调用pcb->connected 因为他就是回调函数 我们刚才已经传递给他了
这里如果对LWIP不熟的话 可能就搜不到了,我们直接告诉大家 在TCP.h中
  #define TCP_EVENT_CONNECTED(pcb,err,ret)                         \
  do {                                                           \
    if((pcb)->connected != NULL)                                 \
      (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
    else (ret) = ERR_OK;                                         \
  } while (0)
所以我们搜索TCP_EVENT_CONNECTED就能找到连接远程服务器成功后 程序到了哪里 并且调用了我们的回调函数。搜一下:
  在tcp_process(struct tcp_pcb *pcb)函数中 果然 这个就是tcp进程的函数,我们看在哪里调用了?
switch (pcb->state) {
  case SYN_SENT:
    LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
     pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
    /* received SYN ACK with expected sequence number? */
    if ((flags & TCP_ACK) && (flags & TCP_SYN)
        && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
      pcb->snd_buf++;
      pcb->rcv_nxt = seqno + 1;
      pcb->rcv_ann_right_edge = pcb->rcv_nxt;
      pcb->lastack = ackno;
      pcb->snd_wnd = tcphdr->wnd;
      pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
      pcb->state = ESTABLISHED;
#if TCP_CALCULATE_EFF_SEND_MSS
      pcb->mss = tcp_eff_send_mss(pcb->mss, &(pcb->remote_ip));
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
      /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
       * but for the default value of pcb->mss) */
      pcb->ssthresh = pcb->mss * 10;
      pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
      LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
      --pcb->snd_queuelen;
      LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
      rseg = pcb->unacked;
      pcb->unacked = rseg->next;
      /* If there's nothing left to acknowledge, stop the retransmit
         timer, otherwise reset it to start again */
      if(pcb->unacked == NULL)
        pcb->rtime = -1;
      else {
        pcb->rtime = 0;
        pcb->nrtx = 0;
      }
      tcp_seg_free(rseg);
      /* Call the user specified function to call when sucessfully
       * connected. */
      TCP_EVENT_CONNECTED(pcb, ERR_OK, err);就是这里 接收到服务器的回应 具体是怎么到这里的 这个就得靠大家去看lwip函数讲解了 这里就不多说了 大致的流程已经和大家说了。
      tcp_ack_now(pcb);
    }
#endif /* LWIP_CALLBACK_API */
  TCP_RMV(&tcp_bound_pcbs, pcb);
  TCP_REG(&tcp_active_pcbs, pcb);
  snmp_inc_tcpactiveopens();
  
  ret = tcp_enqueue(pcb, NULL, 0, TCP_SYN, 0, TF_SEG_OPTS_MSS
#if LWIP_TCP_TIMESTAMPS
                    | TF_SEG_OPTS_TS
#endif
                    );
  if (ret == ERR_OK) { 
    tcp_output(pcb);
  }
  return ret;
}
                
                                                }
OK 这样连接建立后就进入了回调函数tcp_Client_connected 进入函数后
err_t tcp_Client_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
   tcp_write(pcb, GREETING, strlen(GREETING), 0); //输出hello
   tcp_recv(pcb, HelloWorld_recv);//指定接收到数据后的回调函数
  return ERR_OK;
}
我们看到这里和服务器又一样了。连接成功后发送数据给上位机 
#define GREETING "Hello.连接已经建立!欢迎来到科星F107开发板网络学习!\r\n"
然后TCP_RECV指定了接受数据的回调函数 这里我们直接调用了服务器模式时候的函数。
这样客户端模式的通信 就完成了。
注:
  因为客户端的连接,每次都是有固定的流程和时间,也许上电开始服务器并没有准备好,或者是通信过程中由于某些原因连接断开了,那就需要我们能够保证这种情况下也能连接,那么我们就得在没有连上的时候,一直去尝试连接,才能做到从连,所以我们实际应用中把客户端连接的函数Client_init();放到了主循环中。
测试流程
下载程序到开发板,打开上位机 电脑和上位机的配置按照上面的截图
点击“开始监听” 等待开发板客户端的连接 连上后如图
点击“发送” 开发板会返回相应的数据:如图
这样 客户端模式数据通信就实现了。
结束语
在测试一下断线从连,点击“停止监听”等一会在开始监听看看能不能连上?怎样做才能做到更好?这也是开发产品应该完善的。
功能跟进 TCP客户端模式控制开发板的LED灯和蜂鸣器
打开工程《科星F107开发板网络应用篇之TCP客户端-LED灯》
下载程序到开发板,打开上位机,参数配置如下图,
   
开始监听 等到灯变红色 就可以控制开发板的LED和蜂鸣器了 自己试试吧。
【转自】 http://bbs.21ic.com/icview-641202-1-1.html

你可能感兴趣的:(STM32学习笔记)