自从我输入 select 语句以来,我的 UDP 服务器接受任何输入时遇到问题。目的是等待来自 2 个不同套接字(具有不同端口)的数据包。同时,我还希望它能够判断服务器何时要向正在同步侦听的端口之一发送某些内容。在下面的代码中,程序一直运行到select()
语句,此时如果我尝试向服务器(在本地机器上)发送内容,则不接受任何内容,程序挂起,等待。我也尝试注释掉writefds
fd_set及其随附的FD_ISSET
但同样的事情发生了。我试图弄清楚这些东西已经筋疲力尽了,所以可能有很多事情没有意义,但我在目前的状态下尽力了。我道歉。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MYPORT "20444" // the port users will be connecting to
#define MAXBUFLEN 1024 //maximum packet length
#define SERVER_R 142.66.140.13 //Server to the "right" of current
#define RTEX_R_PORT "20445" //Port for routing table exchange
typedef enum {false, true} bool;
/*struct to store packet fields into
seq: sequence number;
type: message type; send get ACK
src: client's unique 10 digit number
dst: destination's unique 10 digit number
payload: the message being transferred, if there is any
*/
struct packet
{
char seq[4];
char type[5];
char src[11];
char dst[11];
char payload[MAXBUFLEN];
};
// 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(void)
{
//rr: reading from server to the "right"
//rw: writing to the server to the "right"
int sockfd, rtex_rr_sockfd, rtex_rw_sockfd, rv, rrr, rrw, numbytes, i, j, first, max_fd;
struct addrinfo hints, *servinfo, *p, *p2, *p3;
struct sockaddr_storage their_addr, right_addr;
fd_set readfds, writefds;
char buf[MAXBUFLEN];
char temp_buf[MAXBUFLEN];
char d_to_s[MAXBUFLEN];
char *field;
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
FILE *m_storage;
struct packet inet_packet;
static const struct packet EmptyPacket;
static int rt[51][4];
bool re_exists=false;
bool rt_empty=true;
struct timeval tv;
memset(&hints, 0, sizeof hints);//"zero out" the hints struct
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
//prepare socket address structures and store them in servinfo and store in linked list
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %sn", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("listener: socket");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("listener: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socketn");
return 2;
}
if ((rrr = getaddrinfo(NULL, RTEX_R_PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %sn", gai_strerror(rrr));
return 1;
}
for(p2 = servinfo; p2 != NULL; p2 = p2->ai_next) {
if ((rtex_rr_sockfd = socket(p2->ai_family, p2->ai_socktype,
p2->ai_protocol)) == -1) {
perror("listener: socket");
continue;
}
if (bind(rtex_rr_sockfd, p2->ai_addr, p2->ai_addrlen) == -1) {
close(rtex_rr_sockfd);
perror("listener: bind");
continue;
}
break;
}
if (p2 == NULL) {
fprintf(stderr, "listener: failed to bind socketn");
return 2;
}
if((rrw = getaddrinfo(NULL, RTEX_R_PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %sn", gai_strerror(rrw));
return 1;
}
for(p3 = servinfo; p3 != NULL; p3 = p3->ai_next) {
if((rtex_rw_sockfd = socket(p3->ai_family, p3->ai_socktype,
p3->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
break;
}
if (p3 == NULL) {
fprintf(stderr, "listener: failed to bind socketn");
return 2;
}
//free up memory no longer needed after binding has completed
freeaddrinfo(servinfo);
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(rv, &readfds);
FD_SET(rrr, &readfds);
FD_SET(rrw, &writefds);
printf("Listen Moden");
//main while loop, listens for packets.
//Upon receipt of packet, information is stored in a struct for processing.
first=0;
while(1)
{
i=0;
inet_packet = EmptyPacket;
rt_empty=true;
tv.tv_sec = 50;
if(rv > rrr && rv > rrw)
max_fd = (rv + 1);
else if(rrr > rv && rrr > rrw)
max_fd = (rrr + 1);
else if(rrw > rv && rrw > rrr)
max_fd = (rrw + 1);
printf("before select...n");
select(max_fd, &readfds, &writefds, NULL, NULL);
printf("after select...n");
addr_len = sizeof their_addr;
if(FD_ISSET(rv, &readfds))
{
printf("rv is set...n");
if((numbytes = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
} else if(FD_ISSET(rrr, &readfds))
{
printf("rr read is set...n");
if((numbytes = recvfrom(rtex_rr_sockfd, buf, sizeof(buf), 0,
(struct sockaddr *)&right_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
} else if(FD_ISSET(rrw, &writefds))
{
printf("rr write is set...n");
if((numbytes = sendto(rtex_rw_sockfd, inet_packet.payload, sizeof(inet_packet.payload),
0, p3->ai_addr, p3->ai_addrlen)) == -1) {
perror("sendto rr");
exit(1);
}
}
运行程序并在程序启动后在终端按回车键。它可能会从选择中返回。为什么?因为你告诉选择听的唯一东西是fd 0,stdin! rv
,rrr
和rrw
都只是从getaddrinfo()
返回的值,而您只是忽略了socket()
返回的实际fd。 max_fd
也将是一个垃圾值,因为所有测试值彼此相等。(如果它恰好为零,它甚至不会对标准做出反应)
另外,下次,请将代码减少到最小示例。如果您的问题是选择不返回,则例如。所有的处理代码都是完全无关紧要的,一个套接字就足够了,使用 getaddrinfo()
是额外的。此外,为了一个最小的示例,在删除getaddrinfo()
内容时,您可能会自己发现错误。