Baldwin 发表于 2013-1-8 10:35:52

请教RT-Thread中LWIP Socket编程?

老规矩,自报家门
STM32F207IGT6   MDK   RTT 1.1.0 LWIP
声明:对于文中引用的文字,如果有侵犯到您权益,请您通知我,我会第一时间删除,谢谢!

LwIP为我们提供了两种API函数来实现TCP/IP协议栈,它们分别是:
1、low-level "core" / "callback" or "raw" API. ——低水平的、基于回调函数的API(后面直接称RAW API)
2、higher-level "sequential" API.——高水平的、连续的API(后面直接称sequential API)

问题1:请问在RT-Thread使用的是哪种API 呢?

我们新手通常会使用connect、 accept、recv或recvfrom这样的阻塞程序,但是使用阻塞程序后会降低程序的效率,有的前辈提出使用selet()这个非阻塞程序来提高程序的效率。

先提一下select的函数原型
int select( int maxfdp,
            fd_set *readfds,
             fd_set *writefds,
             fd_set *errorfds,
            struct timeval *timeout);

maxfdp:所有文件描述符的最大值加1
*readfds:要监视这些文件描述符的读变化
*writefds:要监视这些文件描述符的写变化
*timeout:select的超时时间

返回值:
负值:select错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件

部分程序:
    int listenfd;
    struct sockaddr_in saddr;
    fd_set readset;

    listenfd = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if( listenfd == -1 )
    {
      rt_kprintf("TCP setup can not create socket!\n");
      return;
    }

    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(SETUP_TCP_PORT);

    if( lwip_bind(listenfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1 )
    {
      lwip_close(listenfd);
      rt_kprintf("TCP setup thread socket bind failed!\n");
      return;
    }

    /* Put socket into listening mode */
    if (lwip_listen(listenfd,2) == -1)
    {
      lwip_close(listenfd);
      rt_kprintf("Listen failed.\n");
      return;
    }

    /* Wait for data or a new connection */
    while(1)
    {
      /* Determine what sockets need to be in readset */
      FD_ZERO(&readset);
      FD_SET(listenfd, &readset);
      // wait forever.
      if( lwip_select(listenfd+1, &readset, 0, 0, 0) == 0 )
            continue;
      if (FD_ISSET(listenfd, &readset))
      {
                        ……………..
                        数据接收处理
       }
}
这段代码是RTT开源产品—“以太网转串口”中tcp_setup_thread的,首先感谢Mbbill前辈的无私奉献

将红色这段代码直接改成
if( lwip_select(listenfd+1, &readset, 0, 0, 0) == 0 )
{
   continue;
}
else if(lwip_select(listenfd+1, &readset, 0, 0, 0) > 0)
{
……………..
        数据接收处理
}
else
{
        break;
}
问题2:程序修改后可以运行,但是请问这样修改会不会有不妥的地方呢?

RTT开源产品—“以太网转串口”中的tcp_setup_thread程序疑问

先提一下recv的函数原型
int recvfrom(int sockfd,
               void *buf,
               int len,
               unsigned int flags,
                struct sockaddr *from,
                socket_t *fromlen);

recv() 和 recvfrom()功能相同

参数:
  sockfd:标识一个已连接套接口的描述字。 
  buf:接收数据缓冲区。 
  len:缓冲区长度。 
      flags:调用操作方式。
  from:(可选)指针,指向装有源地址的缓冲区。 
      fromlen:(可选)指针,指向from缓冲区长度值。
返回值:
   正确接收返回接收到的字节数,失败返回0.

#define P2X_HEADER_LEN          (14)

/*********************** 代码段1 ****************************/
……..已成功建立连接
while(1)
{
        int dataLen,bytesRet;
        ret = blocking_lwip_recv(socket,
                                          setup_data_buf,
                                          P2X_HEADER_LEN,
                                          500);
        if( ret == 0 )
                continue;
        if( ret != P2X_HEADER_LEN )
                break;
        dataLen = P2X_GET_LENGTH(setup_data_buf);
        if( dataLen > 0 )
        {
                int gotDataLen=blocking_lwip_recv(socket,
                                                                setup_data_buf+P2X_HEADER_LEN,
                                                                dataLen,
                                                                500);
                if( gotDataLen != dataLen )
                        break;
        }
        bytesRet = processCMD(setup_data_buf,P2X_HEADER_LEN+dataLen);
        if( lwip_send(socket,setup_data_buf,bytesRet,0) < 0 )
                break;
}

/*********************** 代码段2****************************/
int blocking_lwip_recv(int s, void *mem, size_t len, int timeout)
{
    int readlen, offset = 0;
    fd_set fds;
    int ret;
    struct timeval to;

    to.tv_sec = timeout/1000;
    to.tv_usec = (timeout%1000)*1000;

    while(1)
    {
      FD_ZERO(&fds);
      FD_SET(s,&fds);
      ret = lwip_select(s+1,&fds,0,0,&to);
      if( ret == 0 )
            break;
      else if( ret < 0 )
            return ret;
      readlen = lwip_recvfrom(s, ((char*)mem)+offset, len-offset, 0, NULL, NULL);
      if( readlen == 0 )
            return -1;
      else if( readlen < 0 )
            return readlen;
      if( readlen == (len-offset) )
      {
            offset = len;
            break;
      }
      offset += readlen;
    }
    return offset;
}

代码段1调用代码段2中 blocking_lwip_recv()函数,并且给了超时时间,在blocking_lwip_recv()中也调用了select函数,同时增加了500ms的超时判断。那么就有2种情况:
1、        没有数据,超时溢出,跳出进行下一次等待(我能理解)
2、        接收到数据,也有readlen长度的数据

问题3:对于代码段1中
                         if( ret != P2X_HEADER_LEN )
                            break;
为什么要进行这个判断?我自己的理解是它在进行通信时自己添加了一个头“P2X_HEADER”,但是我手上没这个板子,没法验证。

问题4:select()函数的超时判断的时间是依据什么来设置的呢?

问题5:关于代码段2中的超时时间,网上有篇博客提到了超时时间是否每次都需要初始化,小弟对linux内核不懂,请问在lwip中是否也是一样呢?
http://xingyunbaijunwei.blog.163.com/blog/static/76538067201221094057503/

参考1:我觉得blocking_lwip_recv()中的timeout参数可以分成timeout_second,timeout_ microsecond,这样或许更清楚一点,呵呵!

aozima 发表于 2013-1-8 13:44:19

可以先买本《Linux网络编程(Linux典藏大系)》或类似的书:
当当链接:http://product.dangdang.com/main/product.aspx?product_id=20750208

Baldwin 发表于 2013-1-8 15:09:39

aozima 发表于 2013-1-8 13:44 static/image/common/back.gif
可以先买本《Linux网络编程(Linux典藏大系)》或类似的书:
当当链接:http://product.dangdang.com/main/ ...

嗯,好的!aozima版主,这个以太网转串口开源产品还有讨论的板块吗?以前那个都关闭了

zhangpisces 发表于 2013-3-21 15:01:21

楼主你好,我也出现了你的情况,,,,TCP链接不连接,UDP都是可以的

Baldwin 发表于 2013-3-21 15:19:43

zhangpisces 发表于 2013-3-21 15:01 static/image/common/back.gif
楼主你好,我也出现了你的情况,,,,TCP链接不连接,UDP都是可以的

什么现象,用抓包看看

zhangpisces 发表于 2013-3-21 15:52:59

Baldwin 发表于 2013-3-21 15:19 static/image/common/back.gif
什么现象,用抓包看看

抓出来好像就是没有应答

Baldwin 发表于 2013-3-21 21:55:48

zhangpisces 发表于 2013-3-21 15:52 static/image/common/back.gif
抓出来好像就是没有应答

握手能成功吗
页: [1]
查看完整版本: 请教RT-Thread中LWIP Socket编程?