我正在開發一個套接字應用程序,它必須對網絡故障有效。SO_KEEPALIVE在write()的調用過程中不起作用?
應用程序有2個正在運行的線程,一個來自套接字(一個read()循環)的等待消息,另一個發送消息到套接字(一個write()循環)。
我目前正在嘗試使用SO_KEEPALIVE來處理網絡故障。 它工作正常,如果我只在read()上被阻塞。連接丟失後幾秒鐘(網絡電纜被移除),read()將失敗並顯示'連接超時'消息。但是,如果在網絡斷開連接之後(並且在超時結束之前)嘗試使用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);
}
重現錯誤,使用telnet 5656. 如果一對夫婦OS秒後,連接丟失,除非我試着寫在終端的東西后就會退出。在這種情況下,它會永遠阻止。
所以,問題是:怎麼了?如何解決它?還有其他的選擇嗎?
謝謝!
我試過使用Wireshark來檢查網絡連接。如果我不調用write(),我可以看到正在發送的TCP保持活動包,並且連接在幾秒鐘後關閉。
相反,如果我嘗試寫(),它停止發送保活分組,並開始發送TCP重發,而不是(這似乎挺合我意)。問題是,重傳之間的時間間隔在每次失敗後都會變得越來越大,並且似乎永遠不會放棄並關閉套接字。
有沒有辦法設置最大重傳次數或類似的東西? 謝謝
我喜歡它,但我正在實現一個現有協議的一側,它沒有任何方法來強制'ping'。 –