Ringkasan
Terkadang Anda harus mengirim data Out of band (OOB) yang mendesak ke receiver Anda. Receiver adalah pengguna atau aplikasi apa pun yang menerima data. Anda ingin data OOB ini diperlakukan sebagai data dengan prioritas lebih tinggi daripada data normal apa pun yang mungkin Anda kirim. Jika data OOB yang ingin Anda kirim satu byte, Anda bisa menggunakan fungsi Select untuk mencari data OOB. Anda dapat menggunakan fungsi recv untuk membaca data. Namun, dalam Transmission Control Protocol (TCP), blok data OOB selalu satu byte. Oleh karena itu, jika Anda mengirim data OOB bita ganda, hanya byte terakhir data OOB yang diambil. Data yang tersisa diperlakukan seperti data normal. Artikel ini menggunakan kode sampel untuk menjelaskan cara mengirim data OOB bita ganda dengan menggunakan soket Microsoft Windows (Winsock).
DALAM TUGAS INI
PENDAHULUAN
Artikel ini menguraikan cara mengirim data di luar pita beberapa bita dengan menggunakan Winsock. Aplikasi sampel dibuat di Microsoft Visual C++. Karena mekanisme OOB ini tidak didukung secara langsung pada tingkat soket Microsoft Windows, Anda harus menerapkan mekanisme OOB ini di tingkat aplikasi. Ini bukan data OOB True. Dalam aplikasi sampel ini, Anda membuat dua soket pada sisi klien, juga dikenal sebagai sisi pengirim, untuk mengirim data OOB dan data normal. Di sisi server, juga dikenal sebagai sisi Penerima, Anda menggunakan dua soket untuk memproses data dalam dua utas. Satu utas adalah untuk data OOB. Utas lainnya adalah untuk data normal. Untuk mensimulasikan mekanisme OOB, Anda harus menyinkronkan kedua utas tersebut. Pastikan bahwa thread yang memproses data OOB memiliki prioritas lebih tinggi daripada thread yang memproses data normal.Catatan Aplikasi sampel ini menjelaskan satu cara untuk mengirim data multi byte OOB. Anda mungkin harus merevisi kode untuk membuat kode berfungsi di lingkungan Anda.kembali ke atas
Membuat aplikasi klien
-
Membuat aplikasi klien. Dalam aplikasi klien ini, kode contoh berikut menjelaskan cara membuat dua soket:
SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
Variabel Myoobsocket digunakan untuk mengirim data OOB. Variabel Mynormalsocket digunakan untuk mengirim data normal.
-
Karena data yang dikirimkan variabel Myoobsocket tidak benar OOB data, Anda harus memiliki cara untuk memberi tahu server tipe data apa yang harus dikirim sebelum Anda mulai mengirim data. Kode contoh berikut menguraikan cara mengirim karakter awal untuk memberi tahu server tentang jenis data yang akan dikirim. Gunakan "U" untuk OOB data dan gunakan "N" untuk data normal.
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.
-
Kode contoh berikut menjelaskan cara membuat aplikasi klien memantau input dari pengguna. Aplikasi klien mengirim karakter huruf besar sebagai OOB data. Gandakan setiap karakter input untuk membuat string beberapa bita.
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);}...}
Menampilkan kode sampel untuk aplikasi klien, Myclient. CPP kembali ke atas
Membuat aplikasi server
-
Di sisi server, kode contoh berikut menjelaskan cara membuat soket pendengar untuk memantau port yang digunakan untuk komunikasi:
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.
-
Kode contoh berikut menguraikan cara memanggil fungsi Accept untuk membuat soket pendengar menunggu koneksi masuk mencoba:
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....}
-
Untuk mensimulasikan mekanisme OOB, Anda harus menyinkronkan kedua utas tersebut. Dalam aplikasi sampel ini, gunakan objek global event untuk melakukan hal ini. Letakkan pernyataan berikut ini di bagian atas file.
//Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
-
Buat handler OOB yang dijalankan oleh utas terpisah. Dalam handler ini, Anda menggunakan fungsi Select untuk mendeteksi ketika data tiba. Saat data tiba, ikuti langkah-langkah berikut:
-
Panggil fungsi Resetevent untuk mengatur objek event hrecvevent ke status non-signaled.
ResetEvent(hRecvEvent);
Ini memblokir pengendali data normal yang dijalankan di utas lain.
-
Panggil fungsi revc untuk membaca data.
recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
-
Karena data lainnya mungkin dikirim dari buffer yang dapat ditahan, Panggil kembali fungsi recv , bersama dengan bendera MSG_PEEK , untuk menentukan apakah ada data yang tersedia pada kabel.
recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
Gunakan salah satu metode berikut ini, bergantung pada apakah ada data yang tertunda untuk dibaca atau tidak:
-
Jika tidak ada data yang tertunda untuk dibaca, Panggil fungsi Setevent untuk mengatur objek acara tertentu ke status bersinyal.
SetEvent(hRecvEvent);
Saat Anda melakukan ini, utas pengendali data normal dapat melanjutkan. Thread pengendali data normal menunggu untuk objek acara Hrecvevent sebelum utas pengendali data normal melanjutkan.
-
Jika data masih tertunda untuk dibaca, Panggil kembali fungsi recv untuk membaca data yang tersisa. Lalu, lihat lagi untuk data tertunda.
-
Ulangi langkah 4A hingga 4c hingga semua data tertunda telah dibaca.
-
-
Membuat pengendali data normal. Handler ini mirip dengan handler data OOB kecuali bahwa pengendali data normal memanggil fungsi WaitForSingleObject saat pengendali data normal mendeteksi data tersebut telah diterima.
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.
Tampilkan sampel kode untuk aplikasi server, myserver. CPP kembali ke atas
Tampilkan kode sampel untuk aplikasi klien, Myclient. CPP
Catatan Anda harus menyertakan referensi ke file pustaka Winsock, Ws2_32. lib, untuk membuat kompilasi kode.
#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;}
Tampilkan sampel kode untuk aplikasi server, myserver. CPP
Catatan Anda harus menyertakan referensi ke file pustaka Winsock, Ws2_32. lib, untuk membuat kompilasi kode.
#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;}
Menguji sampel
-
Pada prompt perintah, ketik myserver. exe 4444 untuk memulai server pada port 4444. aplikasi server menampilkan pesan berikut ini dan menunggu klien:
Server siap!
-
Di jendela prompt perintah yang berbeda, ketikkanMyclient ipaddress 4444 untuk memulai klien. Catatan Tempat penampung Ipaddress adalah placeholder untuk alamat IP server.
-
Server menampilkan pesan yang mirip dengan yang berikut ini:
Waiting.Waiting*..*Saat Anda menerima pesan sebelumnya, Anda harus mengetikkan Abcdefghi di klien dalam 10 detik sebelum server berlanjut.
Setelah sekitar 10 detik, server menampilkan yang berikut ini:
[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normal]: aaaaaaaaaa [normal]: cccccccccc [normal]: eeeeeeeeee [normal]: gggggggggg [normal]: iiiiiiiiiikembali ke atas
Referensi
Untuk informasi tambahan, kunjungi situs web Microsoft Developer Network (MSDN) berikut:
Fungsi data Out-of-bandhttp://msdn2.Microsoft.com/en-US/Library/ms740102.aspxWinsock yang terpisahhttp://msdn2.Microsoft.com/en-US/Library/ms741394.aspxUntuk informasi tambahan, klik nomor artikel berikut ini untuk menampilkan artikel di Basis Pengetahuan Microsoft:
331756 Fungsi Ioctlsocket tidak dapat mendeteksi data inline di luar bandkembali ke atas