Передача и прием данных
После того как канал создан, можно начинать передачу данных. Для передачи данных при помощи протокола гарантированной доставки TCP вы можете воспользоваться функциями send и recv , которые входят в программный интерфейс Windows Sockets.
Функция передачи данных send имеет три параметра - дескриптор сокета sock, на котором выполняется передача, адрес буфера buf, содержащего передаваемое сообщение, размер этого буфера bufsize и флаги flags:
int send (SOCKET sock, const char FAR* buf, int bufsize, int flags);
В нашем приложении CLIENT мы передаем данные серверу следующим образом:
char szBuf[80]; lstrcpy(szBuf, "Test string"); send (srv_socket , szBuf, lstrlen(szBuf), 0);
Параметры функции recv , предназначенной для приема данных, аналогичны параметрам функции send :
int recv (SOCKET sock, char FAR * buf, int bufsize, int flags);
Заметим, что функции recv и send возвращают количество, соответственно, принятых и переданных байт данных. Приложение, которое принимает данные, должно вызывать функцию recv в цикле до тех пор, пока не будут приняты все переданные данные. При этом на один вызов функции send может приходиться несколько вызовов функции recv.
В случае ошибки обе эти функции возвращают значение SOCKET_ERROR . Для анализа причин возникновения ошибки следует воспользоваться функцией WSAGetLastError .
Приведем список кодов ошибок, которые могут возникать при вызове команды send:
Код ошибки | Описание |
WSANOTINITIALISED | Перед использованием функции необходимо вызвать функцию WSAStartup |
WSAENETDOWN | Сбой в сети |
WSAEACCES | Указанный адрес является широковещательным (broadcast), однако перед вызовом функции не был установлен соответствующий флаг |
WSAEINTR | Работа функции была отменена при помощи функции WSACancelBlockingCall |
WSAEINPROGRESS | Выполняется блокирующая функция интерфейса Windows Sockets |
WSAEFAULT | Параметр buf указан неправильно (он не указывает на адресное пространство, принадлежащее приложению) |
WSAENETRESET | Необходимо сбросить соединение |
WSAENOBUFS | Возникла блокировка буфера |
WSAENOTCONN | Сокет не подсоединен |
WSAENOTSOCK | Указанный в параметре дескриптор не является сокетом |
WSAESHUTDOWN | Сокет был закрыт функцией shutdown |
WSAEWOULDBLOCK | Сокет отмечен как неблокирующий, но запрошенная операция приведет к блокировке |
WSAEMSGSIZE | Был использован сокет типа SOCK_DGRAM
(предназначенный для передачи датаграмм). При этом размер пакета данных превышает максимально допустимый для данной реализации интерфейса Windows Sockets |
WSAEINVAL | Сокет не был подключен функцией bind |
WSAECONNABORTED | Сбой из-за слишком большой задержки или по другой причине |
WSAECONNRESET | Сброс соединения удаленным узлом |
При выполнении функции recv могут возникать следующие ошибки:
Код ошибки | Описание |
WSANOTINITIALISED | Перед использованием функции необходимо вызвать функцию WSAStartup |
WSAENETDOWN | Сбой в сети |
WSAENOTCONN | Сокет не подсоединен |
WSAEINTR | Работа функции была отменена при помощи функции WSACancelBlockingCall |
WSAEINPROGRESS | Выполняется блокирующая функция интерфейса Windows Sockets |
WSAENOTSOCK | Указанный в параметре дескриптор не является сокетом |
WSAESHUTDOWN | Сокет был закрыт функцией shutdown |
WSAEWOULDBLOCK | Сокет отмечен как неблокирующий, но запрошенная операция приведет к блокировке |
WSAEMSGSIZE | Размер пакета данных превышает размер буфера, в результате чего принятый пакет был обрезан |
WSAEINVAL | Сокет не был подключен функцией bind |
WSAECONNABORTED | Сбой из-за слишком большой задержки или по другой причине |
WSAECONNRESET | Сброс соединения удаленным узлом |
Наше приложение SERVER
демонстрирует асинхронный прием данных.
После установки канала связи оно вызывает функцию WSAAsyncSelect , указывая ей в качестве последнего параметра комбинацию констант FD_READ и FD_CLOSE . При этом функция главного окна приложения будет получать сообщение WSA_NETEVENT в тот момент времени, когда чтение данных не вызовет блокировки приложения:
#define WSA_NETEVENT (WM_USER + 2) rc = WSAAsyncSelect (srv_socket , hWnd, WSA_NETEVENT, FD_READ | FD_CLOSE );
При необходимости выполнения асинхронной посылки данных вы можете указать функции WSAAsyncSelect еще и параметр FD_WRITE .
Если функция WSAAsyncSelect выполнилась успешно, она возвращает нулевое значение, при ошибке - значение SOCKET_ERROR.
В зависимости от значения последнего параметра могут возникать разные коды ошибки, которые можно получить при помощи функции WSAGetLastError. Следующие ошибки могут возникнуть при любом значении параметра:
Код ошибки | Описание |
WSANOTINITIALISED | Перед использованием функции необходимо вызвать функцию WSAStartup |
WSAENETDOWN | Сбой в сети |
WSAEINVAL | Сокет не был подключен функцией bind |
WSAEINPROGRESS | Выполняется блокирующая функция интерфейса Windows Sockets |
При использовании параметра FD_CONNECT возможно появление следующих ошибок:
Код ошибки | Описание |
WSAEADDRINUSE | Указанный адрес уже используется |
WSAEADDRNOTAVAIL | Указанный адрес не доступен |
WSAEAFNOSUPPORT | Для данного сокета нельзя использовать указанное семейство адресов |
WSAECONNREFUSED | Попытка установления канала связи была отвергнута |
WSAEDESTADDRREQ | Необходимо указать адрес получателя пакета |
WSAEFAULT | Неправильно указан параметр namelen |
WSAEINVAL | Сокет уже подключен к адресу |
WSAEISCONN | Сокет уже подсоединен |
WSAEMFILE | Больше нет доступных дескрипторов |
WSAENETUNREACH | Из данного узла и в данное время невозможно получить доступ к сети |
WSAENOBUFS | Нет места для размещения буфера |
WSAENOTCONN | Сокет на подключен |
WSAENOTSOCK | Указан дескриптор файла, а не сокета |
WSAETIMEDOUT | При попытке установления канала связи возникла задержка во времени |
Код ошибки | Описание |
WSAENETDOWN | Сбой в сети |
WSAECONNRESET | Сброс соединения удаленным узлом |
WSAECONNABORTED | Сбой из-за слишком большой задержки или по другой причине |
Обработчик сообщения WSA_NETEVENT должен выполнить анализ причины, по которой он был вызван, так как за один вызов функции WSAAsyncSelect можно задать несколько событий, вызывающих генерацию сообщения. Этот анализ проводится, например, следующим образом:
void WndProc_OnWSANetEvent(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { char szTemp[256]; int rc;
// Если на сокете выполняется передача данных, // принимаем и отображаем эти данные в виде // текстовой строки if(WSAGETSELECTEVENT(lParam) == FD_READ ) { rc = recv ((SOCKET)wParam, szTemp, 256, 0); if(rc) { szTemp[rc] = '\0'; MessageBox(NULL, szTemp, "Reсeived data", MB_OK); } return; }
// Если соединение завершено, выводми об этом сообщение else if(WSAGETSELECTEVENT(lParam) == FD_CLOSE ) { MessageBox(NULL, "Connection closed", "Server", MB_OK); } }
Отметим, что параметр wParam содержит дескриптор сокета, на котором выполняется передача данных, а параметр lParam - код события, которое произошло в сети.