Аннотация
Иногда вы должны отправлять своим получателям срочные данные за пределами диапазона (OOB). Приемники — это любые пользователи или приложения, которые получают данные. Вы хотите, чтобы данные OOB обрабатывались как данные с более высоким приоритетом, чем любые обычные данные, которые вы можете отправить. Если данные OOB, которые нужно отправить, представляют собой один байт, можно использовать функцию SELECT для поиска данных OOB. Для чтения данных можно использовать функцию recv . Однако в протоколе управления передачей (TCP) блок данных OOB всегда имеет один байт. Таким образом, если вы отправляете данные OOB с несколькими байтами, извлекается только последний байт данных OOB. Оставшиеся данные обрабатываются как обычные данные. В этой статье описывается пример кода для отправки многобайтовых данных OOB с помощью Microsoft Windows Socket (Winsock).
В ЭТОЙ ЗАДАЧЕ
ВВЕДЕНИЕ
В этой статье описано, как отправлять однобайтовые данные за пределами диапазона с помощью Winsock. Примеры приложений создаются в Microsoft Visual C++. Поскольку этот механизм OOB не поддерживается напрямую на уровне сокетов Microsoft Windows, необходимо реализовать этот механизм OOB на уровне приложения. Это не относится к данным OOB. В этом примере приложения для отправки данных OOB и обычных данных вы создаете два сокета на стороне клиента, также называемой стороной отправителя. На стороне сервера, также называемой получателем, вы используете два сокета для обработки данных в двух потоках. Один поток предназначен для OOB Data. Другой поток предназначен для обычных данных. Чтобы смоделировать механизм OOB, необходимо синхронизировать эти два потока. Убедитесь, что поток, который обрабатывает данные OOB, имеет более высокий приоритет, чем поток, обрабатывающий нормальные данные.Примечание. В этом примере приложения описывается один из способов отправки многобайтовых данных OOB. Возможно, потребуется изменить код, чтобы сделать код работать в среде.к началу статьи
Создание клиентского приложения
-
Создание клиентского приложения. В этом клиентском приложении в следующем примере кода показано, как создать два сокета:
SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
Переменная myOOBSocket используется для отправки данных OOB. Переменная myNormalSocket используется для отправки обычных данных.
-
Так как данные, которые отправляет переменная myOOBSocket , не являются истинными данными OOB, необходимо указать серверу, какие данные должен отправлять сокет, прежде чем начинать отправлять данные. В приведенном ниже примере кода показано, как отправить начальный символ, чтобы уведомить сервер о том, какие данные нужно отправить. Используйте "U" для OOB Data и используйте "N" для обычных данных.
int nErr, nSize;nErr = connect(myNormalSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));//Look for a potential error here.//"remoteIp" is the remote address.nSize = send(myNormalSocket, "N",1, 0);//Look for a potential error here.nErr = connect(myOOBSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));//Look for a potential error here.nSize = send(myOOBSocket,"U",1, 0);//Look for a potential error here.
-
В приведенном ниже примере кода показано, как сделать так, чтобы клиентское приложение проносило данные от пользователя. Клиентское приложение отправляет в качестве данных OOB прописные буквы. Дублирование каждого вводимого символа для создания многобайтовой строки.
for (;;) {int ch = _getch();_putch( ch );if (ch=='.') {shutdown(myOOBSocket, 2); shutdown(myNormalSocket,2);break;} char buf[10]; memset(buf, ch, sizeof(buf)); if (isupper(ch)) {nSize = send(myOOBSocket, buf, sizeof(buf), 0);//Do not use the MSG_OOB flag. Send it as normal data.} else {nSize = send(myNormalSocket, buf, sizeof(buf), 0);}...}
Выводите пример кода для клиентского приложения, Myclient. cpp к началу страницы
Создание серверного приложения
-
На стороне сервера в следующем примере кода показано, как создать сокет прослушивателя для наблюдения за портом, используемым для обмена данными.
int nErr;SOCKET myListener = INVALID_SOCKET;myListener = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//Look for a potential return error here.nErr = bind(myListener,(SOCKADDR *)&localIp,sizeof(localIp));//Look for a potential return error here.//"localIp" is the local address of the server.nErr = listen(myListener, 5);//Look for a potential return error here.
-
В приведенном ниже примере кода показано, как вызвать функцию " принять ", чтобы включить сокет прослушивателя в ожидании попыток входящего подключения.
for (;;) {struct sockaddr_in remoteIp;SOCKET remoteSocket = INVALID_SOCKET;int nAddrLen = sizeof(SOCKADDR);remoteSocket = accept(myListener, (SOCKADDR *)(&remoteIp), &nAddrLen);//"remoteIp" is a pointer to a buffer that receives //the address of the connecting entity.BYTE buffer[1+1];int recv_len=recv(remoteSocket,(char *)&buffer,1,0);//This supposes that the client sockets will send//"U" or "N" as the initial character. buffer[recv_len] = '\0';if(strcmp((char *)buffer,"U")==0)hThread=CreateThread(0,0,OOBHandler,(LPVOID)remoteSocket, 0, &dwThreadId);//Create a new thread to process incoming OOB data. "OOBHandler" is a //pointer to the OOB data handler. else if(strcmp((char *)buffer,"N")==0)hThread=CreateThread(0,0,NormalHandler,(LPVOID)remoteSocket, 0, &dwThreadId);//Create a new thread to process incoming normal data. "NormalHandler" is //a pointer to the normal data handler....}
-
Чтобы смоделировать механизм OOB, необходимо синхронизировать эти два потока. В этом примере приложения используйте глобальный объект события . Добавьте следующий оператор в начало файла.
//Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
-
Создайте обработчик OOB, который выполняется в отдельном потоке. В этом обработчике функция SELECT используется для обнаружения прибытия данных. При получении данных выполните указанные ниже действия.
-
Вызовите функцию ResetEvent , чтобы присвоить объекту события hRecvEvent несигнальное состояние.
ResetEvent(hRecvEvent);
Таким образом блокируется стандартный обработчик данных, который выполняется в другом потоке.
-
Чтобы прочитать данные, вызовите функцию revc .
recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
-
Поскольку данные могут быть отправлены по сравнению с буфером, повторно вызывайте функцию recv вместе с флагом MSG_PEEK , чтобы определить, доступны ли какие-либо данные на канале.
recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
Используйте один из указанных ниже способов в зависимости от того, имеются ли данные для чтения или нет.
-
Если нет данных для чтения, вызовите функцию SetEvent , чтобы задать для определенного объекта события состояние сигнального состояния.
SetEvent(hRecvEvent);
После этого поток обработки данных в обычном режиме может возобновиться. Поток стандартного обработчика данных ожидает объект события hRecvEvent перед возобновлением потока обычных обработчиков данных.
-
Если данные по-прежнему ожидают чтения, вызовите функцию recv еще раз, чтобы прочитать оставшиеся данные. Затем снова просмотрите данные, ожидающие обработки.
-
Повторяйте шаги 4A до 4c, пока не прочтены все ожидающие данные.
-
-
Создайте обычный обработчик данных. Этот обработчик аналогичен обработчику данных OOB, за исключением того, что в обычном обработчике данных вызывается функция WaitForSingleObject , когда в обычном обработчике данных обнаруживается, что данные поступилы.
WaitForSingleObject(hRecvEvent,INFINITE);//You can set the interval parameter to a specific value if you do not want //the normal data handler to wait for the OOB handler indefinitely when OOB data arrives continuously.
Выводите пример кода для серверного приложения, MyServer. cpp, к началу страницы
Выводите пример кода для клиентского приложения Myclient. cpp
Примечание. Чтобы выполнить компиляцию кода, необходимо добавить ссылку на файл библиотеки Winsock Ws2_32. lib.
#include <stdio.h>#include <stdlib.h>#include <conio.h>#include <assert.h>#include <winsock.h>main(int argc, char *argv[]){int nErr, nSize;WSADATA wsaData;SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;unsigned short nPort; // Listen for the port number.struct sockaddr_in remoteIp;if (argc!=3){printf("TcpDemoC <RemoteIp> <Port>\n");return 0;}// The Init Socket API.nErr = WSAStartup(MAKEWORD(1,1),&wsaData);assert(nErr==0);nPort = (unsigned short)atol(argv[2]);myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);assert(myOOBSocket!=INVALID_SOCKET);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);assert(myNormalSocket!=INVALID_SOCKET);// Connect to the remote address.remoteIp.sin_family = AF_INET;remoteIp.sin_port = htons(nPort);;remoteIp.sin_addr.S_un.S_addr = inet_addr(argv[1]);nErr = connect(myOOBSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));if (nErr == SOCKET_ERROR) {printf("Connect failed because of %lX\n", WSAGetLastError());goto Cleanup;}nSize = send(myOOBSocket,"U",1, 0);if (nSize == SOCKET_ERROR) {printf("Send failed because of %lX\n", WSAGetLastError()); goto Cleanup;} nErr = connect(myNormalSocket, (SOCKADDR *)&remoteIp, sizeof(remoteIp));if (nErr == SOCKET_ERROR) {int error = WSAGetLastError();printf("Connect failed because of %lX\n", error);goto Cleanup;} nSize = send(myNormalSocket, "N",1, 0);if (nSize == SOCKET_ERROR) {printf("Send failed because of %lX\n", WSAGetLastError());goto Cleanup;}printf("Read for input:\n");for (;;) {int ch = _getch();_putch( ch );if (ch=='.') {shutdown(myOOBSocket, 2);shutdown(myNormalSocket,2);break;}char buf[10];memset(buf, ch, sizeof(buf));if (isupper(ch)) {nSize = send(myOOBSocket, buf, sizeof(buf), 0);}else{nSize = send(myNormalSocket, buf, sizeof(buf), 0);}if (nSize == SOCKET_ERROR) {printf("Send failed because of %lX\n", WSAGetLastError());break;}}Sleep(1000);Cleanup:if (myOOBSocket!=INVALID_SOCKET)closesocket(myOOBSocket);if (myNormalSocket!=INVALID_SOCKET)closesocket(myNormalSocket);WSACleanup();return 0;}
Выводите пример кода для серверного приложения MyServer. cpp
Примечание. Чтобы выполнить компиляцию кода, необходимо добавить ссылку на файл библиотеки Winsock Ws2_32. lib.
#include <windows.h>#include <stdio.h>#include <stdlib.h>#include <winsock.h>#include <assert.h>// Usage: myserver <port>HANDLE hRecvEvent; DWORD WINAPI OOBHandler(LPVOID lpParam){int nErr, nRecv_len;BYTE buffer[10 + 1];BOOL bClosing=FALSE;fd_set fdread;SOCKET remoteSocket=(SOCKET)lpParam;unsigned long ul=1;int nRet = ioctlsocket(remoteSocket, FIONBIO, &ul);if(SOCKET_ERROR==nRet){printf("Socket() failed: %d\n", WSAGetLastError());return 1;}printf("Waiting");for (int i=0; i<10; i++) {printf(".");Sleep(1000);}printf("The OOB handler is ready!\n");while (!bClosing){//Always clear the set before you call the select method.FD_ZERO(&fdread);//Add sockets to the sets.FD_SET(remoteSocket, &fdread);nErr=select(0,&fdread,0,0,0);if(nErr==SOCKET_ERROR){printf("Select() failed: %d\n",WSAGetLastError());return 1;}if(FD_ISSET(remoteSocket,&fdread)){ResetEvent(hRecvEvent);while(1){nRecv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0); if(nRecv_len==0){bClosing=TRUE;printf("The connection is closed!\n");break;}buffer[nRecv_len] = '\0';printf("[OOB]: %s\n", buffer); nRecv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);if (nRecv_len==SOCKET_ERROR) {if(WSAGetLastError()==WSAEWOULDBLOCK)break;elseprintf("Recv() failed. Win32 error is 0x%lx\n", WSAGetLastError());}}SetEvent(hRecvEvent);} }return 0;}DWORD WINAPI NormalHandler(LPVOID lpParam){int nErr,nRecv_len;BYTE buffer[10 + 1];fd_set fdread;SOCKET remoteSocket=(SOCKET)lpParam;printf("Waiting");for (int i=0; i<10; i++) { printf("*");Sleep(1000);}printf("Normal handler ready!\n");while(1) {//Always clear the set before you call the select method.FD_ZERO(&fdread);//Add sockets to the sets.FD_SET(remoteSocket, &fdread);nErr=select(0,&fdread,0,0,0);if(nErr==SOCKET_ERROR){printf("Select() failed: %d\n",WSAGetLastError());return 1;}if(FD_ISSET(remoteSocket,&fdread)){WaitForSingleObject(hRecvEvent,INFINITE);nRecv_len=recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0); if (nRecv_len==SOCKET_ERROR) {printf("Recv() failed. Win32 error is 0x%lx\n", WSAGetLastError());return 1;} if(nRecv_len==0){printf("Connection Closed!\n");break;} buffer[nRecv_len] = '\0';printf("[Normal]: %s\n", buffer);}}return 0;}int main(int argc, char *argv[]){WSADATA wsaData;int nErr;SOCKET myListener = INVALID_SOCKET;struct sockaddr_in localIp;unsigned short nPort; DWORD dwThreadId;HANDLE hThread=NULL;if (argc!=2){printf("MyServer <Port>\n");return 0;}nPort = (unsigned short)atol(argv[1]);nErr = WSAStartup(MAKEWORD(2,0),&wsaData);assert(nErr==0);assert(wsaData.wVersion == MAKEWORD(2,0));myListener = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);assert(myListener!=INVALID_SOCKET);// Bind the listen socket to any local IP address.localIp.sin_family = AF_INET;localIp.sin_port = htons(nPort);;localIp.sin_addr.S_un.S_addr = INADDR_ANY;nErr = bind(myListener,(SOCKADDR *)&localIp,sizeof(localIp));assert(nErr!=SOCKET_ERROR);nErr = listen(myListener, 5);assert(nErr==0);//Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");if (hRecvEvent == NULL) { printf("CreateEvent failed with error 0x%lx\n", GetLastError());return 1;}printf("The server is ready!\n");for (;;) {struct sockaddr_in remoteIp;SOCKET remoteSocket = INVALID_SOCKET;int nAddrLen = sizeof(SOCKADDR);remoteSocket = accept(myListener, (SOCKADDR *)(&remoteIp), &nAddrLen);if(remoteSocket == INVALID_SOCKET) {int error = WSAGetLastError();printf("Accept() failed. Win32 error is 0x%lx\n", GetLastError());goto Cleanup;} else {printf("Connected from %d.%d.%d.%d:%d\n", remoteIp.sin_addr.S_un.S_un_b.s_b1,remoteIp.sin_addr.S_un.S_un_b.s_b2,remoteIp.sin_addr.S_un.S_un_b.s_b3,remoteIp.sin_addr.S_un.S_un_b.s_b4,ntohs(remoteIp.sin_port));BYTE buffer[1+1];int nRecv_len=recv(remoteSocket,(char *)&buffer,1,0);if (nRecv_len==SOCKET_ERROR) {printf("Recv() failed. Win32 error is 0x%lx\n", WSAGetLastError());return 1;} buffer[nRecv_len] = '\0';if(strcmp((char *)buffer,"U")==0)hThread=CreateThread(0,0,OOBHandler,(LPVOID)remoteSocket, 0, &dwThreadId);else if(strcmp((char *)buffer,"N")==0)hThread=CreateThread(0,0,NormalHandler,(LPVOID)remoteSocket, 0, &dwThreadId); if(hThread==0){printf("CreateThread() failed: %d\n",GetLastError());return 1;}CloseHandle(hThread);}}closesocket(myListener);Cleanup:WSACleanup();return 0;}
Проверка примера
-
В командной строке введите MyServer. exe 4444 , чтобы запустить сервер на порту 4444. серверное приложение отобразит следующее сообщение, а затем ждет клиентов.
Сервер готов!
-
В другом окне командной строки введитеMyClient IPAddress 4444 , чтобы запустить клиент. Примечание. Заполнитель IPAddress — это заполнитель для IP-адреса сервера.
-
Сервер отобразит сообщение, подобное следующему:
Waiting.Waiting*..*При получении предыдущего сообщения необходимо ввести aBcDeFgHi на клиенте в течение 10 секунд, прежде чем сервер продолжит работу.
После примерно 10 секунд сервер выводит следующее:
[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [Normal]: aaaaaaaaaa [Normal]: cccccccccc [Normal]: eeeeeeeeeeк началу статьи
Ссылки
Дополнительные сведения можно найти на веб-сайте Microsoft Developer Network (MSDN) по следующему адресу:
Независимые от протоколов нестандартные данныеhttp://msdn2.Microsoft.com/en-US/Library/ms740102.aspxфункции Winsockhttp://msdn2.Microsoft.com/en-US/Library/ms741394.aspxДополнительные сведения см. в следующей статье базы знаний Майкрософт:
331756 Функция Ioctlsocket не может определить встроенные данные за пределами диапазона.к началу страницы