使用select()表示非阻塞套接字

我正在尝试使用select函数在服务器和1个客户端(不再)之间进行非阻塞i / o,其中通信流畅(可以随时发送而另一个将在不等待发送的情况下接收)。 我找到了一些包含一些代码的教程,并尝试将其改编为我的。 这就是我所拥有的 –

服务器

#define PORT "4950" #define STDIN 0 struct sockaddr name; void set_nonblock(int socket) { int flags; flags = fcntl(socket,F_GETFL,0); assert(flags != -1); fcntl(socket, F_SETFL, flags | O_NONBLOCK); } // get sockaddr, IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) return &(((struct sockaddr_in*)sa)->sin_addr); return &(((struct sockaddr_in6*)sa)->sin6_addr); } int main(int agrc, char** argv) { int status, sock, adrlen, new_sd; struct addrinfo hints; struct addrinfo *servinfo; //will point to the results //store the connecting address and size struct sockaddr_storage their_addr; socklen_t their_addr_size; fd_set read_flags,write_flags; // the flag sets to be used struct timeval waitd; // the max wait time for an event int sel; // holds return value for select(); //socket infoS memset(&hints, 0, sizeof hints); //make sure the struct is empty hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; //tcp hints.ai_flags = AI_PASSIVE; //use local-host address //get server info, put into servinfo if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status)); exit(1); } //make socket sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol); if (sock ai_addr, servinfo->ai_addrlen) < 0) { printf("\nBind error %m", errno); exit(1); } freeaddrinfo(servinfo); //listen if(listen(sock, 5) < 0) { printf("\nListen error %m", errno); exit(1); } their_addr_size = sizeof(their_addr); //accept new_sd = accept(sock, (struct sockaddr*)&their_addr, &their_addr_size); if( new_sd < 0) { printf("\nAccept error %m", errno); exit(1); } set_nonblock(new_sd); cout<<"\nSuccessful Connection!"; char* in = new char[255]; char* out = new char[255]; int numSent; int numRead; while(1) { waitd.tv_sec = 10; FD_ZERO(&read_flags); FD_ZERO(&write_flags); FD_SET(new_sd, &read_flags); if(strlen(out) != 0) FD_SET(new_sd, &write_flags); sel = select(new_sd+1, &read_flags, &write_flags, (fd_set*)0, &waitd); if(sel < 0) continue; //socket ready for reading if(FD_ISSET(new_sd, &read_flags)) { FD_CLR(new_sd, &read_flags); memset(&in, 0, sizeof(in)); if(recv(new_sd, in, sizeof(in), 0) <= 0) { close(new_sd); break; } else cout<<"\n"<<in; } //end if ready for read //socket ready for writing if(FD_ISSET(new_sd, &write_flags)) { FD_CLR(new_sd, &write_flags); send(new_sd, out, strlen(out), 0); memset(&out, 0, sizeof(out)); } } //end while cout<<"\n\nExiting normally\n"; return 0; } 

客户端(基本上只是减去接受电话) –

 #define PORT "4950" struct sockaddr name; void set_nonblock(int socket) { int flags; flags = fcntl(socket,F_GETFL,0); assert(flags != -1); fcntl(socket, F_SETFL, flags | O_NONBLOCK); } // get sockaddr, IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) return &(((struct sockaddr_in*)sa)->sin_addr); return &(((struct sockaddr_in6*)sa)->sin6_addr); } int main(int agrc, char** argv) { int status, sock, adrlen; struct addrinfo hints; struct addrinfo *servinfo; //will point to the results fd_set read_flags,write_flags; // the flag sets to be used struct timeval waitd; // the max wait time for an event int sel; // holds return value for select(); memset(&hints, 0, sizeof hints); //make sure the struct is empty hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; //tcp hints.ai_flags = AI_PASSIVE; //use local-host address //get server info, put into servinfo if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status)); exit(1); } //make socket sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol); if (sock ai_addr, servinfo->ai_addrlen) < 0) { printf("\nclient connection failure %m", errno); exit(1); } cout<<"\nSuccessful connection!"; set_nonblock(sock); char* out = new char[255]; char* in = new char[255]; int numRead; int numSent; while(1) { waitd.tv_sec = 10; FD_ZERO(&read_flags); FD_ZERO(&write_flags); FD_SET(sock, &read_flags); if(strlen(out) != 0) FD_SET(sock, &write_flags); sel = select(sock+1, &read_flags, &write_flags, (fd_set*)0, &waitd); if(sel < 0) continue; //socket ready for reading if(FD_ISSET(sock, &read_flags)) { FD_CLR(sock, &read_flags); memset(&in, 0, sizeof(in)); if(recv(sock, in, sizeof(in), 0) <= 0) { close(sock); break; } else cout<<"\n"<<in; } //end if ready for read //socket ready for writing if(FD_ISSET(sock, &write_flags)) { FD_CLR(sock, &write_flags); send(sock, out, strlen(out), 0); memset(&out, 0, sizeof(out)); } } //end while cout<<"\n\nExiting normally\n"; return 0; } 

问题是,当我运行它们时,没有任何反应。 我可以输入一个然后按Enter键,而其他屏幕上则不会显示任何内容(反之亦然)。 这不是我调试的全部信息,这是我第一次使用select的真正尝试,所以我想也许我只是没有意识到一些简单的事情。 如果发现任何错误或奇怪的东西请指出,任何帮助表示赞赏。

问题是,当我运行它们时,没有任何反应。

真正的问题是人们多年来一直粘贴Beej的东西而不理解它。 这就是为什么我不喜欢那个指南; 它提供了大量代码,但没有详细解释它们。

你没有读任何东西,也没有发送任何东西; 没有fgets,scanf,cin等。这就是我要做的:

 FD_SET(sock, &read_flags); FD_SET(STDIN_FILENO, &read_flags); /* .. snip .. */ if(FD_ISSET(STDIN_FILENO, &read_flags)) { fgets(out, len, stdin); } 

这将监视stdin并在输入可用时读取它; 然后,当套接字si可写( FD_ISSET(sock, &write_flags) )时,它将发送缓冲区。

我现在正在运行该程序。

服务器 –

 #define PORT "4950" #define STDIN 0 struct sockaddr name; void set_nonblock(int socket) { int flags; flags = fcntl(socket,F_GETFL,0); assert(flags != -1); fcntl(socket, F_SETFL, flags | O_NONBLOCK); } // get sockaddr, IPv4 or IPv6: void *get_in_addr(struct sockaddr *sa) { if (sa->sa_family == AF_INET) return &(((struct sockaddr_in*)sa)->sin_addr); return &(((struct sockaddr_in6*)sa)->sin6_addr); } int main(int agrc, char** argv) { int status, sock, adrlen, new_sd; struct addrinfo hints; struct addrinfo *servinfo; //will point to the results //store the connecting address and size struct sockaddr_storage their_addr; socklen_t their_addr_size; fd_set read_flags,write_flags; // the flag sets to be used struct timeval waitd = {10, 0}; // the max wait time for an event int sel; // holds return value for select(); //socket infoS memset(&hints, 0, sizeof hints); //make sure the struct is empty hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; //tcp hints.ai_flags = AI_PASSIVE; //use local-host address //get server info, put into servinfo if ((status = getaddrinfo("127.0.0.1", PORT, &hints, &servinfo)) != 0) { fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status)); exit(1); } //make socket sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol); if (sock < 0) { printf("\nserver socket failure %m", errno); exit(1); } //allow reuse of port int yes=1; if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) { perror("setsockopt"); exit(1); } //unlink and bind unlink("127.0.0.1"); if(bind (sock, servinfo->ai_addr, servinfo->ai_addrlen) < 0) { printf("\nBind error %m", errno); exit(1); } freeaddrinfo(servinfo); //listen if(listen(sock, 5) < 0) { printf("\nListen error %m", errno); exit(1); } their_addr_size = sizeof(their_addr); //accept new_sd = accept(sock, (struct sockaddr*)&their_addr, &their_addr_size); if( new_sd < 0) { printf("\nAccept error %m", errno); exit(1); } //set non blocking set_nonblock(new_sd); cout<<"\nSuccessful Connection!\n"; char in[255]; char out[255]; memset(&in, 0, 255); memset(&out, 0, 255); int numSent; int numRead; while(1) { FD_ZERO(&read_flags); FD_ZERO(&write_flags); FD_SET(new_sd, &read_flags); FD_SET(new_sd, &write_flags); FD_SET(STDIN_FILENO, &read_flags); FD_SET(STDIN_FILENO, &write_flags); sel = select(new_sd+1, &read_flags, &write_flags, (fd_set*)0, &waitd); //if an error with select if(sel < 0) continue; //socket ready for reading if(FD_ISSET(new_sd, &read_flags)) { //clear set FD_CLR(new_sd, &read_flags); memset(&in, 0, 255); numRead = recv(new_sd, in, 255, 0); if(numRead <= 0) { printf("\nClosing socket"); close(new_sd); break; } else if(in[0] != '\0') cout<<"\nClient: "< 

客户端基本相同......唯一不同的是缺乏倾听和接受。

我只是想补充一点,上面的例子可能无法在Linux上正常工作。 在Linux上,waitd可能会被select修改。 所以对于Linux,waitd应该在select之前重写。

请参阅手册页中的“超时”部分以进行选择。