Загальні відомості

Інколи ви повинні надати своїм одержувачам термінову негрупу (OOB) дані. Приймачі – це будь-які користувачі або будь-які програми, які отримують дані. Ви хочете, щоб ці дані OOB розглядалися як вищі пріоритетні дані, ніж будь-які звичайні дані, які ви можете відправити. Якщо дані OOB, які потрібно передати, є одним байтом, можна скористатися функцією Select для пошуку даних OOB. Щоб прочитати дані, можна скористатися функцією redv . Проте в протоколі керування передаванням (TCP) блок даних OOB завжди є одним байтом. Тому, якщо ви надсилаєте кілька байтів даних OOB, буде отримано лише останній байт даних OOB. Решта даних розглядається як звичайні дані. У цій статті використано зразок коду, щоб описати, як передавати кілька байтів даних OOB за допомогою Microsoft Windows Socket (Winsock).

У ЦЬОМУ ЗАВДАННІ

ОСНОВНІ ВІДОМОСТІ

У цій статті описано, як можна використовувати кілька байтів (символів), які не містять діапазону даних за допомогою Winsock. Зразки програм створюються в Microsoft Visual c + +. Оскільки цей механізм OOB не підтримується безпосередньо на рівні програми Microsoft Windows, потрібно виконати цей механізм OOB на рівні застосунку. Це не стосується даних OOB. У цьому зразку програми ви створюєте два гнізда на стороні клієнта, також відомі як сторони відправника, щоб відправити дані в OOB і нормальні дані. На стороні сервера, що також називається приймачем, використовується два розетки для обробки даних у двох потоках. Один потік для даних OOB. Інший потік – для звичайних даних. Щоб імітувати механізм OOB, потрібно синхронізувати ці два потоки. Переконайтеся, що потік, який обробляє дані OOB, має вищий пріоритет, ніж потік, який обробляє нормальні дані.Примітка. Цей зразок програми описує один із способів обміну даними з кількома байтовими надбудовами OOB. Можливо, потрібно буде змінити код, щоб працювати з кодом в навколишньому середовищі.назад на початок

Створення клієнтського застосунку

  1. Створіть клієнтську програму. У цій клієнтській програмі розповідається про те, як створювати два сполуки:

    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. Змінна для обміну звичайними даними використовується в полі « Міонорсогніздо ».

  2. Оскільки дані, які містять змінну Myoobsocket , не є істинними даними OOB, потрібно мати спосіб повідомити про те, що тип даних, який потрібно надіслати, перш ніж почати надсилати дані. У наведеному нижче зразку коду розповідається про те, як надсилати початковий символ, щоб повідомити про те, що тип даних, які надсилатиметься. Використовуйте "U" для даних OOB і використайте "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.
  3. Наведений нижче зразок коду описує, як зробити клієнтський застосунок відстежувати дані користувача. Програма клієнта надсилає великі символи як дані 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. КГЕС повернутися на початок

Створення серверного застосунку

  1. На стороні сервера, у наведеному нижче зразку коду розповідається про те, як створити сокет слухача, щоб стежити за портом, який використовується для спілкування.

    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.
  2. У наведеному нижче зразку коду розповідається про те, як телефонувати за допомогою функції " прийняти ", щоб мати можливість чекати вхідного підключення.

    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....}
  3. Щоб імітувати механізм OOB, потрібно синхронізувати ці два потоки. У цьому зразку програми використовуйте глобальний об'єкт подій , щоб зробити це. У верхній частині файлу помістіть наведений нижче оператор.

    //Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
  4. Створіть обробник OOB, який виконується окремим різьбленням. У цьому обробнику за допомогою функції Select можна визначити, коли надходять дані. Коли дані надходять, виконайте наведені нижче дії.

    1. Викличте функцію Resetevent , щоб установити об'єкт події hrecvevent на несигнал стану.

      ResetEvent(hRecvEvent);

      Цей параметр блокує звичайний обробник даних, який виконується в іншому потоці.

    2. Викличте функцію revc , щоб прочитати дані.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
    3. Оскільки більше даних може бути надіслане, ніж буфер може бути, знову зателефонуйте до функції Rev , використовуючи MSG_PEEK прапора, щоб визначити, чи доступні будь-які дані в проводі.

      recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);

      Виконайте одну з наведених нижче способів, залежно від того, чи є дані, які очікують на читання чи ні.

      • Якщо дані не очікують на читання, зателефонуйте до функції Setevent , щоб установити вказаний об'єкт події на стан сигнал.

        SetEvent(hRecvEvent);

        Під час цього можна відновити звичайну нитку обробника даних. Потік звичайного обробника даних очікує на об'єкт події Hrecvevent перед тим, як не відновиться звичайний потік обробника даних.

      • Якщо дані все ще очікують на читання, знову зателефонуйте до функції Rev , щоб прочитати решту даних. Потім повторіть спробу для відкладеного пошуку даних.

    Повторіть кроки 4A через 4C, доки не буде прочитано всі дані, що очікують на читання.

  5. Створення звичайного обробника даних. Цей обробник схожий на обробник даних 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. КГЕС повернутися на початок

Відобразити зразок коду для клієнтського застосунку, 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;}

назад на початок

Перевірка зразка

  1. У командному рядку введіть MyServer. exe 4444 , щоб почати сервер на порті 4444. програма Server відобразить таке повідомлення, а потім чекає на клієнта:

    Сервер готовий!

  2. В іншому вікні командного рядка введітьMyclient ipaddress 4444 , щоб почати клієнт. Примітка. Покажчик місця заповнення – це покажчик місця заповнення для IP-адреси сервера.

  3. На сервері відобразиться повідомлення, подібне до такого:

    Waiting.Waiting*..*Під час отримання попереднього повідомлення потрібно ввести Abcndefghi на клієнта протягом 10 секунд, перш ніж сервер буде продовжуватися.

Приблизно через 10 секунд сервер відображає таке:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFKFFKDIFF [OOB]: HHHHHHHH [Normal]: aaaaaaaaaa [Normal]: cccccccccc [нормальний]: eeeeeeeeee [нормальний]: ggyggggg [нормальний]: iiiiiiiiiiназад на початок

Посилання

Щоб отримати додаткові відомості, перейдіть на веб-сайт Microsoft Developer Network (MSDN):

Незалежне від діапазону даних функціїhttp://msdn2.Microsoft.com/EN-US/Library/ms740102.aspxWinsock, що залежать від протоколу,http://msdn2.Microsoft.com/EN-US/Library/ms741394.aspxЩоб отримати додаткові відомості, клацніть наведений нижче номер статті, щоб переглянути статтю в базі знань Microsoft Knowledge Base:

331756 Функція Iocttsocket не може виявити вбудовані дані в межах діапазонуназад на початок

Потрібна додаткова довідка?

Отримуйте нові функції раніше за інших

Приєднатися до Microsoft оцінювачів >

Ця інформація корисна?

Наскільки ви задоволені якістю мови?
Що вплинуло на ваші враження?

Дякуємо за відгук!

×