В MacOS я установил сокет чтения + записи с O_NONBLOCK
для подключения к удаленному серверу.Я использую kqueue для ожидания и координации событий ввода / вывода.Вызов connect()
немедленно вызывает EINPROGRESS
, как и должно быть.Вскоре соединение завершено.Это подтверждается получением данных с сервера.
НО, я думал, что kqueue вернет kevent, когда соединение, наконец, завершится и станет доступным для записи?Такое событие не происходит.Страница man для connect()
сообщает:
[EINPROGRESS] Гнездо не блокируется, и соединение не может быть установлено немедленно.Можно выбрать (2) для завершения, выбрав сокет для записи.
(1) Означает ли это, что мне нужно использовать select()
вместо kqueque для получения уведомления о завершенном соединении?
(2) Кстати, если я попытаюсь подключиться к серверу, на котором ничего не прослушивается через порт, то kevent вскоре вернет EOF, чтобы сообщить, что там ничего нет.
(3)Если я пытаюсь подключиться к несуществующему IP-адресу, то время ожидания моего кэвента истекает после установленного мной периода ожидания (15 секунд).
Код ниже.Скомпилируйте, запустите «сервер», такой как nc -l 10000
, запустите программу с аргументом IP-адреса или без него, и он будет подключаться удаленно или локально, соответственно.
#include <sys/event.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
}
int fcntl_flags = fcntl(fd, F_GETFL);
if (fcntl_flags < 0) {
perror("fcntl");
}
fcntl_flags = fcntl(fd, F_SETFL, fcntl_flags | O_NONBLOCK);
if (fcntl_flags < 0) {
perror("fcntl");
}
struct sockaddr_in addr_in;
memset(&addr_in, 0, sizeof addr_in);
addr_in.sin_family = AF_INET;
addr_in.sin_port = htons(10000);
if (!inet_pton(AF_INET, argc > 1 ? argv[1] : "127.0.0.1", &addr_in.sin_addr)) {
perror("inet_aton");
exit(EXIT_FAILURE);
}
struct kevent change;
struct kevent event;
// Create kqueue
int kq;
if ((kq = kqueue()) < 0) {
perror("kqueue");
}
// Set read and write on socket
EV_SET(&change, fd, EVFILT_READ | EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, NULL);
// Put socket in kqueue, return immediately
struct timespec tmout = { 0, 0 };
int nev = kevent(kq, &change, 1, &event, 1, &tmout);
printf("Number events: %d (should be 0)\n", nev);
// Connect socket to server
if (!connect(fd, (struct sockaddr *) &addr_in, sizeof(struct sockaddr))) {
printf("Connected\n");
}
else if (errno == EINPROGRESS) {
printf("EINPROGRESS\n");
}
// Wait for connection to complete
tmout.tv_sec = 15;
nev = kevent(kq, &change, 0, &event, 1, &tmout);
if (nev) {
printf("Number events: %d, filter: %x, flags: %x, data: %ld\n", nev, event.filter, event.flags, event.data);
}
else {
printf("Timeout\n");
}
return 0;
}