--- lbbs/src/bbs_net.c 2025/05/11 11:33:44 1.35 +++ lbbs/src/bbs_net.c 2025/05/13 02:21:39 1.38 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -150,22 +151,32 @@ static void process_bar(int n, int len) int bbsnet_connect(int n) { - int sock, flags, ret, loop, error; - ssize_t len; + int sock, ret, loop, error; + int sock_connected = 0; + int flags_sock; + int flags_stdin; + int flags_stdout; + int len; struct sockaddr_in sin; - char buf[LINE_BUFFER_LEN]; - fd_set read_fds; - fd_set write_fds; - struct timeval timeout; + char input_buf[LINE_BUFFER_LEN]; + char output_buf[LINE_BUFFER_LEN]; + int input_buf_len = 0; + int output_buf_len = 0; + int input_buf_offset = 0; + int output_buf_offset = 0; + struct epoll_event ev, events[MAX_EVENTS]; + int nfds, epollfd; + int stdin_read_wait = 0; + int stdout_write_wait = 0; + int sock_read_wait = 0; + int sock_write_wait = 0; struct hostent *p_host = NULL; int tos; - int i; char remote_addr[IP_ADDR_LEN]; int remote_port; time_t t_used; struct tm *tm_used; int ch; - int offset; clearscr(); @@ -215,73 +226,114 @@ int bbsnet_connect(int n) prints("\033[1;32m穿梭进度条提示您当前已使用的时间,按\033[1;33mCtrl+C\033[1;32m中断。\033[m\r\n"); process_bar(0, MAX_PROCESS_BAR_LEN); - // Set socket as non-blocking - flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); - - if ((ret = connect(sock, (struct sockaddr *)&sin, sizeof(sin))) < 0) + epollfd = epoll_create1(0); + if (epollfd < 0) { - if (errno != EINPROGRESS) - { - prints("\033[1;31m连接失败!\033[m\r\n"); - press_any_key(); - return -1; - } + log_error("epoll_create1() error (%d)\n", errno); + return -1; } - for (i = 0; i < MAX_PROCESS_BAR_LEN; i++) + ev.events = EPOLLOUT; + ev.data.fd = sock; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &ev) == -1) { - ch = igetch(0); // 0.1 second - if (ch == Ctrl('C') || SYS_server_exit) - { - return 0; - } - - FD_ZERO(&read_fds); - FD_SET(sock, &read_fds); - - FD_ZERO(&write_fds); - FD_SET(sock, &write_fds); + log_error("epoll_ctl(socket) error (%d)\n", errno); + return -1; + } - timeout.tv_sec = 0; - timeout.tv_usec = 400 * 1000; // 0.4 second + ev.events = EPOLLIN; + ev.data.fd = STDIN_FILENO; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) == -1) + { + log_error("epoll_ctl(STDIN_FILENO) error (%d)\n", errno); + return -1; + } - ret = select(sock + 1, &read_fds, &write_fds, NULL, &timeout); + // Set socket as non-blocking + flags_sock = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, flags_sock | O_NONBLOCK); - if (ret == 0) // Timeout + while (!SYS_server_exit) + { + if ((ret = connect(sock, (struct sockaddr *)&sin, sizeof(sin))) < 0) { - process_bar(i + 1, MAX_PROCESS_BAR_LEN); + if (errno == EAGAIN || errno == EALREADY || errno == EINPROGRESS) + { + log_std("Debug: %d\n", errno); + // Use select / epoll to check writability of the socket, + // then use getsockopt to check the status of the socket. + // See man connect(2) + break; + } + else if (errno == EINTR) + { + continue; + } + else + { + log_error("connect(socket) error (%d)\n", errno); + prints("\033[1;31m连接失败!\033[m\r\n"); + press_any_key(); + return -1; + } } - else if (ret < 0) + } + + for (int j = 0; j < MAX_PROCESS_BAR_LEN && !sock_connected && !SYS_server_exit; j++) + { + nfds = epoll_wait(epollfd, events, MAX_EVENTS, 500); // 0.5 second + + if (nfds < 0) { if (errno != EINTR) { - log_error("select() error (%d) !\n", errno); + log_error("epoll_wait() error (%d)\n", errno); return -1; } } - // ret > 0 - else if (FD_ISSET(sock, &read_fds) || FD_ISSET(sock, &write_fds)) + else if (nfds == 0) // timeout + { + process_bar(j + 1, MAX_PROCESS_BAR_LEN); + } + else // ret > 0 { - len = sizeof(error); - if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0) + for (int i = 0; i < nfds; i++) { - log_error("getsockopt() error (%d) !\n", error); - return -1; + if (events[i].data.fd == sock) + { + len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) < 0) + { + log_error("getsockopt() error (%d) !\n", error); + return -1; + } + if (error == 0) + { + sock_connected = 1; + } + } + else if (events[i].data.fd == STDIN_FILENO) + { + ch = igetch(0); + if (ch == Ctrl('C')) + { + return 0; + } + } } - - break; // connected } } - if (i == MAX_PROCESS_BAR_LEN) + if (SYS_server_exit) + { + return 0; + } + if (!sock_connected) { prints("\033[1;31m连接超时!\033[m\r\n"); press_any_key(); return -1; } - fcntl(sock, F_SETFL, flags); /* restore file status flags */ - tos = IPTOS_LOWDELAY; if (setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) { @@ -292,124 +344,233 @@ int bbsnet_connect(int n) iflush(); log_std("BBSNET connect to %s:%d\n", remote_addr, remote_port); - BBS_last_access_tm = t_used = time(0); - loop = 1; + ev.events = EPOLLIN | EPOLLOUT | EPOLLET; + ev.data.fd = sock; + if (epoll_ctl(epollfd, EPOLL_CTL_MOD, sock, &ev) == -1) + { + log_error("epoll_ctl(socket) error (%d)\n", errno); + return -1; + } - while (loop && !SYS_server_exit) + ev.events = EPOLLOUT; + ev.data.fd = STDOUT_FILENO; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev) == -1) { - FD_ZERO(&read_fds); - FD_SET(STDIN_FILENO, &read_fds); - FD_SET(sock, &read_fds); + log_error("epoll_ctl(STDOUT_FILENO) error (%d)\n", errno); + return -1; + } - FD_ZERO(&write_fds); - FD_SET(STDIN_FILENO, &write_fds); - FD_SET(sock, &write_fds); + // Set STDIN/STDOUT as non-blocking + flags_stdin = fcntl(STDIN_FILENO, F_GETFL, 0); + flags_stdout = fcntl(STDOUT_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, flags_stdin | O_NONBLOCK); + fcntl(STDOUT_FILENO, F_SETFL, flags_stdout | O_NONBLOCK); - timeout.tv_sec = 0; - timeout.tv_usec = 100 * 1000; // 0.1 second + BBS_last_access_tm = t_used = time(0); + loop = 1; - ret = select(sock + 1, &read_fds, &write_fds, NULL, &timeout); + while (loop && !SYS_server_exit) + { + nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); // 0.1 second - if (ret == 0) // timeout + if (nfds < 0) { - if (time(0) - BBS_last_access_tm >= MAX_DELAY_TIME) + if (errno != EINTR) { - loop = 0; + log_error("epoll_wait() error (%d)\n", errno); + break; } + continue; } - else if (ret < 0) + else if (nfds == 0) // timeout { - if (errno != EINTR) + if (time(0) - BBS_last_access_tm >= MAX_DELAY_TIME) { - log_error("select() error (%d) !\n", errno); - loop = 0; + break; } + continue; } - else if (ret > 0) + + for (int i = 0; i < nfds; i++) { - if (FD_ISSET(STDIN_FILENO, &read_fds) && FD_ISSET(sock, &write_fds)) + if (events[i].data.fd == STDIN_FILENO || stdin_read_wait) { - // Set STDIN as non-blocking - flags = fcntl(STDIN_FILENO, F_GETFL, 0); - fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); - - len = read(STDIN_FILENO, buf, sizeof(buf)); - if (len < 0) + stdin_read_wait = 1; + while (input_buf_len < sizeof(input_buf) && !SYS_server_exit) { - if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + ret = (int)read(STDIN_FILENO, input_buf + input_buf_len, sizeof(input_buf) - (size_t)input_buf_len); + if (ret < 0) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + stdin_read_wait = 0; + break; + } + else if (errno == EINTR) + { + continue; + } + else + { + log_error("read(STDIN) error (%d)\n", errno); + loop = 0; + break; + } + } + else if (ret == 0) // broken pipe { - log_error("read(STDIN) error (%d)\n", errno); + log_std("read(STDIN) EOF\n"); + stdin_read_wait = 0; loop = 0; + break; + } + else + { + input_buf_len += ret; + BBS_last_access_tm = time(0); + continue; } } - else if (len == 0) - { - log_error("read(STDIN) reach EOF\n"); - loop = 0; - } - else + } + + if (events[i].data.fd == sock || sock_write_wait) // EPOLLOUT + { + sock_write_wait = 1; + while (input_buf_offset < input_buf_len && !SYS_server_exit) { - offset = 0; - do + ret = (int)write(sock, input_buf + input_buf_offset, (size_t)(input_buf_len - input_buf_offset)); + if (ret < 0) { - ret = (int)write(sock, buf + offset, (size_t)(len - offset)); - if (ret < 0) + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + sock_write_wait = 0; + break; + } + else if (errno == EINTR) + { + continue; + } + else { log_error("write(socket) error (%d)\n", errno); loop = 0; break; } - offset += ret; - } while (offset < len); - - BBS_last_access_tm = time(0); + } + else if (ret == 0) // broken pipe + { + log_std("write(socket) EOF\n"); + sock_write_wait = 0; + loop = 0; + break; + } + else + { + input_buf_offset += ret; + if (input_buf_offset >= input_buf_len) // Output buffer complete + { + input_buf_offset = 0; + input_buf_len = 0; + break; + } + continue; + } } - - // Restore STDIN flags - fcntl(STDIN_FILENO, F_SETFL, flags); } - if (FD_ISSET(sock, &read_fds) && FD_ISSET(STDIN_FILENO, &write_fds)) - { - // Set socket as non-blocking - flags = fcntl(sock, F_GETFL, 0); - fcntl(sock, F_SETFL, flags | O_NONBLOCK); - len = read(sock, buf, sizeof(buf)); - if (len < 0) + if (events[i].data.fd == sock || sock_read_wait) // EPOLLIN + { + sock_read_wait = 1; + while (output_buf_len < sizeof(output_buf) && !SYS_server_exit) { - if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) + ret = (int)read(sock, output_buf + output_buf_len, sizeof(output_buf) - (size_t)output_buf_len); + if (ret < 0) { - log_error("read(socket) error (%d)\n", errno); + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + sock_read_wait = 0; + break; + } + else if (errno == EINTR) + { + continue; + } + else + { + log_error("read(socket) error (%d)\n", errno); + loop = 0; + break; + } + } + else if (ret == 0) // broken pipe + { + log_std("read(socket) EOF\n"); + sock_read_wait = 0; loop = 0; + break; + } + else + { + output_buf_len += ret; + continue; } } - else if (len == 0) - { - log_error("read(socket) reach EOF\n"); - loop = 0; - } - else + } + + if (events[i].data.fd == STDOUT_FILENO || stdout_write_wait) + { + stdout_write_wait = 1; + while (output_buf_offset < output_buf_len && !SYS_server_exit) { - offset = 0; - do + ret = (int)write(STDOUT_FILENO, output_buf + output_buf_offset, (size_t)(output_buf_len - output_buf_offset)); + if (ret < 0) { - ret = (int)write(STDOUT_FILENO, buf + offset, (size_t)(len - offset)); - if (ret < 0) + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + stdout_write_wait = 0; + break; + } + else if (errno == EINTR) + { + continue; + } + else { log_error("write(STDOUT) error (%d)\n", errno); loop = 0; break; } - offset += ret; - } while (offset < len); + } + else if (ret == 0) // broken pipe + { + log_std("write(STDOUT) EOF\n"); + stdout_write_wait = 0; + loop = 0; + break; + } + else + { + output_buf_offset += ret; + if (output_buf_offset >= output_buf_len) // Output buffer complete + { + output_buf_offset = 0; + output_buf_len = 0; + break; + } + continue; + } } - - // Restore socket flags - fcntl(sock, F_SETFL, flags); } } } + // Restore STDIN/STDOUT flags + fcntl(STDIN_FILENO, F_SETFL, flags_stdin); + fcntl(STDOUT_FILENO, F_SETFL, flags_stdout); + + // Restore socket flags + fcntl(sock, F_SETFL, flags_sock); + if (close(sock) == -1) { log_error("Close socket failed\n"); @@ -492,7 +653,7 @@ int bbs_net() ch = igetch(0); switch (ch) { - case KEY_NULL: // broken pipe + case KEY_NULL: // broken pipe case Ctrl('C'): // user cancel return 0; case KEY_TIMEOUT: