公司网站英文,做电商,淘宝美工与网站开发,做网站要多少的分辨率网络编程中经常需要处理的一个问题就是如何正确地处理Socket超时,对于C/C,有几种常用的技术可以用来设置Socket接收超时时间,在这篇文章中,我们将详细介绍如何在C/C中设置Socket的非阻塞模式以及如何配置接收超时时间,需要的朋友可以参考下 C/C Socket设置非阻塞模式接收超时时…网络编程中经常需要处理的一个问题就是如何正确地处理Socket超时,对于C/C,有几种常用的技术可以用来设置Socket接收超时时间,在这篇文章中,我们将详细介绍如何在C/C中设置Socket的非阻塞模式以及如何配置接收超时时间,需要的朋友可以参考下 C/C Socket设置非阻塞模式接收超时时间的多种方法 非阻塞模式fcntl 设置非阻塞模式 非阻塞模式下的接收超时 使用select函数 使用select设置接收超时 setsockopt方法设置Socket超时 setsockopt函数概述 使用setsockopt设置接收超时 完整示例代码 小结 总结
C/C Socket设置非阻塞模式接收超时时间的多种方法 网络编程中经常需要处理的一个问题就是如何正确地处理Socket超时。对于C/C有几种常用的技术可以用来设置Socket接收超时时间。在这篇文章中我们将详细介绍如何在C/C中设置Socket的非阻塞模式以及如何配置接收超时时间。 非阻塞模式fcntl 默认情况下Socket操作都是阻塞的。这意味着当调用某个Socket函数时例如recv如果数据还未就绪函数会阻塞等待直到有数据可用为止。然而在许多情况下让函数阻塞并不是最佳解决方案容易造成卡死。这时就需要使用非阻塞模式。 设置非阻塞模式 要将Socket设置为非阻塞模式可以使用fcntl函数。以下是一段示例代码 int flags fcntl(sock_fd, F_GETFL, 0); fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK); 上述代码首先获取了Socket当前的文件状态标志然后将O_NONBLOCK标志位添加到文件状态标志中最后使用F_SETFL命令将新的文件状态标志设置回Socket。此时Socket已经处于非阻塞模式。 非阻塞模式下的接收超时 在非阻塞模式下如果没有数据可用recv函数会立即返回一个错误并设置errno为EWOULDBLOCK或EAGAIN。因此可以通过检查errno来确定是否超时。以下是一段示例代码 #include errno.h #include unistd.h #include stdio.h #include string.h #define MAX_RETRIES 5 #define SLEEP_DURATION 1000000 // One second #define BUFFER_SIZE 1024 int retries 0; char buffer[BUFFER_SIZE]; while(retries MAX_RETRIES) { memset(buffer, 0, sizeof(buffer)); // Clear the buffer ssize_t recv_status recv(sock_fd, buffer, BUFFER_SIZE - 1, 0); if(recv_status 0) { if(errno EWOULDBLOCK || errno EAGAIN) { usleep(SLEEP_DURATION); retries; } else { perror(Error in recv); // Print error message break; } } else if(recv_status 0) { // Socket is closed printf(Socket is closed by the peer\n); break; } else { // Handle received data printf(Received data: %s\n, buffer); break; } } if(retries MAX_RETRIES) { printf(Failed to receive data after %d retries\n, MAX_RETRIES); } 在上述代码中我们在一个循环中不断地尝试接收数据。如果recv返回了错误并且errno被设置为EWOULDBLOCK或EAGAIN我们就让进程睡眠一段时间然后重试。如果尝试了指定的次数还未能成功接收到数据那么我们就认为已经超时。
这种方法的优点是简单直观。但缺点是可能会占用大量的CPU资源因为在超时期间程序会不断地在循环中运行。 使用select函数 另一种处理Socket超时的方法是使用select函数。select函数可以监听一组文件描述符等待它们中的任何一个进入就绪状态例如数据可读或者直到超时。这种方法的优点是可以同时监听多个Socket并且不会占用过多的CPU资源。 使用select设置接收超时 以下是一段使用select设置接收超时的示例代码 #include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #define TIMEOUT_SECONDS 5 #define BUFFER_SIZE 1024 int main() { fd_set set; struct timeval timeout; char buffer[BUFFER_SIZE]; int sock_fd; // TODO: Initialize the socket here. You need to write your own logic to do this. FD_ZERO(set); FD_SET(sock_fd, set); timeout.tv_sec TIMEOUT_SECONDS; timeout.tv_usec 0; int rv select(sock_fd 1, set, NULL, NULL, timeout); if(rv 0) { // Timeout printf(Timeout occurred! No data after %d seconds.\n, TIMEOUT_SECONDS); } else if(rv 0) { // Error occurred perror(Error occurred in select); } else { // Socket ready, can receive data now ssize_t bytes_received recv(sock_fd, buffer, BUFFER_SIZE - 1, 0); // leave space for \0 if(bytes_received 0) { // Error occurred in recv perror(Error occurred in recv); } else { // Null-terminate the received data buffer[bytes_received] \0; printf(Received data: %s\n, buffer); } } // Clean up and close the socket if(close(sock_fd) 0) { perror(Error occurred while closing the socket); } return 0; } 在上述代码中我们首先初始化了一个文件描述符集合和一个时间间隔结构体。然后我们将目标Socket添加到文件描述符集合中并设置了超时时间。最后我们调用select函数并检查其返回值。如果select返回0表示已经超时。如果select返回负数表示发生了错误。如果select返回正数表示有文件描述符已经就绪此时我们就可以调用recv来接收数据了。 setsockopt方法设置Socket超时 除了上述介绍的非阻塞模式和select函数还有一种常用的方法是使用setsockopt函数来直接设置Socket的超时时间。 setsockopt函数概述 setsockopt函数用于设置指定的Socket选项。它的原型如下
1 2 int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 这个函数接收五个参数sockfd是要设置的Socket的文件描述符level指定选项所在的协议层optname是需要设置的选项的名称optval指向包含新选项值的缓冲区optlen是optval缓冲区的大小。 使用setsockopt设置接收超时 在Socket编程中SO_RCVTIMEO和SO_SNDTIMEO选项可以分别用来设置接收和发送超时。这两个选项都位于套接字层所以在调用setsockopt函数时level参数应设为SOL_SOCKET。
以下是一段示例代码展示如何使用setsockopt设置接收超时
struct timeval timeout; timeout.tv_sec TIMEOUT_SECONDS; timeout.tv_usec 0; if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(timeout)) 0) { // Error occurred } 在上述代码中我们首先创建了一个timeval结构体并设置了超时时间。然后我们调用setsockopt函数将SO_RCVTIMEO选项的值设置为指向timeout结构体的指针。如果setsockopt返回负数表示发生了错误。
需要注意的是SO_RCVTIMEO和SO_SNDTIMEO选项设置的超时时间是一个总时间而不是在Socket函数阻塞时每次等待的时间。这意味着如果你在一个循环中多次调用recv函数那么这些函数调用的总时间将不会超过你设置的超时时间。 完整示例代码 下面是一个unix domain socket使用setsockopt函数设置接收超时的示例代码用文件套接字通信其中FILE_PATH是文件路径。
bool nonBlockingRecv() { struct sockaddr_un addr; int sock_fd; char buffer[BUFFER_SIZE] REQ; addr.sun_family AF_UNIX; strcpy(addr.sun_path, FILE_PATH.c_str()); sock_fd socket(AF_UNIX, SOCK_STREAM, 0); if (sock_fd 0) { std::cout Request socket failed\n; return false; } if (connect(sock_fd, (struct sockaddr*)addr, sizeof(addr)) 0) { std::cout Connect socket failed\n; close(sock_fd); return false; } //1.send command SEND_INFO(COMMAND); // Set recv timeout to 100ms struct timeval timeout; timeout.tv_sec 0; timeout.tv_usec 100000; // 100 ms if (setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, timeout, sizeof(timeout)) 0) { std::cout Setting socket timeout failed\n; close(sock_fd); return false; } //2.receive response of register req memset(buffer, 0, BUFFER_SIZE); int recv_status recv(sock_fd, buffer, BUFFER_SIZE, 0); if (recv_status 0) { if (errno EWOULDBLOCK || errno EAGAIN) { std::cout Receive timeout\n; } else { std::cout Receive error\n; } close(sock_fd); return false; } std::cout Received [ buffer ] from manager std::endl; //3.check result if (NULL ! strstr(buffer, SUCCESS.c_str()))//receive success. { std::cout Received success\n; close(sock_fd); return true; } else { std::cout Received fail\n; close(sock_fd); return false; } }
小结 使用setsockopt函数设置SO_RCVTIMEO选项是一种直接且有效的方法来设置Socket接收超时。这种方法的优点是简单直观只需要一行代码就可以完成设置。然而它的缺点是灵活性较差因为它只能设置一个固定的超时时间而不能动态地根据网络状况调整超时时间。 总结 在C/C中有多种方法可以用来设置Socket接收超时时间。非阻塞模式和select函数亦或setsockopt函数都是处理这个问题的有效工具。需要注意的是选择哪种方法取决于具体的应用场景。例如如果你需要同时处理多个Socket那么select函数可能是更好的选择。如果想要方便setsockopt函数可以考虑