面试题答案
一键面试以下是示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8888
#define MAX_EVENTS 10
int main() {
int sockfd;
struct sockaddr_in servaddr, cliaddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 设置为非阻塞模式
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(PORT);
if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
close(sockfd);
exit(EXIT_FAILURE);
}
if (listen(sockfd, MAX_EVENTS) < 0) {
perror("listen failed");
close(sockfd);
exit(EXIT_FAILURE);
}
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
struct timeval timeout;
timeout.tv_sec = 5; // 等待5秒
timeout.tv_usec = 0;
int activity = select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
if (activity < 0) {
perror("select error");
} else if (activity == 0) {
printf("select timeout\n");
} else {
if (FD_ISSET(sockfd, &read_fds)) {
int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &(socklen_t){sizeof(cliaddr)});
if (connfd < 0) {
perror("accept failed");
} else {
printf("Accepted connection\n");
close(connfd);
}
}
}
close(sockfd);
return 0;
}
select函数参数含义
- nfds:监控的文件描述符集里最大文件描述符加1。
- readfds:指向
fd_set
结构的指针,用于检查可读性的文件描述符集合。 - writefds:指向
fd_set
结构的指针,用于检查可写性的文件描述符集合。 - exceptfds:指向
fd_set
结构的指针,用于检查是否有异常条件的文件描述符集合。 - timeout:指向
timeval
结构的指针,用于设置select等待的时间。NULL
表示无限等待;timeval
结构体中tv_sec
是秒数,tv_usec
是微秒数。
根据返回值判断I/O事件
- 返回值大于0:表示有文件描述符就绪,即有I/O事件发生。可以通过
FD_ISSET
宏来判断具体哪个文件描述符发生了事件。例如,若要判断sockfd
是否可读,可使用if (FD_ISSET(sockfd, &read_fds))
。 - 返回值等于0:表示在指定的
timeout
时间内没有任何文件描述符就绪,即超时。 - 返回值小于0:表示发生错误,
errno
会被设置以指示错误类型,如EINTR
表示操作被信号中断,EBADF
表示文件描述符无效等。