在UDP方式进行网络传输时,为什么客户端只向服务端发送一次数据数据,而服务端的Recvfrom会不停地收到相同的数据。
我们在recvfrom input buffer sequence的数据后,如果将已经读取的数据清除掉?
Server:int main_UDP()
{
int nRet = 0;
int nLastError = 0;
char recvBuf[256] = {0};
SOCKET socket_server = {0};

WORD wVersionRequested;
WSADATA wsaData; // Ready To Use socket.
wVersionRequested = MAKEWORD( 2, 2 );
nRet = WSAStartup(WINSOCK_VERSION, &wsaData); 
if (nRet != 0)
{
printf("WSAStartup Fail nErr %d!\n", WSAGetLastError());
goto ERR_CATCH;
} /* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions later    */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we      */
/* requested.                                        */
 
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL.                                  */
WSACleanup( );
return 0; 
} // Create
socket_server = socket(AF_INET, SOCK_DGRAM, 0);

if (INVALID_SOCKET == socket_server)
{
nLastError = WSAGetLastError();
printf("socket %d, last Error %d!\n", socket_server, nLastError);
}

// Bind
sockaddr_in sock_add;
sock_add.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
sock_add.sin_family = AF_INET; 
sock_add.sin_port = htons(5698);
nRet = bind(socket_server, (sockaddr*)&sock_add, sizeof(sock_add));
if (nRet != 0)
{
printf("bind fail nRet %d, nErr %d\n", nRet, WSAGetLastError());
goto ERR_CATCH;
} {
while(1)
{
int nfrom = 0;
sockaddr scfrom = {0};
nfrom = sizeof(scfrom);
printf("Recvfrom before\n");

nRet = recvfrom(socket_server, recvBuf, 256, MSG_PEEK, &scfrom, &nfrom);
if (nRet > 0)
{
printf("recvBuf: %s, nRet: %d \n", recvBuf, nRet);
//break;
}
nRet = sendto(socket_server, "Hello Friend", strlen("Hello Friend")+1, 0, &scfrom, nfrom);
if (nRet > 0)
{
printf("sendto: %s, nRet: %d \n", "Hello Friend", nRet);
//break;
}
//Sleep(10000);
printf("Recvfrom end, nRet %d\n", nRet);
printf("---------------------\n");
memset((void*)recvBuf, 0, 256);
}
} printf("Done!");ERR_CATCH:
closesocket(socket_server);
WSACleanup( );
return 0;
}
Client:int main_UDP()
{
int nCount = 0;
int nRet = 0;
int nLastError = 0;
SOCKET socket_client = {0};
char bufrecv[256] = {0};

WORD wVersionRequested;
WSADATA wsaData;
SOCKADDR_IN addrSrv; // Ready To Use socket.
wVersionRequested = MAKEWORD( 2, 2 );
nRet = WSAStartup(WINSOCK_VERSION, &wsaData); 
if (nRet != 0)
{
printf("WSAStartup Fail nErr %d!\n", WSAGetLastError());
goto ERR_CATCH;
} /* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions later    */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we      */
/* requested.                                        */
 
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL.                                  */
WSACleanup( );
return 0; 
}
// Create
socket_client = socket(AF_INET, SOCK_DGRAM, 0);
if (INVALID_SOCKET == socket_client)
{
nLastError = WSAGetLastError();
printf("socket %d, last Error %d!\n", socket_client, nLastError);
}
{
//while(1)
{
int fromlen = 0;
SOCKADDR sckfrom = {0};
char szBufRecv[256] = {0};
addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.0.72");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(5698);

nRet = sendto(socket_client, "Name: Password:123456", strlen("Name: Password:123456"), 0, (SOCKADDR *)&addrSrv, sizeof(addrSrv));
if (nRet > 0)
printf("Send Successsful\n");


fromlen = sizeof(sckfrom);

//
nRet = recvfrom(socket_client, szBufRecv, 256, 0, &sckfrom, &fromlen);
if (nRet < 0)
{
printf("recvfrom nRet %d, nErr %d \n", nRet, WSAGetLastError());
}
printf("recvfrom: %s, nRet %d\n", szBufRecv, nRet);
//Sleep(6000);
printf("---------------------\n");
}

}
printf("\nbufrecv %s, nRet %d, nCount %d\n", bufrecv, nRet, nCount);
ERR_CATCH:
closesocket(socket_client);
WSACleanup( );

return 0;
}

解决方案 »

  1.   

    recvfrom(socket_server, recvBuf, 256, MSG_PEEK, &scfrom, &nfrom);用 MSG_PEEK 接收, 肯定会重复.... 可以看看MSDN
      

  2.   

    Hi zzz3265,
    从MSDN上的描述来看:只有两个选项MSG_PEEK、MSG_OOB,是否还有其他选项?he following table shows the values can be used to construct the flags parameter. The bitwise OR operator is applied to these values.Value  Description
    MSG_PEEKPeeks at the incoming data. The data is copied into the buffer but is not removed from the input queue, and the function returns the number of bytes currently pending to receive.MSG_OOBProcesses OOB data. (See section DECnet Out-Of-band data for a discussion of this topic.)
      

  3.   

    接受完了,要把beffer中的清除掉,防止重复接收
      

  4.   

    我将
    recvfrom(socket_server, recvBuf, 256, MSG_PEEK, &amp;scfrom, &amp;nfrom);
    修改为:
    recvfrom(socket_server, recvBuf, 256, 0, &amp;scfrom, &amp;nfrom);
    就可以正常接收了,
    但如果,我想使用MSG_PEEK参数,那么我就需要调用额外的函数将队列中的buffer清空,那我应该调用哪个函数实现情况这个Buffer内容呢?
      

  5.   

    我从‘春风吹过黄土地’的空间得到如下信息:
    http://hi.baidu.com/lcs_p/blog/item/9ddc85d1610b483a10df9b61.html如何清除socket缓冲区?!!(C/C++)
    首先说明一点,如果你曾经提出这样的问题,而且也没有解决,那么看完本文你一定会解决!        如何清除socket缓冲区?socket系统缓冲区是无法在应用程序中清除的,而且也系统没有提供这样的函数,socket系统缓冲区有系统内核维护,用户无法控制。        主要说的是TCP流。我当时的情况是:        我在客户端用sock建立TCP流套接字连接服务器,第一次向服务器发送http请求(http协议基于TCP流),并设置应用程序接收缓冲区为1024,因为我只想接收http头部信息,即只要包含头部信息就ok了,不想接受其他太多的数据。第一次recv()结果正常,接收到了包含http头部在内的1024的数据,我再次发送http请求,而后recv()结果得到的数据是上次请求没有接收完的数据!!!所以,我当时在网上搜索如何清空sock缓冲区!发现许多人都在问这个问题,但是好像都没有一个确定的答案!         提这样问题的人其实是自己编程有很大的问题(呵呵至少我是这样)。因为这样编写的思路根本就已经有点问题哦,当我们把recv()函数第三个参数设置为0时,我们每次从系统缓冲区拷贝多少数据到应用程序缓冲区,系统缓冲区才会删除多少数据,否则不会丢弃数据,直到缓冲区满而不再接收服务器响应数据。若不将recv()第三个参数设置为0的话,那么即使拷贝了系统缓冲区也不会删除所接收的数据。这是TCP协议决定的,我们改变不了。
      

  6.   

    方法一: 调用Shutdown和close, 再连接就清空了...
    方法二: 调用一次 recvfrom(socket_server, recvBuf, 256, 0, &amp;scfrom, &amp;nfrom); 就清空了.