Write FD set 的用途
一段時間,我其實都不知道 select 和 epoll 這一類 function 裏面的 writefds 到底有什麼用。read fd set很容易理解,那就是有東西要讀,或者白話一點,收到封包了。那 write fd set 到底要幹嘛呢?直到今天遇到了一個情境我才使用了它 ...
在寫 TCP socket 程式的時候,會使用到 connect 這個函式。一般來說, connect 這個函式是 blocking 的,也就是會一直到事情做完才會返回。但如果將 socket 設定成 non-blocking 模式呢?這時候 connect 會立刻返回並將 error no 設定為 EINPROGRESS。下面是"男人"的解釋:
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).
好了,這時候我們可以看到 select 可以用來判斷 connect 是否完成。範例程式碼如下:
在寫 TCP socket 程式的時候,會使用到 connect 這個函式。一般來說, connect 這個函式是 blocking 的,也就是會一直到事情做完才會返回。但如果將 socket 設定成 non-blocking 模式呢?這時候 connect 會立刻返回並將 error no 設定為 EINPROGRESS。下面是"男人"的解釋:
EINPROGRESS
The socket is nonblocking and the connection cannot be completed immediately. It is possible to select(2) or poll(2) for completion by selecting the socket for writing. After select(2) indicates writability, use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to determine whether connect() completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR is one of the usual error codes listed here, explaining the reason for the failure).
好了,這時候我們可以看到 select 可以用來判斷 connect 是否完成。範例程式碼如下:
#include最後,別問我 exceptfds 是什麼 ...// for printf() and fprintf() #include // for socket(), connect(), send(), and recv() #include // for sockaddr_in and inet_addr() #include // for atoi() and exit() #include // for memset() #include // for close() #include // for fcntl() static int make_socket_non_blocking (int sfd) { int flags, s; flags = fcntl (sfd, F_GETFL, 0); if (flags == -1) { perror ("fcntl"); return -1; } flags |= O_NONBLOCK; s = fcntl (sfd, F_SETFL, flags); if (s == -1) { perror ("fcntl"); return -1; } return 0; } int main(int argc, char *argv[]) { int sock; // Socket descriptor struct sockaddr_in servAddr; // Server address unsigned short servPort; // Server port char *servIP; // Server IP address (dotted quad) struct timeval tv; fd_set write_fds; int ready; if ( ( argc < 2 ) || (argc > 3 ) ) // Test for correct number of arguments { fprintf( stderr, "Usage: %s [ ]\n", argv[0] ); exit(1); } servIP = argv[1]; // First arg: server IP address (dotted quad) if ( argc == 3 ) { // Use given port, if any servPort = atoi(argv[2]); } else { // 7 is the well-known port for the echo service servPort = 50000; } // Create a reliable, stream socket using TCP if ( ( sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP ) ) < 0 ) { perror("socket() failed"); exit(1); } make_socket_non_blocking( sock ); // Construct the server address structure memset(&servAddr, 0, sizeof(servAddr)); // Zero out structure servAddr.sin_family = AF_INET; // Internet address family servAddr.sin_addr.s_addr = inet_addr(servIP); // Server IP address servAddr.sin_port = htons(servPort); // Server port connect( sock, (struct sockaddr *) &servAddr, sizeof( servAddr ) ); while(1) { tv.tv_sec = 0; tv.tv_usec = 1000; FD_ZERO( &write_fds ); FD_SET( sock, &write_fds ); ready = select( sock + 1, NULL, &write_fds, NULL, &tv ); if( FD_ISSET( sock, &write_fds ) ) { printf( "Connection done.\n" ); break; } else { printf( "Timeout.\n" ); } } printf("\n"); // Print a final linefeed close(sock); return 0; }
留言
張貼留言