TCP回响服务器实现及源码分析

tcp_echoserver.c 移植

文件目录

C:\Users\Think\STM32Cube\Repository\STM32Cube_FW_F4_V1.24.1\Projects\STM324xG_EVAL\Applications\LwIP\LwIP_TCP_Echo_Server\Src

实验分析

tcp_echoserver 业务分析

static struct tcp_pcb *tcp_echoserver_pcb;

/* ECHO protocol states */
enum tcp_echoserver_states
{
  ES_NONE = 0,
  ES_ACCEPTED,
  ES_RECEIVED,
  ES_CLOSING
};

/* structure for maintaing connection infos to be passed as argument 
   to LwIP callbacks*/
struct tcp_echoserver_struct
{
  u8_t state;             /* current connection state */
  u8_t retries;
  struct tcp_pcb *pcb;    /* pointer on the current tcp_pcb */
  struct pbuf *p;         /* pointer on the received/to be transmitted pbuf */
};

void tcp_echoserver_init(void);
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err);
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err);
static void tcp_echoserver_error(void *arg, err_t err);
static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb);
static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len);
static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);
static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es);

tcp_echoserver_struct

/* structure for maintaing connection infos to be passed as argument 
   to LwIP callbacks*/
struct tcp_echoserver_struct
{
  u8_t state;             /* current connection state */
  u8_t retries;
  struct tcp_pcb *pcb;    /* pointer on the current tcp_pcb */
  struct pbuf *p;         /* pointer on the received/to be transmitted pbuf */
};

tcp_echoserver_init

/**
  * @brief  Initializes the tcp echo server
  * @param  None
  * @retval None
  */
void tcp_echoserver_init(void)
{
  /* create new tcp pcb */
  tcp_echoserver_pcb = tcp_new();
  //内存块是动态分配的,需要判断是否创建成功
  if (tcp_echoserver_pcb != NULL)
  {
    err_t err;
    
    /* 
    	针对我们创建的tcp控制块,绑定本机的ip第一种,以及端口7
    */
    err = tcp_bind(tcp_echoserver_pcb, IP_ADDR_ANY, 7);
    
    if (err == ERR_OK)
    {
      /* 
      	开启监听
      	监听返回值为一个新的tcp的控制块,如果监听成功,之前的控制块就会释放掉
      */
      tcp_echoserver_pcb = tcp_listen(tcp_echoserver_pcb);
      
      /* 
      	等待客户端连接
      	这里用回调的形式实现的,就是tcp_echoserver_accept
      */
      tcp_accept(tcp_echoserver_pcb, tcp_echoserver_accept);
    }
    else 
    {
      /* 
      	如果绑定失败,释放掉tcp控制块
      */
      memp_free(MEMP_TCP_PCB, tcp_echoserver_pcb);
    }
  }
}

tcp_echoserver_accept

/**
  * @brief  This function is the implementation of tcp_accept LwIP callback
  * @param  arg: not used
  * @param  newpcb: 指向客户端的tcp的控制块
  * @param  err: not used 
  * @retval err_t: error status
  */
static err_t tcp_echoserver_accept(void *arg, struct tcp_pcb *newpcb, err_t err)
{
  err_t ret_err;
  //这是整个echoserver的控制块(结构体)
  struct tcp_echoserver_struct *es;

  LWIP_UNUSED_ARG(arg);
  LWIP_UNUSED_ARG(err);

  /* 设置客户端的tcp的优先级为最低 */
  tcp_setprio(newpcb, TCP_PRIO_MIN);

  /* 动态分配的 tcp_echoserver_struct */
  es = (struct tcp_echoserver_struct *)mem_malloc(sizeof(struct tcp_echoserver_struct));
  //只有涉及到动态分配,都需要判断返回地址是否正确
    if (es != NULL)
  {
    //当前的server的状态为客户接入完成
    es->state = ES_ACCEPTED;
    //pcb指向客户的tcp控制块
    es->pcb = newpcb;
    //重发次数,赋值为0
    es->retries = 0;
    //数据接收/发送的缓冲区指向空
    es->p = NULL;
    
    /* 
    	每一pcb控制块里面成员很多,有一个成员就是专门存放跟当前tcp相关的参数
    	把我们整个程序用的tcp_echoserver_struct,当做参数传入到我们的pcb控制块中
    */
    tcp_arg(newpcb, es);
    
    /* 
    	初始化 接收的回调函数 tcp_echoserver_recv
    */ 
    tcp_recv(newpcb, tcp_echoserver_recv);
    
    /* 初始化错误产生后的回调函数  */
    tcp_err(newpcb, tcp_echoserver_error);
    
    /*初始化 轮询的回调函数,但是 intercal值为0,就是不启用轮询功能*/
    tcp_poll(newpcb, tcp_echoserver_poll, 0);
    
    ret_err = ERR_OK;
  }//创建tcp_echoserver_struct 失败
  else
  {
    /*  关闭tcp连接 */
    tcp_echoserver_connection_close(newpcb, es);
    /* 返回内存分配失败 */
    ret_err = ERR_MEM;
  }
  return ret_err;  
}

tcp_echoserver_recv

/**
  * @brief  This function is the implementation for tcp_recv LwIP callback
  * @param  arg: 这个就是tcp_echoserver_struct
  * @param  tpcb: 当前使用tcp控制块
  * @param  pbuf: 指向了接收到数据的缓冲区,这个pbuf是lwip内部接收到数据后,自动分配的
  * @param  err: 错误信息
  * @retval err_t: error code
  */
static err_t tcp_echoserver_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
  struct tcp_echoserver_struct *es;
  err_t ret_err;

  LWIP_ASSERT("arg != NULL",arg != NULL);
  //获取到当前tcp_echoserver_struct
  es = (struct tcp_echoserver_struct *)arg;
  
  /* 接收到的数据为空 */
  if (p == NULL)
  {
    /* 改变状态为关闭*/
    es->state = ES_CLOSING;
    //判断我们是否还需要发送数据
    if(es->p == NULL)
    {
       /*不许发送,直接关闭tcp连接*/
       tcp_echoserver_connection_close(tpcb, es);
    }
    else
    {
      /*创建发送数据完成后的回调函数*/
      tcp_sent(tpcb, tcp_echoserver_sent);
      
      /* 触发发送,真正的发送到网卡上*/
      tcp_echoserver_send(tpcb, es);
    }
    ret_err = ERR_OK;
  }   
  /* 收到数据了,但是产生错误了 */
  else if(err != ERR_OK)
  {
    /* 释放接收的缓冲区*/
    if (p != NULL)
    {
      es->p = NULL;
      pbuf_free(p);
    }
    ret_err = err;
  }
  else if(es->state == ES_ACCEPTED)
  {
    /* 改变状态为 接收 */
    es->state = ES_RECEIVED;
    
    /* 把接收的pbuf的地址,存取到我们的本地结构体里 */
    es->p = p;
    
    /* 进行应答------ 我们功能是回显,回响 */
    tcp_sent(tpcb, tcp_echoserver_sent);
    
    /* 直接发送数据到网卡 */
    tcp_echoserver_send(tpcb, es);
    
    ret_err = ERR_OK;
  }
  else if (es->state == ES_RECEIVED)
  {
    /* 判断 我们的发送缓冲区里面,是否有未发的数据,没有*/
    if(es->p == NULL)
    {
      //把pbuf进行存储
      es->p = p;
  
      /* 进行发送 */
      tcp_echoserver_send(tpcb, es);
    }//有数据需要发送
    else
    {
      struct pbuf *ptr;

      /* 
      	由于之前数据没有发送完成,需要进行数据连接
      */
      ptr = es->p;//把之前待发送的地址获取到
      pbuf_chain(ptr,p);//进行首尾连接
    }
    ret_err = ERR_OK;
  }//处于关闭状态
  else if(es->state == ES_CLOSING)
  {
    /* 
    	释放pbuf
    */
    tcp_recved(tpcb, p->tot_len);
    es->p = NULL;
    pbuf_free(p);
    ret_err = ERR_OK;
  }
  else
  {
    /* 释放pbuf  */
    tcp_recved(tpcb, p->tot_len);
    es->p = NULL;
    pbuf_free(p);
    ret_err = ERR_OK;
  }
  return ret_err;
}

tcp_echoserver_error

/**
  * @brief  当产生错误时,lwip调用此函数 
  * @param  arg: 指向我们 tcp_echoserver_struct 
  * @param  err: not used
  * @retval None
  */
static void tcp_echoserver_error(void *arg, err_t err)
{
  struct tcp_echoserver_struct *es;

  LWIP_UNUSED_ARG(err);

  es = (struct tcp_echoserver_struct *)arg;
  if (es != NULL)
  {
    /* 释放 tcp_echoserver_struct */
    mem_free(es);
  }
}

tcp_echoserver_poll

/**
  * @brief  This function implements the tcp_poll LwIP callback function
  * @param  arg: pointer on argument passed to callback
  * @param  tpcb: pointer on the tcp_pcb for the current tcp connection
  * @retval err_t: error code
  */
static err_t tcp_echoserver_poll(void *arg, struct tcp_pcb *tpcb)
{
  err_t ret_err;
  struct tcp_echoserver_struct *es;

  es = (struct tcp_echoserver_struct *)arg;
  if (es != NULL)
  {
    //有数据需要发送
    if (es->p != NULL)
    {
     
      tcp_sent(tpcb, tcp_echoserver_sent);
      /* there is a remaining pbuf (chain) , try to send data */
      tcp_echoserver_send(tpcb, es);
    }
    else
    {
      /* 判断状态是否为关闭 */
      if(es->state == ES_CLOSING)
      {
        /*  关闭tcp连接 */
        tcp_echoserver_connection_close(tpcb, es);
      }
    }
    ret_err = ERR_OK;
  }	//没有创建tcp_echoserver_struct
  else
  {
    /* 终止 tcp任务 */
    tcp_abort(tpcb);
    ret_err = ERR_ABRT;
  }
  return ret_err;
}

tcp_echoserver_sent

/**
  * @brief  当发送完成以后,会调用这个回调函数
  * @param  None
  * @retval None
  */
static err_t tcp_echoserver_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
{
  struct tcp_echoserver_struct *es;

  LWIP_UNUSED_ARG(len);

  es = (struct tcp_echoserver_struct *)arg;
  es->retries = 0;
  
  if(es->p != NULL)
  {
    /* 还有数据需要继续发送 */
    tcp_sent(tpcb, tcp_echoserver_sent);
    tcp_echoserver_send(tpcb, es);
  }
  else
  {
    /* 需要关闭,我们才关闭*/
    if(es->state == ES_CLOSING)
      tcp_echoserver_connection_close(tpcb, es);
  }
  return ERR_OK;
}

tcp_echoserver_send

/**
  * @brief  把数据发送到网卡
  * @param  tpcb: pointer on the tcp_pcb connection
  * @param  es: pointer on echo_state structure
  * @retval None
  */
static void tcp_echoserver_send(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
{
  struct pbuf *ptr;
  err_t wr_err = ERR_OK;
 //做个三个判断
    /*
    	1、tcp任务没有出错
    	2、有待发送的数据
    	3、将要发送的数据,长度小于 tcp发送长度
    	3.1、 tcp发送buf,有限制的,不是无穷大的,我们要发送长度,不能大于它,如果大于它就会出错
    */
  while ((wr_err == ERR_OK) &&
         (es->p != NULL) && 
         (es->p->len <= tcp_sndbuf(tpcb)))
  {
    
    /* 获取到待发送的pbuf的指针*/
    ptr = es->p;

    /* 调用write进行发送,发送到网卡上 */
    wr_err = tcp_write(tpcb, ptr->payload, ptr->len, 1);
    
    if (wr_err == ERR_OK)
    {
      u16_t plen;
      u8_t freed;
	  //获取剩余长度
      plen = ptr->len;
     
      /* 如果这个pbuf链表还有其他的,再此发送 */
      es->p = ptr->next;
      
      if(es->p != NULL)
      {
        /* 刷新计数值 */
        pbuf_ref(es->p);
      }
      
     /*  */
      do
      {
        /* 释放pbuf */
        freed = pbuf_free(ptr);
      }
      while(freed == 0);
     /* 进行接收 */
     tcp_recved(tpcb, plen);
   }
   else if(wr_err == ERR_MEM)
   {
      /* 进行重发 */
     es->p = ptr;
   }
   else
   {
     /* other problem ?? */
   }
  }
}

tcp_echoserver_connection_close

/**
  * @brief  This functions closes the tcp connection
  * @param  tcp_pcb: pointer on the tcp connection
  * @param  es: pointer on echo_state structure
  * @retval None
  */
static void tcp_echoserver_connection_close(struct tcp_pcb *tpcb, struct tcp_echoserver_struct *es)
{
  
  /* 移植所有的回调函数 */
  tcp_arg(tpcb, NULL);
  tcp_sent(tpcb, NULL);
  tcp_recv(tpcb, NULL);
  tcp_err(tpcb, NULL);
  tcp_poll(tpcb, NULL, 0);
  
  /* 释放  tcp_echoserver_struct 内存空间*/
  if (es != NULL)
  {
    mem_free(es);
  }  
  
  /* 关闭tcp连接,这里会释放pcb控制块的内存 */
  tcp_close(tpcb);
}

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值