【问题标题】:UDP client not receiving UDP server messageUDP 客户端未收到 UDP 服务器消息
【发布时间】:2015-09-09 04:39:01
【问题描述】:

海友我是新手学习winsock2。以下是我的 udp 服务器和客户端程序。

我这个程序客户端不知道服务器的IP地址,但只知道端口。但是服务器会在整个网络上广播一条消息。

当客户端收到消息时,它会追溯服务器的 ip 并连接到它。

我的服务器和客户端在编译时都没有显示错误。

但是在执行客户端的 recvfrom() 语句时显示错误 以下是我的代码的一部分。

服务器代码:

#include "stdafx.h"
#include<stdio.h>
#include<WinSock2.h>
#pragma comment (lib,"ws2_32.lib")

DWORD WINAPI UDPCONN(LPVOID x)
{
    SOCKET s=(SOCKET)x;
    char bro[200]="I am SERVER";
    struct sockaddr_in hum;
    hum.sin_family=AF_INET;
    hum.sin_addr.s_addr=INADDR_BROADCAST;
    hum.sin_port=htons(8888);
    while (1)
    {
        if(sendto(s,bro,sizeof(bro),0,(struct sockaddr *)&hum,sizeof(hum))==SOCKET_ERROR)
        {
            printf("\nBroadcast failed ERROR CODE : %d\n",WSAGetLastError());
            exit(1);
        }
    }
 }

int _tmain(int argc, _TCHAR* argv[])
{
    SOCKET s;
    struct sockaddr_in hum;
    int opt=1;

    //Initializing winsock2
    WSADATA wsa;
    if((WSAStartup(MAKEWORD(2,2),&wsa))!=0)
    {
        printf("\nWinsock Not initialized ERROR CODE : %d\n",WSAGetLastError());
        return 1;
    }

    //UDP Socket Creation
    if((s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==INVALID_SOCKET)
    {
        printf("\nUDP Socket not created ERROR CODE : %d\n",WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //socket defenition
    hum.sin_family=AF_INET;
    hum.sin_addr.s_addr=INADDR_ANY;
    hum.sin_port=htons(8888);

    //Broadcast permission
    if((setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char *)&opt,sizeof(opt)))<0)
    {
        printf("\nBroadcast permissions failed ERROR CODE : %d\n",WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //UDP SOCKET Binding 
    if((bind(s,(sockaddr *)&hum,sizeof(hum)))==SOCKET_ERROR)
    {
        printf("\nUDP socket binding failed ERROR CODE :%d\n",WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //UDP connection thread
    DWORD th;
    CreateThread(NULL,0,UDPCONN,(LPVOID)s,0,&th);

客户代码:

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    //UDP Data
    int slen;
    char message[300];
    int opt=1;
    SOCKET s;
    struct sockaddr_in sent;

    //Initializing winsock
    WSADATA wsa;
    if((WSAStartup(MAKEWORD(2,2),&wsa))!=0)
    {
        printf("\nFailed Initializing Winsock EROR CODE : %d\n",WSAGetLastError());
        return 1;
    }

    //UDP Socket creation
    if((s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))== SOCKET_ERROR)
    {
        printf("\nUDP socket creation failed ERROR CODE :%d\n",WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //UDP Broadcast permissions
    if((setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char *)&opt,sizeof(opt)))<0)
    {
        printf("\nERROR in broadcasting ERROR CODE : %d \n",WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //UDP socket definition
    sent.sin_family=AF_INET;
    sent.sin_addr.s_addr=INADDR_ANY;
    sent.sin_port=htons(8888);

    //UDP Receiving broadcasted data
    slen=sizeof(sent);
    //fflush(stdout);
    memset(message,'\0',300);
    if((recvfrom(s,message,sizeof(message),0,(struct sockaddr *)&sent,&slen))<0)
    {
        printf("\nUDP Broadcast not received ERROR CODE : %d\n",WSAGetLastError());
        WSACleanup();
        return 1;
    }
    puts("\nGot server broadcast\n");
    puts("\nTracing server ip\n");
    getpeername(s,(sockaddr *)&sent,&slen);

    ...
}

这里我的客户抛出一个错误

UDP Broadcast not received ERROR CODE : 10022

错误代码10022的说明:无效参数。

提供了一些无效参数(例如,为 setsockopt 函数指定了无效级别)。在某些情况下,它还指代套接字的当前状态——例如,在未侦听的套接字上调用接受。

根据错误描述,recvfrom() 函数中的参数之一无效。但我找不到无效的论点。

请帮我清除错误。

【问题讨论】:

  • 这是 setsockopt() 函数的语法:int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 这不是作为第 5 个参数传递给 setsockopt 的语法

标签: c udp winsock winsock2


【解决方案1】:

如果您阅读了recvfrom() documentation,它会准确地告诉您为什么您会在客户端代码中收到错误:

参数

[输入]
标识绑定套接字的描述符。

...

WSAEINVAL
套接字未绑定bind,或指定了未知标志,或为启用 SO_OOBINLINE 的套接字指定了 MSG_OOB,或(对于字节流样式仅限套接字)len 为零或负数。

在调用recvfrom() 之前,您没有在套接字上调用bind()。更重要的是,当客户端和服务器在同一台机器上运行时,您不能bind() 使用相同的 IP/端口。更改您广播到的端口,然后让客户端绑定到该端口。

另外,recvfrom() 的最后两个参数为您提供数据报发送方的 IP/端口。它们不是用于指定要在其上读取数据报的网络适配器。在这种情况下,您不需要使用getpeername()

您的代码还有一些其他问题:

在客户端,socket() 在失败时返回INVALID_SOCKET,而不是SOCKET_ERROR

在服务器端,你真的不应该使用INADDR_BROADCAST。您应该 bind() 将服务器套接字连接到特定的网络适配器,然后 sendto() 其特定的子网广播 IP,您可以使用 GetAdaptersInfo()GetAdaptersAddresses() 计算。

并且在双方,如果WSAStartup() 失败,则不能使用WSAGetLastError(),这就是WSAStartup() 直接返回错误代码的原因。而且你在调用WSACleanup()之前没有关闭套接字;

试试这个:

服务器代码:

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment (lib,"ws2_32.lib")

DWORD WINAPI UDPCONN(LPVOID x)
{
    SOCKET s = (SOCKET)x;
    char bro[200] = "I am SERVER";
    struct sockaddr_in hum;

    memset(&hum, 0, sizeof(addr));
    hum.sin_family = AF_INET;
    hum.sin_addr.s_addr = INADDR_BROADCAST; // TODO: replace with subnet broadcast IP
    hum.sin_port = htons(8887);

    while (1)
    {
        if (sendto(s, bro, sizeof(bro), 0, (struct sockaddr *)&hum, sizeof(hum)) == SOCKET_ERROR)
        {
            printf("\nBroadcast failed ERROR CODE : %d\n", WSAGetLastError());
            return 1;
        }
    }

    return 0;
 }

int _tmain(int argc, _TCHAR* argv[])
{
    SOCKET s;
    struct sockaddr_in hum;
    int err, opt=1;

    //Initializing winsock2
    WSADATA wsa;
    err = WSAStartup(MAKEWORD(2,2), &wsa);
    if (err != 0)
    {
        printf("\nWinsock Not initialized ERROR CODE : %d\n", err);
        return 1;
    }

    //UDP Socket Creation
    s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s == INVALID_SOCKET)
    {
        printf("\nUDP Socket not created ERROR CODE : %d\n", WSAGetLastError());
        WSACleanup();
        return 1;
    }

    //socket defenition
    memset(&hum, 0, sizeof(hum));
    hum.sin_family = AF_INET;
    hum.sin_addr.s_addr = INADDR_ANY; // TODO: replace with a specific NIC IP
    hum.sin_port = htons(8888);

    //Broadcast permission
    if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt)) == SOCKET_ERROR)
    {
        printf("\nBroadcast permissions failed ERROR CODE : %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    //UDP SOCKET Binding 
    if (bind(s, (sockaddr *)&hum, sizeof(hum)) == SOCKET_ERROR)
    {
        printf("\nUDP socket binding failed ERROR CODE : %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    //UDP connection thread
    DWORD th;
    HANDLE hThread = CreateThread(NULL,0, UDPCONN, s, 0, &th);
    if (!hThread)
    {
        printf("\nUDP thread failed ERROR CODE : %u\n", GetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    // do other things, wait for thread to terminate ...

    DWORD exitCode = 0;
    GetExitCodeThread(hThread, &exitCode);
    CloseHandle(hThread);

    closesocket(s);
    WSACleanup();

    return exitCode;
}

客户代码:

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
    //UDP Data
    int addrlen, msglen;
    char message[300];
    int err, opt=1;
    SOCKET s;
    struct sockaddr_in hum, addr;

    //Initializing winsock
    WSADATA wsa;
    err = WSAStartup(MAKEWORD(2,2), &wsa);
    if (err != 0)
    {
        printf("\nFailed Initializing Winsock EROR CODE : %d\n", err);
        return 1;
    }

    //UDP Socket creation
    s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s == INVALID_SOCKET)
    {
        printf("\nUDP socket creation failed ERROR CODE : %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    //UDP Broadcast permissions
    if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt)) == SOCKET_ERROR)
    {
        printf("\nERROR in broadcasting ERROR CODE : %d \n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    //UDP socket definition
    memset(&hum, 0, sizeof(addr));
    hum.sin_family = AF_INET;
    hum.sin_addr.s_addr = INADDR_ANY;
    hum.sin_port = htons(8887);

    //UDP SOCKET Binding 
    if (bind(s, (sockaddr *)&hum, sizeof(hum)) == SOCKET_ERROR)
    {
        printf("\nUDP socket binding failed ERROR CODE : %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    //UDP Receiving broadcasted data
    addrlen = sizeof(addr);
    msglen = recvfrom(s, message, sizeof(message), 0, (struct sockaddr *)&addr, &addrlen);
    if (msglen == SOCKET_ERROR)
    {
        printf("\nUDP Broadcast not received ERROR CODE : %d\n", WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    printf("\nGot server broadcast\n");
    printf("\nServer ip: %s, port: %hu\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
    printf("\nMessage: %.*s\n", msglen, message);

    ...
}

【讨论】:

  • 请参考这篇文章的答案*.com/questions/32433403/…。我在检查并成功执行了 Visual Studios 2012 中的代码后写了这个答案。此外,根据 WSAGetLastError() 文档,即使 WSAStartup() 失败也可以调用它。同样在客户端,我尝试了 SOCKET_ERROR 和 INVALID_SOCKET。在这两种情况下,套接字创建都是成功的。但是谢谢你给了我关于绑定的想法。
  • 我刚刚尝试了绑定。由于我在同一台计算机上运行这两个程序,因此它在绑定中返回错误。你能想到其他方法吗????
  • 在您链接到的答案中,客户端在调用recvfrom() 之前调用sendto(),因此它正在执行隐式bind()。你的代码没有这样做,所以你需要一个明确的bind()。但是当它们在同一台机器上运行时,您不能将客户端和服务器绑定到相同的INADDR_ANY:8888 端口。将服务器更改为在不同端口上广播,并将客户端更改为绑定到该端口以进行接收。
  • 我完成了我的代码编辑。现在客户端运行没有任何错误。但客户端没有收到服务器广播。 recvfrom() 命令正在等待。我尝试单独使用 hum 或单独使用 addr,但结果仍然相同。
  • @KauthamKrishna 你还在发送给INADDR_BROADCAST吗?如果是这样,您需要将其更改为您发送的子网的广播地址。
【解决方案2】:

无论我尝试从服务器发送广播并从客户端接收广播的任何方法都无法有效地工作。

相反,我做了相反的事情,即从客户端发送广播以搜索服务器工作正常。

这是我修改后的代码。

服务器代码:

#include "stdafx.h"
#include<stdio.h>
#include<WinSock2.h>
#pragma comment (lib,"ws2_32.lib")

DWORD WINAPI UDPCONN(LPVOID x)
{

SOCKET s=(SOCKET)x;
struct sockaddr_in server;
int len;
char buf[500]=("I am server");

char message[500];
len=sizeof(server);

int opt=1;

if((s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==INVALID_SOCKET)
{
    printf("\nSocket creation failed. ERROR CODE : %d\n",WSAGetLastError());
    WSACleanup();
    ExitThread(NULL);
}

server.sin_family=AF_INET;
server.sin_addr.s_addr=INADDR_ANY;
server.sin_port=htons(8888);

if(setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char *)&opt,sizeof(opt))<0)
{
    printf("\nSetting broadcast failed : %d\n",WSAGetLastError());
    WSACleanup();
    ExitThread(NULL);
}
if((bind(s,(sockaddr *)&server,sizeof(server)))==SOCKET_ERROR)
{
    printf("\nBind failed. ERROR CODE : %d\n",WSAGetLastError());
    WSACleanup();
    ExitThread(NULL);
}

while(1)
{

    fflush(stdout);
     memset(message,'\0', 500);
    //try to receive some data, this is a blocking call
    if (recvfrom(s, message, 500, 0, (struct sockaddr *) &server,&len) == SOCKET_ERROR)
    {
        printf("recvfrom() failed with error code : %d" , WSAGetLastError());
        ExitThread(NULL);
    }

    if(sendto(s,buf,sizeof(buf),0,(struct sockaddr *)&server,sizeof(server))==SOCKET_ERROR)
    {
        printf("\nIP Broadcast failed ERROR code : %d\n",WSAGetLastError());
        WSACleanup();
        ExitThread(NULL);
    }
 }

}
int _tmain(int argc, _TCHAR* argv[])
{
SOCKET s;
//struct sockaddr_in hum;
int opt=1;
//Initializing winsock2
WSADATA wsa;
if((WSAStartup(MAKEWORD(2,2),&wsa))!=0)
{
    printf("\nWinsock Not initialized ERROR CODE : %d\n",WSAGetLastError());
    return 1;
}
s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

//UDP connection thread
DWORD th;
CreateThread(NULL,0,UDPCONN,(LPVOID)s,0,&th);
closesocket(s);

客户代码:

#include "stdafx.h"
#include <stdio.h>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

int _tmain(int argc, _TCHAR* argv[])
{
//UDP Data
int slen;
char message[600];
char buf[300]=("Hai server");
int opt=1;
SOCKET s;
struct sockaddr_in sent;

//TCP Connection data
SOCKET t;
struct sockaddr_in server;
char messag[300],server_reply[300];
int recv_size;


//Initializing winsock
WSADATA wsa;
if((WSAStartup(MAKEWORD(2,2),&wsa))!=0)
{
    printf("\nFailed Initializing Winsock EROR CODE : %d\n",WSAGetLastError());
    return 1;
}

//UDP Socket creation
if((s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))== INVALID_SOCKET)
{
    printf("\nUDP socket creation failed ERROR CODE :%d\n",WSAGetLastError());
    WSACleanup();
    return 1;
}

//UDP Broadcast permissions
if((setsockopt(s,SOL_SOCKET,SO_BROADCAST,(char *)&opt,sizeof(opt)))<0)
    {
        printf("\nERROR in broadcasting ERROR CODE : %d \n",WSAGetLastError());
        WSACleanup();
        return 1;
    }

//UDP socket definition
sent.sin_family=AF_INET;
sent.sin_addr.s_addr=INADDR_BROADCAST;
sent.sin_port=htons(8888);

if (sendto(s, buf, sizeof(buf) , 0 , (struct sockaddr *) &sent,sizeof(sent)) == SOCKET_ERROR)
    {
        printf("sendto() failed with error code : %d" , WSAGetLastError());
        exit(EXIT_FAILURE);
    }

//UDP Receiving broadcasted data
slen=sizeof(sent);
fflush(stdout);
memset(message,'\0',300);
if((recvfrom(s,message,sizeof(message),0,(struct sockaddr *)&sent,&slen))<0)
{
    printf("\nUDP Broadcast not received ERROR CODE : %d\n",WSAGetLastError());
    WSACleanup();
    return 1;
}
puts("\nGot server broadcast\n");
puts("\nTracing server ip\n");
getpeername(s,(sockaddr *)&sent,&slen);
closesocket(s);

这里的两个代码在 Visual Studios 2012 中都可以正常工作。 这也只是我整个大计划的一部分。但是这部分仍然可以独立工作。

所以请确保您在发布负面 cmets 之前编译并运行该程序。

【讨论】:

  • 在评论之前编译和链接。很难做到,因为很多代码都是特定于 Visual Studio 和 Windows 的。无论如何,在编译时,始终启用所有警告。然后,除其他外,编译器会向客户端发出至少两个警告:1) 未使用的参数:argc 2) 未使用的参数:argv[]。服务器代码的类似注意事项。然后我们注意到服务器代码在 main 函数的末尾缺少几行。因此,1) 代码没有完全编译,2) 服务器代码缺少一些行。
  • 是的,代码丢失了。正如我之前提到的,它只是我的大程序的一部分。你要我为你写一切吗?我提出这个程序只是为了告诉其他人还有另一种方法可以执行该程序,并且通过提供此代码,我为他们提供了编写代码的逻辑。关于 argc 和 argv 我也从未在整个程序中使用它们。这就是我提到 vs2012 的原因。我不是在这里开玩笑来提出一个不值得的答案。检查我的问题是否也是同样的方式。所以在评论之前请记住,* 是为了帮助编码人员。不要用勺子喂他们。