C ( LINUX ) 中的非阻塞客户端套接字

时间:2021-04-08 15:58:16

标签: c sockets tcp nonblocking

我正在处理一项检查服务器上网络连接可用性的要求(如果网络稳定与否)。 我有一个函数,它可以找到在中央服务器上注册的服务器。我通过提取服务器的 URL 和端口号并尝试通过表现得像一个简单的 TCP 客户端来连接到它们,为此功能添加了一层网络检查。如果返回值大于0,则表示网络工作正常;如果 -1,则网络损坏。

通过这样的检查,当服务器的网络关闭时,我有时会观察到变量不一致的时间。链接中也说明了这种行为的原因:Inconsistent time

为了解决这个问题,我有一个非阻塞客户端套接字的想法,它尝试连接到服务器。使用这种方法,我观察到的是,每次客户端连接到服务器时,connect() 函数的返回值始终为 -1。

我计划监控 connect() 函数最多 5 秒,如果失败,它应该发送否定响应。

我想实现的目标:如果服务器可用,连接=0,然后断开它,否则等待2秒,如果仍然没有响应,则得出超时结论。

printf("--Checking for network connectivity--\n");
        for(size_t i = 0; i < serverOnNetworkSize; i++) {
           UA_ServerOnNetwork *server = &serverOnNetwork[i];
           A[i] = (char *)UA_malloc(server->discoveryUrl.length+1);
           memcpy(A[i],server->discoveryUrl.data,server->discoveryUrl.length);
           A[i][server->discoveryUrl.length] = 0;
           int length = strlen(A[i]);
          
          //discovery URLs are of the form : opc.tcp://hostname:port
          
          //new addition to extract port
            B[i] = A[i] + 10;
          //printf("Hostname: %s\n", B[i]);
            char *p = strrchr(B[i], ':');
            int port = strtoul(p+1, NULL, 10);
          //printf("%d\n",port);
            B[i][length-5]='\0';
          //printf("Hostname: %s\n", B[i]);
         

         
           //removing the port
           A[i][length-5]='\0';
           //without initial tcp binding
           C[i] = A[i] + 10;
          //printf("Hostname: %s\n", C[i]);

          // FIND IP OF THAT HOST
           if(i!=0){
            char ip_address[50];

            find_ip_address(C[i],ip_address);  
            socketCommunication(ip_address,C[i],port);
       }
}
          printf("--Checks done!--\n");

全局函数:

int find_ip_address(char *hostname, char *ip_address)
{
      struct hostent *host_name;
      struct in_addr **ipaddress;
      int count;
      if((host_name = gethostbyname(hostname)) == NULL)
      {
            herror("\nIP Address Not Found\n");
            return 1;
      }
      else
      {
            ipaddress = (struct in_addr **) host_name->h_addr_list;
            for(count = 0; ipaddress[count] != NULL; count++)
            {
                  strcpy(ip_address, inet_ntoa(*ipaddress[count]));
                  return 0;
            }
      }
      return 1;
}
int ret;
void socketCommunication(char *ip_address,char *hostname, int port){
     int clientSocket,ret;
    struct  sockaddr_in serverAddr;
    char buffer[1024];
 
    
    clientSocket = socket(AF_INET,SOCK_STREAM | SOCK_NONBLOCK,0);
    if(clientSocket<0){
        printf("Error in connection \n");
        exit(1);
    }

    //printf("Client socket is created\n");

    memset(&serverAddr,'\0',sizeof(serverAddr));

    serverAddr.sin_port = htons(port);
    serverAddr.sin_family=AF_INET;
    
    serverAddr.sin_addr.s_addr=inet_addr(ip_address);
    //monitoring for 5 seconds
    for(int i =0; i<=5;i++){
        
        ret = connect(clientSocket,(struct sockaddr*)&serverAddr,sizeof(serverAddr));
        //printf("%d ", ret);
    
        sleep(1);
    }
    if(ret<0){
        printf("\nLOOKS LIKE NETWORK CONNECTION HAS FAILED. HAVE A LOOK AT    THE NETWORK CONNECTIVITY at host : %s\n",hostname);
        printf("\n----Updated Status Information----:\n");
        printf("Discovery URL : opc.tcp://%s:%d\n",hostname,port);
        printf("Status:CONNECTON TIMED OUT\n");
        printf("\n");
   }

输出: 尽管服务器处于活动状态,但它显示响应失败

LOOKS LIKE NETWORK CONNECTION HAS FAILED. HAVE A LOOK AT THE NETWORK CONNECTIVITY at host : o755-gksr

----Updated Status Information----:
Discovery URL : opc.tcp://o755-gksr:4840
Status:CONNECTON TIMED OUT

--Checks done!--
Time measured: 6 seconds.

从技术上讲,是否有可能有一个非阻塞客户端,只是有一个连接()函数到一个阻塞服务器?服务器始终在侦听来自客户端的连接。

如果可以实现,这里有关于这种方法的任何指导吗?

问候, 洛山

1 个答案:

答案 0 :(得分:5)

您不应该重复 connect 操作。只需发出 connect 一次。如果 EINPROGRESS 失败,您只需要等待 connect 成功。您不需要发出另一个 connect

您可以通过多种方式检查连接是否成功。最简单的方法可能就是使用 getpeername。如果您有对等方,则套接字已连接。您还可以使用 selectpoll 检查套接字是否可写。

如果连接失败,你想知道错误原因,只要尝试 read 一个字节。它会失败并给你错误代码。您也可以使用 getsockopt (fd, SOL_SOCKET, SO_ERROR, ...)

请参阅 this famous post 了解更多信息。