2009-05-15 LWIP SOCKET编程
 
前几天看了关于LWIP协议栈的实现和FREERTOS的基本原理。今天开始调试LWIPsocket通信,是基于freertos系统的ARM9_STR91X_IAR开发板。
这是个现成的实例,由于lwip已经由freertos移植好了,而调试的目的就是实现在lwip上的socket通信。本来以为很容易的问题,结果还是搞了一天,当然和EWARM工具的难用也还是有很大关系的。
 
首先要注意的一点是sys_thread_newxTaskCreate的区别。我们知道xTaskCreate是用来创建一个task任务的。类似于windows下的process
/*
  Starts a new thread with priority "prio" that will begin its execution in the
  function "thread()". The "arg" argument will be passed as an argument to the
  thread() function. The id of the new thread is returned. Both the id and
  the priority are system dependent.
*/
sys_thread_t sys_thread_new(void (* thread)(void *arg), void *arg, int prio)
{
xTaskHandle CreatedTask;
int result;
 
    result = xTaskCreate(thread, ( signed portCHAR * ) s_sys_arch_state.cTaskName, s_sys_arch_state.nStackDepth, arg, prio, &CreatedTask );
 
    // For each task created, store the task handle (pid) in the timers array.
    // This scheme doesn't allow for threads to be deleted
    timeoutlist[nextthread++].pid = CreatedTask;
 
    if(result == pdPASS)
    {
        ++s_sys_arch_state.nTaskCount;
       
        return CreatedTask;
    }
    else
    {
        return NULL;
    }
}
很显然,sys_thread_new最终对task的创建也是通过xTaskCreate来实现的。但是请注意不同点,前者会把新创建的taskpid放入到一个timeoutlist的链表中。
起初我也没有主要这个小问题,直到用socket系列函数的select的时候,一直过不去,而是abort中断了,具体原因请看select函数实现,其中sys_arch_timeouts的调用就是获取当前tasktimeout,如果没有用sys_thread_new创建的话,这里就没有我用来调用select的当前task,所以就有了以后的abort中断了。
 
下面给一个socket编程的实现
static void vLWIPSendTask( void *pvParameters )
{
    int listenfd;
    int remotefd;
    int len;
    struct sockaddr_in local_addr,remote_addr; 
        fd_set readset;
        fd_set writeset;
        struct timeval timeout;
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
 
 
    //struct lwip_socket* sock;
 
    listenfd = socket(AF_INET,SOCK_STREAM,0);
 
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(80);
    local_addr.sin_len = sizeof(local_addr);
    local_addr.sin_addr.s_addr = INADDR_ANY;
   
    if (bind(listenfd, (struct sockaddr *) &local_addr, sizeof(local_addr)) < 0)
    {
        return ;
    }
 
    if (listen(listenfd, 1) == -1)
    {
        return;
    }
 
    len = sizeof(remote_addr);
        while(1)
        {
      // 这里注意一下,lwip的阻塞不是在listen函数,而是accept
      remotefd = accept(listenfd, (struct sockaddr *)&remote_addr, &len);
          //close(listenfd);
          //listenfd = -1;
      //getpeername(remotefd, (struct sockaddr *)&remote_addr, &len);
          if(remotefd != -1)
          {
            int ret;
            send(remotefd,"start to work!\r\n",16,0);
            for(;;)
            {
              FD_ZERO(&readset);
              FD_ZERO(&writeset);
              FD_SET(remotefd, &readset);
              FD_SET(remotefd, &writeset);
 
              ret = lwip_select(remotefd+1, &readset, &writeset, 0, &timeout);
                    
              if(ret > 0)
              {
                if (FD_ISSET(remotefd, &readset))
                {
                  memset(buf,0,50);
                  if(recv(remotefd,buf,50,0) <= 0)
                  {
                    close(remotefd);
                    remotefd = -1;
                    break;
                  }
                  else
                  {
                    int i = strlen(buf);
                    send(remotefd,buf,i ,0);
                  }
                }
                /*
                else if(FD_ISSET(remotefd, &writeset))
                {
                   send(remotefd,"this is time to send!\r\n",25,0);
                }
                */
              }
              else if(ret < 0)
              {
                close(remotefd);
                remotefd = -1;
                break;
              } 
             
            }
          }
        }
       
        vTaskDelete( NULL );
}
对的,这是作为server端实现的,可以和通用的socket客户端配。经过测试至少可以与windows上的socket程序匹配(接收,发送数据)。