דלג לתוכן הראשי
היכנס דרך Microsoft
היכנס או צור חשבון.
שלום,
בחר חשבון אחר.
יש לך חשבונות מרובים
בחר את החשבון שברצונך להיכנס באמצעותו.

סיכום

לעתים עליך לשלוח נתונים של ' מחוץ להרכב ' (OOB) לרסיברים שלך. מקלטים הם כל המשתמשים או כל היישומים המקבלים את הנתונים. ברצונך שנתונים אלה של OOB יטופלו כנתונים בעלי עדיפות גבוהה יותר מאשר כל הנתונים הרגילים שאתה עשוי לשלוח. אם נתוני OOB שברצונך לשלוח הם בית אחד, באפשרותך להשתמש בפונקציה select כדי לחפש נתונים של OOB. באפשרותך להשתמש בפונקציה קבל כדי לקרוא את הנתונים. עם זאת, בפרוטוקול בקרת השידור (TCP), בלוק הנתונים של OOB הוא תמיד בית אחד. לכן, אם אתה שולח נתוני OOB מרובי בתים, רק הבייט האחרון של נתוני OOB מאוחזר. הנתונים הנותרים מטופלים כנתונים רגילים. מאמר זה משתמש בקוד לדוגמה כדי לתאר כיצד לשלוח נתוני OOB מרובי בתים באמצעות Microsoft Windows Socket (Winsock).

במשימה זו

מבוא

מאמר זה מתאר כיצד לשלוח נתונים מחוץ לקבוצה (chars) מחוץ ללהקה באמצעות Winsock. היישומים לדוגמה נוצרים ב-Microsoft Visual C++. מאחר שמנגנון OOB זה אינו נתמך ישירות ברמת השקע של Microsoft Windows, עליך ליישם מנגנון זה של OOB ברמת היישום. אין זה OOB נתונים true. ביישום לדוגמה זה, אתה יוצר שני שקעים בצד הלקוח, הנקרא גם בצד השולח, כדי לשלוח נתוני 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. משתנה myNormalSocket משמש לשליחת נתונים רגילים.

  2. מאחר שהנתונים שהמשתנה myOOBSocket שולח אינם נתונים מסוג true 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.
  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. cpp בחזרה לראש הדף

יצירת יישום שרת

  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. מאחר שייתכן שיישלחו נתונים רבים יותר מאלה שהמאגר יכול להכיל, התקשר שוב לפונקציה קבל , יחד עם דגל הMSG_PEEK , כדי לקבוע אם כל הנתונים זמינים בכבל.

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

      השתמש באחת מהשיטות הבאות, בהתאם לשאלה אם יש נתונים הממתינים לקריאה או לא:

      • אם לא ממתינים נתונים לקריאה, התקשר לפונקציה SetEvent כדי להגדיר את אובייקט האירוע שצוין למצב הנקוב.

        SetEvent(hRecvEvent);

        כאשר תעשה זאת, הליך המשנה הרגיל של מטפל בנתונים יכול לחדש. הליך המשנה של מטפל הנתונים הרגיל ממתין לאובייקט האירוע hRecvEvent לפני שהליך המשנה של מטפל הנתונים הרגיל מתחדש.

      • אם הנתונים עדיין ממתינים לקריאה, התקשר שוב לפונקציה קבל כדי לקרוא את הנתונים הנותרים. לאחר מכן, הסתכל שוב על נתונים ממתינים.

    חזור על שלבים 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. 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;}

חזרה לראש הדף

בדיקת המדגם

  1. בשורת הפקודה, הקלד Myserver. exe 4444 כדי להפעיל את השרת ביציאה 4444. יישום השרת מציג את ההודעה הבאה ולאחר מכן ממתין ללקוחות:

    השרת מוכן!

  2. בחלון שורת פקודה אחר, הקלדMyClient IPAddress 4444 כדי להפעיל את הלקוח. הערה מציין המיקום IPAddress הוא מציין מיקום עבור כתובת ה-IP של השרת.

  3. השרת מציג הודעה הדומה להודעה הבאה:

    Waiting.Waiting*..*כאשר אתה מקבל את ההודעה הקודמת, עליך להקליד aBcDeFgHi בלקוח תוך 10 שניות לפני שהשרת ימשיך.

לאחר כ-10 שניות, השרת מציג את הפרטים הבאים:

[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normal]: aaaaaaaaaa [Normal]: cccccccccc [normal]: eeeeeeeeeeחזרה לראש הדף

הפניות

לקבלת מידע נוסף, בקר באתרי האינטרנט הבאים של Microsoft Developer Network (MSDN):

Protocol-הפונקציה data out-of-bandHttp://msdn2.microsoft.com/en-us/library/ms740102.aspxWinsock functionshttp://msdn2.microsoft.com/en-us/library/ms741394.aspxלקבלת מידע נוסף, לחץ על מספר המאמר הבא כדי להציג את המאמר ב-Microsoft Knowledge Base:

331756 הפונקציה Ioctlsocket אינה מזהה נתונים מוטבעים מחוץ ללהקהחזרה לראש הדף

זקוק לעזרה נוספת?

מעוניין באפשרויות נוספות?

גלה את יתרונות המנוי, עיין בקורסי הדרכה, למד כיצד לאבטח את המכשיר שלך ועוד.

קהילות עוזרות לך לשאול שאלות ולהשיב עליהן, לתת משוב ולשמוע ממומחים בעלי ידע עשיר.

האם מידע זה היה שימושי?

עד כמה אתם מרוצים מאיכות השפה?
מה השפיע על החוויה שלך?
בלחיצה על 'שלח', אתה מאפשר למשוב שלך לשפר מוצרים ושירותים של Microsoft. מנהל ה-IT שלך יוכל לאסוף נתונים אלה. הצהרת הפרטיות.

תודה על המשוב!

×