IT袋

当前位置:主页 > 经验教程 > 系统教程 >

Linux

Linux poll详解(3)

时间:2024-01-09 15:58:29 来源:IT袋 作者:马勇
导读:Linux,编译上述程序生成  poll_server  并运行,然后使用  nc  命令模拟三个客户端并给  poll_server  发送消息,效果如下: 由于 nc 命令是以 \n 作为结束标志的,

Linux

编译上述程序生成 poll_server 并运行,然后使用 nc 命令模拟三个客户端并给 poll_server 发送消息,效果如下:

Linux

由于 nc 命令是以\n作为结束标志的,所以poll_server收到客户端消息时显示时分两行的。

通过上面的示例代码,我们也能看出poll函数存在的一些缺点:

  • 在调用poll函数时,不管有没有有意义,大量的 fd 的数组被整体在用户态和内核地址空间之间复制;
  • 与 select 函数一样,poll 函数返回后,需要遍历 fd 集合来获取就绪的 fd,这样会使性能下降;
  • 同时连接的大量客户端在一时刻可能只有很少的就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

与 select 函数实现非阻塞的 connect 原理一样,我们可以使用 poll 去实现,即通过 poll 检测 clientfd 在一定时间内是否可写,示例代码如下:

/**
 * Linux 下使用poll实现异步的connect,linux_nonblocking_connect_poll.cpp
 * zhangyl 2019.03.16
 */
#include <sys/types.h> 
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <poll.h>
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#define SERVER_ADDRESS "127.0.0.1"
#define SERVER_PORT     3000
#define SEND_DATA       "helloworld"
int main(int argc, char* argv[])
{
    //1.创建一个socket
    int clientfd = socket(AF_INET, SOCK_STREAM, 0);
    if (clientfd == -1)
    {
        std::cout << "create client socket error." << std::endl;
        return -1;
    }
 
 //将 clientfd 设置成非阻塞模式 
 int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);
 int newSocketFlag = oldSocketFlag | O_NONBLOCK;
 if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)
 {
  close(clientfd);
  std::cout << "set socket to nonblock error." << std::endl;
  return -1;
 }
    //2.连接服务器
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
    serveraddr.sin_port = htons(SERVER_PORT);
 for (;;)
 {
  int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
  if (ret == 0)
  {
   std::cout << "connect to server successfully." << std::endl;
   close(clientfd);
   return 0;
  } 
  else if (ret == -1) 
  {
   if (errno == EINTR)
   {
    //connect 动作被信号中断,重试connect
    std::cout << "connecting interruptted by signal, try again." << std::endl;
    continue;
   } else if (errno == EINPROGRESS)
   {
    //连接正在尝试中
    break;
   } else {
    //真的出错了,
    close(clientfd);
    return -1;
   }
  }
 }
 
 pollfd event;
 event.fd = clientfd;
 event.events = POLLOUT;
 int timeout = 3000;
 if (poll(&event, 1, timeout) != 1)
 {
  close(clientfd);
  std::cout << "[poll] connect to server error." << std::endl;
  return -1;
 }
 
 if (!(event.revents & POLLOUT))
 {
  close(clientfd);
  std::cout << "[POLLOUT] connect to server error." << std::endl;
  return -1;
 }
 
 int err;
    socklen_t len = static_cast<socklen_t>(sizeof err);
    if (::getsockopt(clientfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
        return -1;
        
    if (err == 0)
        std::cout << "connect to server successfully." << std::endl;
    else
     std::cout << "connect to server error." << std::endl;
    
 //5. 关闭socket
 close(clientfd);
    return 0;
}

运行效果与前面的 select 实现非阻塞的 connect 一样,这里就不再给出运行效果图了。

上述就是Linux 和 poll详解的详细讲解,IT袋网小编希望本文能给你带来生活上的帮助!

相关阅读