Resumo
Às vezes, você deve enviar dados de OOB (fora da faixa) urgentes para seus destinatários. Os receptores são usuários ou todos os aplicativos que recebem os dados. Você deseja que esses dados OOB sejam tratados como dados de prioridade mais alta do que qualquer dado normal que você possa enviar. Se os dados OOB que você deseja enviar forem um byte, você pode usar a função selecionar para procurar dados OOB. Você pode usar a função recv para ler os dados. No entanto, no protocolo de controle de transmissão (TCP), o bloco de dados OOB é sempre um byte. Portanto, se você enviar dados OOB de vários bytes, apenas o último byte dos dados OOB será recuperado. Os dados restantes são tratados como dados normais. Este artigo usa código de exemplo para descrever como enviar dados OOB de vários bytes usando o Microsoft Windows Socket (Winsock).
NESTA TAREFA
INTRODUÇÃO
Este artigo descreve como enviar dados fora de banda de vários bytes (chars) usando o Winsock. Os aplicativos de exemplo são criados no Microsoft Visual C++. Como esse mecanismo OOB Não tem suporte direto no nível de soquete do Microsoft Windows, você deve implementar esse mecanismo OOB no nível do aplicativo. Isso não é dados OOB verdadeiros. Neste aplicativo de exemplo, você cria dois soquetes no lado do cliente, também conhecido como o lado do remetente, para enviar dados OOB e dados normais. No lado do servidor, também conhecido como o lado do receptor, você usa dois soquetes para processar os dados em dois threads. Um thread é para dados OOB. O outro thread é para dados normais. Para simular o mecanismo OOB, você deve sincronizar esses dois threads. Verifique se o thread que processa os dados OOB tem prioridade mais alta do que o thread que processa dados normais.Observação Este aplicativo de exemplo descreve uma maneira de enviar dados OOB de vários bytes. Talvez seja necessário revisar o código para que o código funcione em seu ambiente.voltar ao início
Criar um aplicativo cliente
-
Crie um aplicativo cliente. Neste aplicativo cliente, o seguinte exemplo de código descreve como criar dois soquetes:
SOCKET myOOBSocket = INVALID_SOCKET;SOCKET myNormalSocket = INVALID_SOCKET;myOOBSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);myNormalSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
A variável myOOBSocket é usada para enviar dados OOB. A variável myNormalSocket é usada para enviar dados normais.
-
Como os dados que a variável myOOBSocket envia não são dados OOB verdadeiros, você deve ter uma maneira de saber o servidor que tipo de dados o soquete deve enviar antes de começar a enviar dados. O código de exemplo a seguir descreve como enviar um caractere inicial para notificar o servidor sobre o tipo de dados que serão enviados. Use "U" para dados OOB e use "N" para dados normais.
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.
-
O código de exemplo a seguir descreve como fazer o aplicativo cliente monitorar a entrada do usuário. O aplicativo cliente envia caracteres em maiúsculas como dados OOB. Duplicar cada caractere de entrada para compor uma cadeia de caracteres de vários bytes.
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);}...}
Exiba o código de exemplo do aplicativo cliente, myclient. cppdevolta para o início
Criar um aplicativo de servidor
-
No lado do servidor, o seguinte exemplo de código descreve como criar um soquete de escuta para monitorar a porta usada para comunicação:
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.
-
O código de exemplo a seguir descreve como chamar a função Accept para fazer com que o soquete de escuta Espere por tentativas de conexão de entrada:
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....}
-
Para simular o mecanismo OOB, você deve sincronizar esses dois threads. Neste aplicativo de exemplo, use um objeto de evento global para fazer isso. Coloque a instrução a seguir na parte superior do arquivo.
//Create a manual-reset event object. hRecvEvent=CreateEvent(NULL,TRUE,TRUE,"RecvEvent");
-
Crie o manipulador OOB que é executado por um thread separado. Nesse manipulador, use a função selecionar para detectar quando os dados chegam. Quando os dados chegarem, siga estas etapas:
-
Chame a função ResetEvent para definir o objeto de evento hRecvEvent como o estado não sinalizado.
ResetEvent(hRecvEvent);
Isso bloqueia o manipulador de dados normal que é executado em outro thread.
-
Chame a função revC para ler os dados.
recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
-
Como mais dados podem ser enviados do que o buffer pode conter, chame a função recv novamente, juntamente com o sinalizador MSG_PEEK , para determinar se os dados estão disponíveis no cabo.
recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
Use um dos seguintes métodos, dependendo se há dados pendentes para serem lidos ou não:
-
Se não houver dados pendentes a serem lidos, chame a função SetEvent para definir o objeto de evento especificado como o estado sinalizado.
SetEvent(hRecvEvent);
Ao fazer isso, o thread do manipulador de dados normal pode continuar. O thread do manipulador de dados normal aguarda o objeto de evento hRecvEvent antes que o thread do manipulador de dados normal seja reiniciado.
-
Se os dados ainda estiverem pendentes para serem lidos, chame a função recv novamente para ler os dados restantes. Em seguida, procure novamente os dados pendentes.
-
Repita as etapas 4a até 4C até que todos os dados pendentes tenham sido lidos.
-
-
Crie o manipulador de dados normal. Esse manipulador é semelhante ao manipulador de dados OOB, exceto que o manipulador de dados normal chama a função WaitForSingleObject quando o manipulador de dados normal detecta que os dados chegaram.
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.
Exiba o código de exemplo para o aplicativo do servidor, meuservidor. cppdevolta para o início
Exibir o código de exemplo do aplicativo cliente, myclient. cpp
Observação Você deve incluir uma referência ao arquivo de biblioteca do Winsock, Ws2_32. lib, para fazer o código compilar.
#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;}
Exibir o código de exemplo do aplicativo servidor, meuservidor. cpp
Observação Você deve incluir uma referência ao arquivo de biblioteca do Winsock, Ws2_32. lib, para fazer o código compilar.
#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;}
Testar o exemplo
-
Em um prompt de comando, digite meuservidor. exe 4444 para iniciar o servidor na porta 4444. o aplicativo do servidor exibe a mensagem a seguir e, em seguida, aguarda os clientes:
O servidor está pronto!
-
Em uma janela de prompt de comando diferente, digiteMyclient IPAddress 4444 para iniciar o cliente. Observação O IPAddress do espaço reservado é um espaço reservado para o endereço IP do servidor.
-
O servidor exibe uma mensagem semelhante à seguinte:
Waiting.Waiting*..*Ao receber a mensagem anterior, você deve digitar aBcDeFgHi no cliente em 10 segundos antes de o servidor continuar.
Após cerca de 10 segundos, o servidor exibe o seguinte:
[OOB]: BBBBBBBBBB [OOB]: DDDDDDDDDD [OOB]: FFFFFFFFFF [OOB]: HHHHHHHHHH [normal]: aaaaaaaaaa [normal]: cccccccccc [normal]: eeeeeeeeee [normal]: gggggggggg [normal]: IIIIIIIIIIvoltar ao início
Referências
Para obter informações adicionais, acesse os seguintes sites da Microsoft Developer Network (MSDN):
Dados fora de banda independentes de protocolohttp://msdn2.Microsoft.com/en-us/library/ms740102.aspxfunções de Winsockhttp://msdn2.Microsoft.com/en-us/library/ms741394.aspxPara obter mais informações, clique no número de artigo a seguir para ler o conteúdo na Base de Dados de Conhecimento Microsoft:
331756 A função ioctlsocket não pode detectar dados fora de banda embutidosretornar ao início