В UNIX, если соединение установлено, сокет доступен для записи. Если же произошла ошибка, то сокет будет доступен одновременно для записи и для чтения. Однако на это нельзя полагаться при проверке успешности соединения, поскольку можно возвратиться из connect и получить первые данные еще до обращения к select. В таком случае сокет будет доступен и для чтения, и для записи -в точности, как при возникновении ошибки.
Листинг 3.26. UNIX-версия функции isconnected
1 int isconnected( SOCKET s, fd_set *rd, fd_set *wr, fd_set *ex )
2 {
3 int err;
4 int len = sizeoff err );
5 errno =0; /* Предполагаем, что ошибки нет. */
6 if ( !FD_ISSET( s, rd ) && !FD_ISSET( s, wr ) )
7 return 0;
8 if (getsockopt( s, SOL_SOCKET, SO_ERROR, &err, &len ) < 0)
9 return 0;
10 errno = err; /* Если мы не соединились. */
11 return err == 0;
12 }
5-7 Если сокет не доступен ни для чтения, ни для записи, значит, соединение не установлено, и возвращается нуль. Значение errno заранее установлено в нуль, чтобы вызывающая программа могла определить, что сокет действительно, не готов (разбираемый случай) или имеет Metro ошибка.
8-11 Вызываем getsockopt для получения статуса сокета. В некоторых версиях UNIX getsockopt возвращает в случае ошибки -1. В таком случае записываем в errno код ошибки. В других версиях система просто возвращает статус, оставляя его проверку пользователю. Идея кода, который корректно работает в обоих случаях, позаимствована из книги [Stevens 1998].
Согласно спецификации Winsock, ошибки, которые возвращает connect через неблокирующий сокет, индицируются путем возбуждения события исключения в select. Следует заметить, что в UNIX событие исключения всегда свидетельствует о поступлении срочных данных. Версия функции isconnected для;Windows показана в листинге 3.27.
Листинг 3.27. Windows-версия функции isconnected
1 int isconnected( SOCKET s, fd_set *rd, fd_set *wr, fd_set *ex)
2 {
3 WSASetLastError ( 0 );
4 if ( !FD_ISSET( s, rd ) && !FD_ISSET(s, wr ) )
5 return 0;
6 if ( FD_ISSET( s, ex ) )
7 return 0;
8 return 1;
9 }
3-5 Так же, как и в версии для UNIX, проверяем, соединен ли сокет. Если нет, устанавливаем последнюю ошибку в нуль и возвращаем нуль.
6-8 Если для сокета есть событие исключения, возвращается нуль, в противном случае - единица.