Есть прога, фрагмент котороый представлен ниже. Нужно в указанном месте определить, разорвалось ли подключение с сервером. В общем, читайте комментарии. В инете читал, что нужно попытаться отправить send() и если оно вернёт -1, то соединения нет. НО! В моём случаи используются не блокируемые сокеты, так что send() всегда возвращает -1 (мол, подождите, щас отправлю). Код (Text): while (1) { tv.tv_sec = 1; tv.tv_usec = 0; /* ждём, до появления данных в сокете */ FD_ZERO (&readfds); FD_SET (priv->socket, &readfds); rc = select (priv->socket+1, &readfds, NULL, NULL, &tv); if (rc<0) { /* какая-то ошибка в select() */ } else if (rc==0) { /* тайм аут, в моей реализации - ждать дальше с новым select() */ continue; } else { /* какие-то данные пришли, можно обрабатывать */ } /* А здесь я хочу проверить, живо ли ещё соединение. Вызвать send() не предлагать. */ }
man send Код (Text): RETURN VALUES The call returns the number of characters sent, or -1 if an error occurred. ERRORS The send() function and sendto() and sendmsg() system calls fail if: [EBADF] An invalid descriptor was specified. [EACCES] The destination address is a broadcast address, and SO_BROADCAST has not been set on the socket. [ENOTSOCK] The argument s is not a socket. [EFAULT] An invalid user space address was specified for an argument. [EMSGSIZE] The socket requires that message be sent atomically, and the size of the message to be sent made this impossible. [EAGAIN] The socket is marked non-blocking and the requested operation would block. [ENOBUFS] The system was unable to allocate an internal buffer. The operation may succeed when buffers become avail- able. [ENOBUFS] The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient con- gestion. [EHOSTUNREACH] The remote host was unreachable. [EISCONN] A destination address was specified and the socket is already connected. [ECONNREFUSED] The socket received an ICMP destination unreachable message from the last message sent. This typically means that the receiver is not listening on the remote port. [EHOSTDOWN] The remote host was down. [ENETDOWN] The remote network was down. [EPERM] The process using a SOCK_RAW socket was jailed and the source address specified in the IP header did not match the IP address bound to the prison. [EPIPE] The socket is unable to send anymore data (SBS_CANTSENDMORE has been set on the socket). This typically means that the socket is not connected. Эти значения в errno попадают. А вообще твоя прога обязана следить за разрывом соединения. И потом не будет необходимости проверять соединение Обычно происходит так: когда рвется соединение, select сообщает, что на сокете есть данные, ты вызываешь read: Код (Text): ssize_t ret = recv(sclient, buffer, buf_size, 0); if((!ret) || (ret < 0 && errno != EAGAIN)) { // обработка закрытия сокета, удаление его из массива например ... } else { // данные пришли, сокет живой }
perez Это не выход. У меня recv получает данные, и если возвращает -1, то, мол, данные в сокете закончились (обрабатывай и жди дальше появления новых). Хотя, можно подправить немного алгоритм и сделать проверку `первых' данных Подумаю на досуге
А вообще по теме обнаружения разрыва соединения: В Windows'е есть понятие FD_CLOSE, которое можно перехватить, и если оно перехвачено, то соединение разорорвалось. Так это всё работает в мой Windows программе. Не понимаю, почему такое событие трудно было принять в POSIX ;-( *ужас...
AlannY Вот написал tcp сервер с использованием select. На суперправильность не претендую, однако все работает =) Код (Text): #include <stdio.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/sockio.h> #include <sys/sysctl.h> #include <sys/time.h> #include <sys/wait.h> #include <ctype.h> #include <err.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <string.h> #include <unistd.h> #include <sysexits.h> #include <signal.h> #include <stdarg.h> #include <net/if.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #include <netinet/ip_fw.h> #include <netinet/ip_dummynet.h> #include <netinet/tcp.h> #include <arpa/inet.h> #include <sys/un.h> #include <fcntl.h> #include <sys/select.h> #include <time.h> #include <vector> #include <map> using namespace std; class client_info { int some_param; }; int main() { int sock = socket(AF_INET, SOCK_STREAM, 0); if (socket < 0) { printf("socket() failed: %d\n", errno); return 0; } sockaddr_in sin; sock= socket(AF_INET, SOCK_STREAM, 0); bzero(&sin, sizeof(sin)); sin.sin_family= AF_INET; sin.sin_port= htons(6666); sin.sin_addr.s_addr= htonl(INADDR_ANY); int ss_n = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&ss_n, sizeof(ss_n)); if(bind(sock, (struct sockaddr*)&sin, sizeof(sin))== -1) { printf("No bind!\n"); return 0; } if(listen(sock, 1) != 0){ printf("No listen!"); return 0; } fcntl(sock, F_SETFL, O_NONBLOCK); fd_set fdset_r, fdset_w; map<int, client_info> map_clients; while(true) { FD_ZERO(&fdset_r); // обнуляем списки дескрипторов FD_ZERO(&fdset_w); FD_SET(sock, &fdset_r); // загоняем главный сокет (ждущий соединений) в списки дескрипторов для вызова select FD_SET(sock, &fdset_w); int max_fd = sock; for(map<int, client_info> ::iterator Iter = map_clients.begin(), End = map_clients.end(); Iter != End; Iter++) { int sclient = Iter->first; FD_SET(sclient, &fdset_r); // загоняем в списки дескрипторов все клиентские сокеты if(sclient > max_fd) max_fd = sclient; } int ret = select(max_fd + 1, &fdset_r, &fdset_w, NULL, NULL); if(ret <= 0) continue; if(FD_ISSET(sock, &fdset_r) || FD_ISSET(sock, &fdset_w)) // значит пришло новое соединение на главный сокет { int sclient = accept(sock, NULL, NULL); // принимаем соединение //fcntl(sclient, F_SETFL, O_NONBLOCK); if(sclient <= 0) break; map_clients[sclient]; // загоняем новый сокет в std::map printf("Socket #%02d connected!\n", sclient); } for(map<int, client_info> ::iterator Iter = map_clients.begin(), End = map_clients.end(); Iter != End; Iter++) { int sclient = Iter->first; if(FD_ISSET(sclient, &fdset_r)) // проверяем, есть ли активность на этом сокете { FD_CLR(sclient, &fdset_r); int buf_size = 1024; char *buffer = new char[buf_size]; bzero(buffer, buf_size); ssize_t ret = recv(sclient, buffer, buf_size, 0); if((!ret) || (ret < 0 && errno != EAGAIN)) { printf("Socket #%02d disconnected!\n", sclient); delete[] buffer; map_clients.erase(sclient); close(sclient); if(map_clients.empty()) break; Iter = map_clients.begin(); End = map_clients.end(); continue; } printf("socket #%02d:\t%s\n", sclient, buffer); send(sclient, buffer, strlen(buffer), 0); delete[] buffer; } } } // end of main while for(map<int, client_info> ::iterator Iter = map_clients.begin(), End = map_clients.end(); Iter != End; Iter++) { int sclient = Iter->first; close(sclient); } close(sock); return 0; } Компилится в FreeBSD 7.0. В линуксе возможно нужно будет поправить хедеры. Далее подклюдаемся с другой машины (консоли): Видим, что пишет сервер: Потом произвольно вводим с клавы любые символы в телнет - сессиях: Код (Text): socket #06: a socket #06: s socket #06: d socket #06: a socket #06: s socket #06: d socket #06: f socket #07: s socket #07: d socket #07: f socket #07: s socket #07: d socket #07: f socket #05: a socket #05: d socket #05: f socket #07: s socket #07: d socket #07: f socket #06: s socket #06: d socket #06: f socket #06: s socket #06: d Затем по очереди закрываем телнет сессии: Как видишь, все работает.
Всё, мне понятна моя ошибка. Нужно проверять, что возвращает recv, а у меня проверки нет (как я уже писал, мол, если ошибка (или recv возвращает -1), то данные на сокете закончились: обрабатывать и ждать заного).