Chinaunix首页 | 论坛 | 博客
  • 博客访问: 517178
  • 博文数量: 91
  • 博客积分: 9223
  • 博客等级: 中将
  • 技术积分: 1777
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-02 17:37
个人简介

!!!!!!!!!!!!

文章分类

全部博文(91)

文章存档

2013年(3)

2012年(4)

2011年(37)

2010年(36)

2009年(9)

2008年(2)

分类: BSD

2009-04-27 09:50:02

【摘要】

本文主要解决conncet超时处理的问题。

【关键词】

Connect(), 超时

一、问题的提出

在移植ftp客户端代码时,发现conncet()没有添加超时处理,后来发现通过setitimer()并不能在超时时使connect()错误返回,后来在网上查到了一些资料,解决了问题。

二、解决思路

Connect()函数的定义如下:

如果connect()连接超时,会返回-1,错误码为ETIMEDOUTconncet是一个阻塞方法, connect默认的超时时间很长,一般是75秒,但测试了一下linux服务器,大约有189秒,这个时间过长,不适合用户使用,但是通过设置定时器的方式,又对connect不起作用。

所以,找到了下面的方法:

1.建立socket
    2.将该socket设置为非阻塞模式
    3.调用connect()
    4.使用select()检查该socket描述符是否可写(注意,是可写)
    5.根据select()返回的结果判断connect()结果
  
 6.将socket设置为阻塞模式(如果不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)

下面是ftp客户端的一部分代码:

SWORD32 ftpHookup(

    char *host,          /* server host name or inet address */

     WORD16 wPort

    )

  {

    SWORD32 ctrlSock;

    SWORD32 inetAddr = 0;

    struct sockaddr_in ctrlAddr;

    BYTE ucCommType = 0;

    SWORD32 iRet = 0;

  

    if ((inetAddr = (SWORD32) inet_addr (host)) == ERROR)

    {

          return (ERROR);

    }

    /* make our control socket */

    ctrlSock = socket (AF_INET, SOCK_STREAM, 0);

    if (ctrlSock < 0)

       {

        return (ERROR);

      }

    ctrlAddr.sin_family      = AF_INET;

    ctrlAddr.sin_addr.s_addr = INADDR_ANY;

    ctrlAddr.sin_port        = htons (0);

    if (bind (ctrlSock, (struct sockaddr *)&ctrlAddr, sizeof (ctrlAddr)) < 0)

    {

        close (ctrlSock);

        return (ERROR);

    }

    /* connect to other side */

    ctrlAddr.sin_addr.s_addr = inetAddr;

    ctrlAddr.sin_port        = htons (wPort);

    //没有ftp服务器的情况下等待时间过长,需要加定时器。

    struct timeval tm;

    tm.tv_sec = 5;

    tm.tv_usec = 0;

    iRet = Connect(ctrlSock, ctrlAddr, tm);//修改的

    if(iRet != SUCCESS)

       {

          //printf("can not connect the ftp server!\n");

          close (ctrlSock);

           return (ERROR);

       }

   ftpReplyGet (ctrlSock, FALSE); /* read startup message from server */

return(ctrlSock);

}

 

 

WORD32 Connect(SWORD32 ctrlSock, struct sockaddr_in ctrlAddr, struct timeval tm)

{

   SWORD32 ret = 0;

  fd_set set;

  SWORD32 error = 0;

  SWORD32 len = sizeof(SWORD32);

  SWORD32 flags =0;

   

    flags = fcntl(ctrlSock, F_GETFL, 0);

    flags |= O_NONBLOCK;

    fcntl(ctrlSock, F_SETFL, flags); //设置非阻塞

   

     if(connect (ctrlSock, (struct sockaddr *)&ctrlAddr, sizeof (ctrlAddr)) == -1)

     {

          FD_ZERO(&set);

          FD_SET(ctrlSock, &set);

          if( select(ctrlSock+1, NULL, &set, NULL, &tm) > 0)

          {

               getsockopt(ctrlSock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);

               if(error == 0) //连接到ftp服务器

                   {

                      ret = 1;

                   }

               else //在规定时间内没有连接到ftp服务器

                   {

                      ret = 0;

                   }

          }

         else //在规定时间内没有连接到ftp服务器

           {

              ret = 0;

           }

    }

    else

       {

          ret = 1;

       }

    

     //取消非阻塞模式

     flags &= ~O_NONBLOCK;

     fcntl(ctrlSock, F_SETFL, flags);

 

     if(!ret)

     {

         close (ctrlSock);

         return FAIL;

     }

     return SUCCESS;

}

select来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏FD_ISSET来检查某个描述符是否在其中。由于这里只有一个套接口描述符,就没有使用FD_ISSET宏来检查调用select之后这个sockfd是否在set里面,其实是需要加上这个判断的。不过用了getsockopt来检查,这样才可以判断出这个套接口是否是真的连接上了,因为我们只是变相的用select来检查它是否连接上了,实际上select检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:

这样,就需要用getsockopt函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,主要是判断是否是条件a

在使用ftp上传、下载文件时,如果ftp服务器端突然终止,这时ftp客户端的recv()函数会收到SIGPIPE信号,默认的情况下会终止客户端进程,通过给recv设置MSG_DONTWAIT标志,可以组织接收该信号。

SWORD32 ftpReplyGet(

    SWORD32 ctrlSock,       /* control socket fd of FTP connection */

    Boolean expecteof      /* TRUE = EOF expected, FALSE = EOF is error */

)

{

    char c;

    SWORD32 codeType;

    SWORD32 code;

    SWORD32 dig;

    SWORD32 continuation;

    SWORD32 origCode;

    Boolean eof;

    SWORD32 iCnt = 0;  

    struct timeval tm;

 

    /* read all lines of a reply:

     *    do

     *        while not eof and not eol

     *            process char

     *    while not eof and not last line of reply

     */

 

    origCode = 0;

    codeType = 0;

    memset(acRcvBuf, 0, 100);  

    do

    {

    /* read all characters of a line */

 

        dig  = 0;

        code = 0;

        continuation = FALSE;

        tm.tv_sec = 5;

        tm.tv_usec = 0;

 

        while (!(eof = (Recv(ctrlSock, &c, 1,tm)==0/*recv (ctrlSock, &c, 1,0) == 0*/)) && (c != '\n'))

        {

            dig++;

            acRcvBuf[iCnt] = c;

            iCnt++;

            if (dig == 1)       /* char 1 is code type */

            {

                codeType = c - '0';

            }

            if (dig <= 3)       /* chars 1-3 are code */

            {

                if (!isdigit(c))

                    code = -1;

                else

                    if (code != -1)

                    code = code * 10 + (c - '0');

            }

 

            if (dig == 4)       /* char 4 is continuation marker */

            continuation = (c == '-');

 

            if ((c != '\r') &&

            ((ftpVerbose || ((codeType == FTP_ERROR) && (dig > 4)))))

            {

                write (STDERR_FILENO, &c, 1);

            }

        }

 

        /* print newline if we've been printing this reply */

 

        if ((ftpVerbose || (codeType == FTP_ERROR)))

          {

            printf("\r\n");

          }

 

        /* save the original reply code */

 

        if (origCode == 0)

            origCode = code;

    }

    /* while not eof and not last line of reply */

    while (!eof && !((dig >= 3) && (code == origCode) && !continuation));

 

    acRcvBuf[iCnt] = '\0';  /* add by gaoyu for WlanFtpDownloadBreak */

    /* return error if unexpected eof encountered */

    if (eof & !expecteof)

      {

          return (ERROR);

      }

    else

    return (origCode / 100);    /* return most signif digit of reply */

}

WORD32 Recv(SWORD32 ctrlSock, void *pch, SWORD32 wLen, struct timeval tm)

{

     fd_set set;

     SWORD32 ret = 0;

     SWORD32 iRes = 0;

     SWORD32 error = 0;

     SWORD32 len = sizeof(SWORD32);

     

     

     if(NULL == pch)

        {

           return 0;

        }

     FD_ZERO(&set);

   FD_SET(ctrlSock, &set);

   if( select(ctrlSock+1, &set, NULL, NULL, &tm) > 0)

    {

        getsockopt(ctrlSock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);

        if(error == 0)

          {

            ret = 1;

            iRes = recv(ctrlSock, pch, wLen, MSG_DONTWAIT); //MSG_DONTWAIT防止由于ftp服务器端关闭而发送给客户端SIGPIPE,而中断lap进程

            if(iRes<=0)

               {

                  ret = 0;

               }

          }

        else

          {

            ret = 0;

          }

     }

    else

        {

           ret = 0;

        }

   return ret;      

}

阅读(2234) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~