面试题答案
一键面试- 建立Socket连接步骤:
- 创建Socket:使用
socket()
函数创建一个套接字,指定协议族(如AF_INET
用于IPv4)、套接字类型(如SOCK_STREAM
用于TCP连接)和协议(通常为0)。例如在C语言中:
int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { // 处理错误 }
- 设置非阻塞模式:使用
fcntl()
函数将套接字设置为非阻塞模式。在C语言中:
int flags = fcntl(sockfd, F_GETFL, 0); if (flags == -1) { // 处理错误 } if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) { // 处理错误 }
- 绑定地址:如果是服务器端,使用
bind()
函数将套接字绑定到特定的地址和端口。在C语言中:
struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERVER_PORT); servaddr.sin_addr.s_addr = INADDR_ANY; if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { // 处理错误 }
- 监听连接(服务器端):如果是服务器端,使用
listen()
函数开始监听连接。在C语言中:
if (listen(sockfd, BACKLOG) == -1) { // 处理错误 }
- 发起连接(客户端):如果是客户端,使用
connect()
函数发起连接。在C语言中:
struct sockaddr_in servaddr; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(SERVER_PORT); inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr); int conn_result = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); if (conn_result == -1 && (errno == EINPROGRESS || errno == EAGAIN || errno == EWOULDBLOCK)) { // 处理连接中错误,如下面所述 }
- 创建Socket:使用
- 处理EAGAIN或EWOULDBLOCK错误:
- 客户端:当
connect()
返回 -1且errno
为EAGAIN
或EWOULDBLOCK
时,说明连接正在进行中。可以通过以下方式处理:- 使用
select
、poll
或epoll
:这些I/O多路复用机制可以监听套接字的可写事件。例如使用select
:
fd_set write_fds; FD_ZERO(&write_fds); FD_SET(sockfd, &write_fds); struct timeval timeout; timeout.tv_sec = 5; // 设置超时时间 timeout.tv_usec = 0; int sel_result = select(sockfd + 1, NULL, &write_fds, NULL, &timeout); if (sel_result == -1) { // 处理select错误 } else if (sel_result == 0) { // 连接超时处理 } else { int err; socklen_t errlen = sizeof(err); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) { // 处理getsockopt错误 } if (err == 0) { // 连接成功处理 } else { // 连接失败处理,根据错误码处理 } }
- 使用
- 服务器端:当
accept()
返回 -1且errno
为EAGAIN
或EWOULDBLOCK
时,说明当前没有新的连接到达,此时可以继续通过select
、poll
或epoll
监听是否有新连接到来,然后再次调用accept()
。例如使用epoll
:int epollfd = epoll_create1(0); if (epollfd == -1) { // 处理epoll_create1错误 } struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = sockfd; if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) { // 处理epoll_ctl错误 } struct epoll_event events[MAX_EVENTS]; int num_events = epoll_wait(epollfd, events, MAX_EVENTS, -1); for (int i = 0; i < num_events; i++) { if (events[i].data.fd == sockfd) { int clientfd = accept(sockfd, NULL, NULL); if (clientfd == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { continue; } else if (clientfd == -1) { // 处理其他accept错误 } else { // 处理新连接 } } }
- 客户端:当