SOCKET通信—如何设置成非阻塞模式、该模式下判断connect成功(失败)、判断recv/recvfrom成功(失败)、判断send/sendto成功(失败)

SOCKET
       对于一个socket 是阻塞模式还是非阻塞模式的处理方法::

       方法:fcntl 设置;

                   即先用fcntl的F_GETFL获取flags,

                   用F_SETFL设置flags|O_NONBLOCK;        

                   (注意,取消非阻塞的方式是F_SETFL 设置flags&~O_NONBLOCK)

                   并在recv,send 时,将flag参数设置为MSG_DONTWAIT。

             

                                                               实现

 fcntl 函数可以将一个socket 句柄设置成非阻塞模式:
      flags = fcntl(sockfd, F_GETFL, 0);                       //获取文件的flags值。

      fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);   //设置成非阻塞模式;

      flags  = fcntl(sockfd,F_GETFL,0);

      fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK);    //设置成阻塞模式;

      并在recv,recvfrom和send,sendto数据时,将flag设置为MSG_DONTWAIT

      即:recv, send 函数的最后有一个flag 参数可以设置成MSG_DONTWAIT

      recv(sockfd, buff, buff_size,MSG_DONTWAIT);     //非阻塞模式的消息发送

      send(scokfd, buff, buff_size, MSG_DONTWAIT);   //非阻塞模式的消息接受

 设置之后每次的对于sockfd 的操作都是非阻塞的。

(

        connect   当返回0时,表示立即创建了socket链接,
                        当返回-1时,需要判断errno是否是EINPROGRESS(表示当前进程正在处理),否则失败
                        (

                                     下面会有select或epoll监听fd是否建立链接,

select的例子:

    int ret = ::connect(_socket_fd, add.addr(), add.length());
    if(ret == 0)
    {
            //建立链接成功
    }
    else if(ret < 0 && errno == EINPROGRESS) //errno == EINPROGRESS表示正在建立链接
    {
          // 等待连接完成,errno == EINPROGRESS表示正在建立链接
         fd_set set;
         FD_ZERO(&set);
         FD_SET(_socket_fd,&set);


         time_t = 10;          //(超时时间设置为10秒)
         struct timeval timeo;
         timeo.tv_sec = timeout / 1000; 
         timeo.tv_usec = (timeout % 1000) * 1000;


          int retval = select(_socket_fd + 1, NULL, &set, NULL, &timeo);           //事件监听
          if(retval < 0)   
          {
                 //建立链接错误close(_socket_fd)
          }
          else if(retval == 0) // 超时
          {
                 //超时链接没有建立close(_socket_fd)
          }

          //将检测到_socket_fd读事件或写时间,并不能说明connect成功
          if(FD_ISSET(_socket_fd,&set))
         {
               int error = 0;
               socklen_t len = sizeof(error);
               if(getsockopt(_socket_fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
               {
                       //建立简介失败close(_socket_fd)
               }
               if(error != 0) // 失败
                {
                         //建立链接失败close(_socket_fd)
                }
                else
                {
                         //建立链接成功
                }
          }

    }
 else
 {
      //出现错误 close(_sock_fd)
 }

epoll的例子:

     (//待续)

                                   当epoll或select监听到sockfd上有EPOLL_IN或EPOLL_OUT时,即读写事件时,

                             并不能说明链接已经建立

                                int error = 0;
                                socklen_t ilen = sizeof(error);
                                ret = getsockopt(fd,SOL_SOCKET,SO_ERROR,&error,&ilen);
                                if(ret < 0)
                                {
                                          //说明链接建立失败,close(fd);

                                }
                                else if(error != 0 )
                                {

                                          //说明链接建立失败,close(fd);
                                }

                                else

                                {

                                          //说明链接建立成功。即可以向fd上写数据。

                                }

                        ); 

 

 

 

        recv       当返回值为0时,表示对端已经关闭了这个链接,我们应该自己关闭这个链接,

                       即close(sockfd)。(因为异步操作会用select或epoll做事件触发,所以:)

                                   如果使用select,应该将sockfd清除掉,不再监听。

                                   如果使用epoll,系统会自己将sockfd清除掉,不再进行监听。

                       当返回值大于0 且 小于sizeof(buffer)时,表示数据肯定读完。(如果等于sizeof(buffer),可能有数据还没读,应该继续读,不可能有大于)

                       当返回值小于0,即等于-1时:

                                     如果   errno   为  EAGAINE  或 EWOULDBLOCK                                      

                                     表示暂时无数据可读,可以继续读,或者等待epoll或select的后续通知。

                                     如果   errno   为  EINTR

                                     表示被中断了,可以继续读,或者等待epoll或select后续的通知。

                                     否则,真的是读取数据失败。(此时应该close(sockfd))

                      (

                         发生EAGAINE,EWOULDBLOCK,EINTR错误时,

                         表明socket没有问题,即不用close(sockfd);

                         产生的原因:

                                 EAGAINE    和   EWOULDBLOCK  可能是多进程读同一个sockfd,可能一个进程读

                         到数据,其他进程就读取不到数据,当然单个进程也可能出现这种情况。

                         对于这种错误,不需用close(sockfd)。

                                可以等待select或epoll的下一次触发,继续读。

                      )

        (recvfrom的判断也是一样的)

       send      返回值是实际发送的字符数。

                     因为我们知道要发送的总长度,所以,如果没有发送完,我们可以继续发送。

                     当返回值为 -1   时, 我们需要判断  errno:

                         如果errno为  EAGAINE   或 EWOULDBLOCK ,表示当前缓冲区写满,可以继续写,或者等待epoll或select的后续通知。  

                         如果errno为EINTR  ,表示被终端了,可以继续写,或者等待epoll或select的后续通知。

                       (

                         发生EAGAINE,EWOULDBLOCK,EINTR错误时,

                         表明socket没有问题,即不用close(sockfd);

                         产生的原因:

                                 EAGAINE    和   EWOULDBLOCK  可能是多进程写同一个sockfd,可能一个进程写

                         了数据,其他进程就不能写数据,当然单个进程也可能出现这种情况。

                         对于这种错误,不需用close(sockfd)。

                                可以等待select或epoll的触发。

                      )

                        否则真的出错了,即errno不为EAGAINE或EWOULDBLOCK或EINTR,此时应该close(sockfd)

       (sendto的判断也是一样)

)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值