seewolf 发表于 2013-11-1 12:32:58

简析UIP协议栈作为服务器/客户端实现TCP/UDP通信的方法

有阵子没发新帖了,这阵子搞了搞UIP在STM32上的移植,总结了一点经验,在这里分享非大家~协议栈的移植方法在这里不多说,因为这类的开发板例程很多,我在这里主要说一下用UIP作为服务器或者客户端实现TCP/UDP通信的方法,相信还是有一些人对此存在一些疑问的,希望能够帮到大家,呵呵~

1、TCP服务器方式;
这种方式的例程也是最多的,在这里简单说下步骤好了:
初始化网络:InitNet()-->创建监听端口:uip_listen(HTONS(1000))-->
设置回调函数:void tcp_comm_appcall(void)
{
    if (uip_aborted())
        {
                printf("uip_aborted!\r\n");
                aborted();
        }

        if (uip_timedout())
        {
                printf("uip_timedout!\r\n");
                timedout();
        }

        if (uip_closed())
        {
                printf("uip_closed!\r\n");
                closed();
        }

        if (uip_connected())
        {
                printf("uip_connected!\r\n");
                connected();
        }

        if (uip_acked())
        {
                acked();
        }

        /* 接收到一个新的TCP数据包,准备需要发送数据 */
        if (uip_newdata())
        {
                newdata();
        }

      if (uip_poll())
      {
                poll_event();
      }      

        /* 当需要重发、新数据到达、数据包送达、连接建立时,通知uip发送数据 */
        if (uip_rexmit() ||        uip_newdata() || uip_acked() ||        uip_connected() || uip_poll())
        {
                senddata();
        }
}-->然后等着被连接就行了

2、TCP客户端方式;
TCP的客户端和服务器的程序配置基本一样,首先会增加一个建立连接的函数,在UIP栈初始化之后即可调用,发起连接,函数如下:/**********************************************************************************************************
*        函 数 名: void tcp_comm_init(void)
*        功能说明: 设备主动发起一个TCP链接
*        形    参:无
*        返 回 值: 无
**********************************************************************************************************/
void tcp_comm_connect(void)
{
    uip_ipaddr_t ipaddr;

    uip_ipaddr(&ipaddr, 192,168,1,103);
    uip_connect(&ipaddr, HTONS(1001));
}然后在回调函数“void tcp_comm_appcall(void)”中加入uip_poll的轮询,如下:if (uip_poll())
{
    poll_event();
}
static void poll_event(void)
{
    struct tcp_comm_appstate *s = (struct tcp_comm_appstate *)&uip_conn->appstate;
   
    switch(uip_conn->rport)
    {
      case (HTONS(1001)):
      if(TCP_port1001_sendflag)
      {
            TCP_port1001_sendflag=0;
            s->textptr = "Hello World!";
            s->textlen = strlen((char *)s->textptr);
      }      
      break;
      default:
      break;
    }
}在函数“static void poll_event(void)”,借助UIP栈的轮询机制,当轮询到我们想要发送数据的端口1001时,并且数据发送标志位“TCP_port1001_sendflag”已经被置位,就可以实现向服务器主动发送数据。

3、UDP服务器方式
其实UIP的协议栈比较适合TCP通信,UDP通信做的并不好,比如在作为服务器的时候我们并不知道谁会连接我们,但是协议栈在第一次收到UDP数据包后会对包进行验证,结果必然验证不通过,包被丢弃。所以用UDP服务器方式,要先修改一下协议栈,修改部分就在uip.c中,大概1100行左右,代码如下:#if UIP_UDP
/* UDP input processing. */
udp_input:
/* UDP processing is really just a hack. We don't do anything to the
   UDP/IP headers, but let the UDP application do all the hard
   work. If the application sets uip_slen, it has a packet to
   send. */
#if UIP_UDP_CHECKSUMS
uip_len = uip_len - UIP_IPUDPH_LEN;
uip_appdata = &uip_buf;
if(UDPBUF->udpchksum != 0 && uip_udpchksum() != 0xffff) {
    UIP_STAT(++uip_stat.udp.drop);
    UIP_STAT(++uip_stat.udp.chkerr);
    UIP_LOG("udp: bad checksum.");
    goto drop;
}
#else /* UIP_UDP_CHECKSUMS */
uip_len = uip_len - UIP_IPUDPH_LEN;
#endif /* UIP_UDP_CHECKSUMS */
    /*******************************添加代码起始***********************************/
    if(uip_udp_conn !=0&&(uip_udp_conn->rport!=UDPBUF->srcport
                ||uip_udp_conn->ripaddr!=UDPBUF->srcipaddr))                                                //如果是已经连接并且和接收到的端口号或者IP地址不一致        
        {
      uip_udp_remove(uip_udp_conn);                                                                                        //删除连接
          uip_udp_conn->rport=UDPBUF->srcport;                                                        //将目的端口设置为收到的远端UDP包的端口
                memcpy(uip_udp_conn->ripaddr,UDPBUF->srcipaddr,sizeof(uip_ipaddr_t ));       //将目的IP地址设置为收到的远端UDP包的源IP地址
    }
    if(uip_udp_conn->rport==0)                                                                                                   //如果首次接收到某个远端UDP包
        {
            uip_udp_conn->rport=UDPBUF->srcport;                                                //将目的端口设置为收到的远端UDP包的端口
                memcpy(uip_udp_conn->ripaddr,UDPBUF->srcipaddr,sizeof(uip_ipaddr_t ));       //将目的IP地址设置为收到的远端UDP包的源IP地址
        }
   
    if(uip_udp_conn != 0)
    {
      uip_udp_bind(uip_udp_conn, HTONS(1999));                                                        //绑定本地端口为LPORT,也就是LPORT-->RPORT 发数据
    }
    /*******************************添加代码截止***********************************/
/* Demultiplex this UDP packet between the UDP "connections". */
然后其余的初始化流程和TCP的方式一样,只是要添加属于UDP的回调函数,函数中内容如下即可,其余的这里不再多说。if (uip_poll())
    {
      if(UDP_port7000_sendflag)
      {
            UDP_port7000_sendflag = 0;
            myudp_send("Hello World!\n",13);
      }
    }
    else if (uip_newdata())//接收到一个新的UDP数据包,准备需要发送数据
    {
      UDP_newdata();
    }4、UDP客户端方式
这种方式的初始化依旧和之前说的流程大致一样,只是要添加一个建立UDP连接的函数,函数内容如下:struct uip_udp_conn *myudp_conn;

void myudp_init(void)
{
    //客户端
    uip_ipaddr_t ipaddr;//定义IP类型变量
    uip_ipaddr(ipaddr, 192,168,1,103);   //远程IP为192.168.1.103
    if(myudp_conn != NULL)
    {
      uip_udp_remove(myudp_conn);//如果连接已经建立,则删除之
    }
   
    myudp_conn = uip_udp_new(&ipaddr, HTONS(7000));//建立到远程ipaddr,端口为7000的连接
    if(myudp_conn != NULL)
    {
      uip_udp_bind(myudp_conn, HTONS(LPORT));//绑定本地端口为1999,也就是1999-->7000 发数据
    }
}该函数在初始化完成后即可调用,然后相应的回调函数如下,它主动发送数据的方式和TCP是一样,借助UIP栈的轮询机制,不再多说。/*******************************************************************************
*        函数名: void udp_comm_appcall(void) )
*        参数:
*        返回: 无
*        功能: UDP主函数
**************************************************************************/
void udp_comm_appcall(void)
{
    //客户端模式
    if(myudp_conn->rport == (HTONS(7000)))
    {
      if (uip_poll())
      {
            if(UDP_port7000_sendflag)
            {
                UDP_port7000_sendflag = 0;
                myudp_send("Hello World!\n",13);
            }
      }
      else if (uip_newdata())
      {
            UDP_newdata();
      }   
    }
}

hygs 发表于 2013-11-1 12:37:53

好帖!有含金量。谢谢楼主分享

NEWT 发表于 2013-11-1 14:43:16

好贴,谢谢LZ分享

chengying 发表于 2013-11-28 19:00:37

谢谢,刚好用得着

momolf0727 发表于 2013-11-28 19:45:44

请教下大神,用超声波的设备端和手机间通信,需要如何设计一套自有的通信协议,?

zhiyuanzhitian 发表于 2016-11-17 16:21:05

mark...uip tcp 服务器端

ycwjl728 发表于 2017-1-2 10:45:37

楼主的帖子,只有遇到问题才能体现出价值!
非常感谢!解决了我的问题。

as413526602 发表于 2018-1-4 10:25:52

{:smile:}{:smile:}谢谢分享!!

TGS 发表于 2021-8-18 15:51:38

谢谢分享
页: [1]
查看完整版本: 简析UIP协议栈作为服务器/客户端实现TCP/UDP通信的方法