Я пытаюсь реализовать простую функцию, чтобы проверить, прослушивает ли сервер определенный порт. У меня есть требование, чтобы вызов был переносимым и неблокирующим, поэтому я не могу просто полагаться на connect().
FD_ISSET(sock, &write_set)
, кажется, возвращает true, даже если удаленная служба не работает.
Это то, что я получил до сих пор:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
int check_connection(const char* ip_address, int port) {
struct sockaddr_in server_addr;
int sock, flags;
// Create a socket
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("Socket creation failed");
return -1;
}
// Set server address parameters
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
// Convert IP address to binary form and set it in the server address structure
if (inet_pton(AF_INET, ip_address, &server_addr.sin_addr) <= 0) {
perror("Invalid IP address");
close(sock);
return -1;
}
// Set socket to non-blocking mode
if ((flags = fcntl(sock, F_GETFL, 0)) < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
perror("Failed to set socket to non-blocking mode");
close(sock);
return -1;
}
// Connect to the server
int result = connect(sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
printf("connect() -> res: %d\n", result);
if (result < 0 && errno != EINPROGRESS) {
perror("Connection failed");
close(sock);
return -1;
}
// Check if the connection is established
fd_set write_set;
FD_ZERO(&write_set);
FD_SET(sock, &write_set);
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
result = select(sock + 1, NULL, &write_set, NULL, &timeout);
printf("select() -> res: %d\n", result);
if (result <= 0 || !FD_ISSET(sock, &write_set)) {
perror("Connection timed out or failed");
close(sock);
return -1;
}
// Connection successful
close(sock);
return 0;
}
int main(int argc, char *argv[]) {
if (argc == 3) {
if (check_connection(argv[1], atoi(argv[2])) == 0) {
printf("Service is up\n");
} else {
printf("Service is down\n");
}
} else {
printf("usage:\n");
printf(" %s <ip> <port>\n", argv[0]);
}
}
Полученные результаты
Это результат, который я получаю. Проверка с помощью netcat.
Тест на обслуживание, которое работает
$ ./main 10.0.1.100 80
connect() -> res: -1
select() -> res: 1
Service is up
$ nc -vz 10.0.1.100 80
Connection to 10.0.1.100 80 port [tcp/http] succeeded!
Тест на сервис, который не работает
$ ./main 10.0.1.100 443
connect() -> res: -1
select() -> res: 1
Service is up
$ nc -vz 10.0.1.100 443
nc: connect to 10.0.1.100 port 443 (tcp) failed: Connection refused
Поскольку он неблокирующий, соедините return -1 с errno = EINPROGRESS.
это то, как он говорит вам, что соединение завершено?
Хорошо, но в этом режиме он действительно что-то делает с соединением, пока вы не попытаетесь что-то отправить?
Похоже, это так, добавив send(), я смог определить, установлено ли соединение или нет.
После успешного select
повторного запуска вы должны getsockopt(sock, SOL_SOCKET, SO_ERROR, ...)
получить фактическое состояние соединения.
Страница руководства очень понятна:
Сокет является неблокирующим, и соединение не может быть завершено сразу, можно выбрать(2) или опросить(2) для завершения, выбрав сокет для записи. После select(2) указывает на возможность записи, используйте getsockopt(2) для чтения параметр SO_ERROR на уровне SOL_SOCKET для определения успешно ли завершено соединение () (SO_ERROR ноль) или безуспешно (SO_ERROR — одна из обычных коды ошибок, перечисленные здесь, объясняющие причину отказ).
connect() -> res: -1
?