Я разрабатываю приложение сокета, которое должно быть надежным для сетевых сбоев.SO_KEEPALIVE не работает во время вызова write()?
Приложение имеет 2 работающих потока, одно ожидающее сообщение из сокета (цикл чтения()), а другое отправляет сообщения в сокет (цикл write()).
В настоящее время я пытаюсь использовать SO_KEEPALIVE для обработки сетевых сбоев. Он работает нормально, если я заблокирован только для чтения(). Через несколько секунд после того, как соединение будет потеряно (сетевой кабель удален), сбой чтения() завершится с сообщением «Тайм-аут соединения».
Но, если я попытаюсь выполнить wrte() после отключения сети (и до истечения таймаута), как write(), так и read() будут блокироваться навсегда, без ошибок.
Это код с разделенным образцом, который направляет stdin/stdout в сокет. Он прослушивает порт 5656:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
int socket_fd;
void error(const char *msg) {
perror(msg);
exit(1);
}
//Read from stdin and write to socket
void* write_daemon (void* _arg) {
while (1) {
char c;
int ret = scanf("%c", &c);
if (ret <= 0) error("read from stdin");
int ret2 = write(socket_fd, &c, sizeof(c));
if (ret2 <= 0) error("write to socket");
}
return NULL;
}
//Read from socket and write to stdout
void* read_daemon (void* _arg) {
while (1) {
char c;
int ret = read(socket_fd, &c, sizeof(c));
if (ret <= 0) error("read from socket");
int ret2 = printf("%c", c);
if (ret2 <= 0) error("write to stdout");
}
return NULL;
}
//Enable and configure KEEPALIVE - To detect network problems quickly
void config_socket() {
int enable_no_delay = 1;
int enable_keep_alive = 1;
int keepalive_idle =1; //Very short interval. Just for testing
int keepalive_count =1;
int keepalive_interval =1;
int result;
//=> http://tldp.org/HOWTO/html_single/TCP-Keepalive-HOWTO/#setsockopt
result = setsockopt(socket_fd, SOL_SOCKET, SO_KEEPALIVE, &enable_keep_alive, sizeof(int));
if (result < 0)
error("SO_KEEPALIVE");
result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPIDLE, &keepalive_idle, sizeof(int));
if (result < 0)
error("TCP_KEEPIDLE");
result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPINTVL, &keepalive_interval, sizeof(int));
if (result < 0)
error("TCP_KEEPINTVL");
result = setsockopt(socket_fd, SOL_TCP, TCP_KEEPCNT, &keepalive_count, sizeof(int));
if (result < 0)
error("TCP_KEEPCNT");
}
int main(int argc, char *argv[]) {
//Create Server socket, bound to port 5656
int listen_socket_fd;
int tr=1;
struct sockaddr_in serv_addr, cli_addr;
socklen_t clilen = sizeof(cli_addr);
pthread_t write_thread, read_thread;
listen_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_socket_fd < 0)
error("socket()");
if (setsockopt(listen_socket_fd,SOL_SOCKET,SO_REUSEADDR,&tr,sizeof(int)) < 0)
error("SO_REUSEADDR");
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(5656);
if (bind(listen_socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
error("bind()");
//Wait for client socket
listen(listen_socket_fd,5);
socket_fd = accept(listen_socket_fd, (struct sockaddr *) &cli_addr, &clilen);
config_socket();
pthread_create(&write_thread, NULL, write_daemon, NULL);
pthread_create(&read_thread , NULL, read_daemon , NULL);
close(listen_socket_fd);
pthread_exit(NULL);
}
Чтобы воспроизвести ошибку, используйте телнет 5656. Если выйдет через пару секунд после того, как бя связь теряется, если я пытаюсь написать что-то в терминале. В этом случае он будет блокироваться навсегда.
Итак, вопросы: что случилось? как это исправить? Существуют ли другие альтернативы?
Спасибо!
Я попытался использовать Wireshark для проверки сетевого подключения. Если я не вызываю write(), я могу видеть, что пакеты TCP keep-alive отправляются, а соединение закрывается через несколько секунд.
Если вместо этого я пытаюсь написать(), он перестает отправлять пакеты Keep-Alive и вместо этого начинает отправлять повторные передачи TCP (мне кажется, что все в порядке). Проблема в том, что время между повторными передачами становится все больше и больше после каждого сбоя, и, похоже, он никогда не отказывается и не закрывает сокет.
Есть ли способ установить максимальное количество повторных передач или что-нибудь подобное? Спасибо
Мне это нравится, но я реализую только одну сторону существующего протокола, который не имеет никакого способа заставить «пинги». –