요약
때로는 수신기에 긴급 대역외 (OOB) 데이터를 보내야 합니다. 수신기는 사용자 또는 데이터를 수신 하는 모든 응용 프로그램입니다. 이 OOB 데이터를 사용자가 보낼 수 있는 일반 데이터 보다 높은 우선 순위의 데이터로 처리 하려는 경우 보내려는 OOB 데이터가 1 바이트 이면 select 함수를 사용 하 여 oob 데이터를 찾을 수 있습니다. Recv 함수를 사용 하 여 데이터를 읽을 수 있습니다. 그러나 TCP (전송 제어 프로토콜)에서 OOB 데이터 블록은 항상 1 바이트입니다. 따라서 다중 바이트 OOB 데이터를 보내는 경우 OOB 데이터의 마지막 바이트만 검색 됩니다. 나머지 데이터는 일반 데이터 처럼 처리 됩니다. 이 문서에서는 샘플 코드를 사용 하 여 Winsock (Microsoft Windows 소켓)을 사용 하 여 다중 바이트 OOB 데이터를 보내는 방법을 설명 합니다.
문서 내용
소개
이 문서에서는 Winsock을 사용 하 여 여러 바이트 (문자) 대역외 데이터를 보내는 방법에 대해 설명 합니다. 샘플 응용 프로그램은 Microsoft Visual c + +에서 만들어집니다. 이 OOB 메커니즘은 Microsoft Windows 소켓 수준에서 직접 지원 되지 않으므로 응용 프로그램 수준에서이 OOB 메커니즘을 구현 해야 합니다. 이것은 진정한 OOB 데이터는 아닙니다. 이 샘플 응용 프로그램에서는 클라이언트 쪽에 두 개의 소켓을 만들어 보낸 사람 측이 라고도 하며, OOB 데이터와 일반 데이터를 보낼 수 있습니다. 서버 쪽에서 수신기 측이 라고도 하는 두 개의 소켓을 사용 하 여 두 개의 스레드에서 데이터를 처리 합니다. 한 스레드는 OOB 데이터에 대 한 것입니다. 다른 스레드는 일반 데이터에 대 한 것입니다. 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 데이터가 아니기 때문에 데이터 보내기를 시작 하기 전에 소켓에서 전송 해야 하는 데이터의 종류를 서버에 알리는 방법을 알아야 합니다. 다음 샘플 코드에서는 초기 문자를 전송 하 여 전송 될 데이터의 종류에 대해 서버에 알리는 방법을 설명 합니다. OOB 데이터에 "U"를 사용 하 고 일반 데이터에 "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 함수를 호출 하 여 h vevent 이벤트 개체를 신호 되지 않은 상태로 설정 합니다.
ResetEvent(hRecvEvent);
이는 다른 스레드에서 실행 되는 일반 데이터 처리기를 차단 합니다.
-
Revc 함수를 호출 하 여 데이터를 읽습니다.
recv(remoteSocket,(char *)&buffer,sizeof(buffer) - 1,0);
-
버퍼가 보유할 수 있는 것 보다 더 많은 데이터가 전송 될 수 있으므로, MSG_PEEK 플래그와 함께 recv 함수를 다시 호출 하 여 연결 된 데이터를 사용할 수 있는지 여부를 확인 합니다.
recv(remoteSocket,(char *)&buffer,sizeof(buffer)-1,MSG_PEEK);
읽을 수 있는 데이터가 있는지 여부에 따라 다음 방법 중 하나를 사용 합니다.
-
읽을 데이터가 보류 되지 않은 경우 SetEvent 함수를 호출 하 여 지정 된 이벤트 개체를 신호 된 상태로 설정 합니다.
SetEvent(hRecvEvent);
이렇게 하면 일반 데이터 처리기 스레드를 다시 시작할 수 있습니다. 일반 데이터 처리기 스레드는 일반 데이터 처리기 스레드를 다시 시작 하기 전에 Hnormal vevent 이벤트 개체를 기다립니다.
-
데이터를 계속 읽을 수 있는 경우에는 recv 함수를 다시 호출 하 여 나머지 데이터를 읽습니다. 그런 다음 보류 중인 데이터를 다시 확인 합니다.
-
보류 중인 모든 데이터를 읽을 때까지 4a ~ 4c 단계를 반복 합니다.
-
-
일반 데이터 처리기를 만듭니다. 이 처리기는 일반 데이터 처리기가 데이터에 도달 했을 때 WaitForSingleObject 함수를 호출 한다는 점을 제외 하 고는 OOB 데이터 처리기와 유사 합니다.
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에 대 한 참조를 포함 해야 합니다.
#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
참고 코드를 컴파일하려면 Winsock 라이브러리 파일 Ws2_32에 대 한 참조를 포함 해야 합니다.
#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 4444 를 입력 하 여 포트 4444에서 서버를 시작 합니다. 서버 응용 프로그램에서 다음 메시지가 표시 된 다음 클라이언트를 기다립니다.
서버가 준비 되었습니다!
-
다른 명령 프롬프트 창에서Myclient IPAddress 4444 를 입력 하 여 클라이언트를 시작 합니다. 참고 자리 표시자 IPAddress 는 서버의 IP 주소에 대 한 자리 표시자입니다.
-
서버에 다음과 유사한 메시지가 표시 됩니다.
Waiting.Waiting*..*이전 메시지를 받으면 10 초 내에 클라이언트에 aBcDeFgHi 를 입력 하 고 서버를 계속 해야 합니다.
약 10 초 후에 서버에는 다음이 표시 됩니다.
[OOB]: BBBBBBB b [OOB]: DDDDDDDDDD [oob]: FFFFFFFFFF [OOB]: HHHHHHHHHH [Normal]: aaaaaaaaaa [normal]: cccccccccc [일반]: eeeeeeeeee ee: iiiiiiiiii맨 위로 돌아가기
참조
추가 정보를 보려면 다음 MSDN (Microsoft Developer Network) 웹 사이트를 방문 하세요.
프로토콜 독립 대역외 데이터Http://msdn2.microsoft.com/en-us/library/ms740102.aspxWinsock 기능http://msdn2.microsoft.com/en-us/library/ms741394.aspx추가 정보는 다음 문서 번호를 클릭 하 여 Microsoft 기술 자료 문서를 참조 하세요.