c++ - 如何在c ++中使用winsock接收udp数据包?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14665543/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:33:54  来源:igfitidea点击:

How do I receive udp packets with winsock in c++?

c++socketsudpwinsock

提问by Adam Phelps

As an attempt at wrapping my head around udp sockets I've tried to port the code from this tutorial page http://www.linuxhowtos.org/C_C++/socket.htmto winsock (running on win 8 if it matters). [direct links below]

作为试图围绕 udp 套接字的尝试,我尝试将本教程页面http://www.linuxhowtos.org/C_C++/socket.htm 中的代码移植到 winsock(如果重要,请在 win 8 上运行)。[下面的直接链接]

The code currently compiles and runs but I never receive a packet, both programs just wait and block on recvfrom(). I keep having this same issue (for example with a modified version of this code https://stackoverflow.com/a/679529/873217that includes a recvfrom() call. And with this code C++ problem with Datagram (UDP)winsocket to sendto and recvfrom on the same socket through the loopback adaptermaking the changes suggested). I imagine I'm making some simple and fundamental mistake; but I'm unable to locate it on my own. Hopefully someone with more experience can shine some light on this issue for me. Thanks.

代码当前编译并运行,但我从未收到数据包,两个程序都只是等待并阻塞 recvfrom()。我一直有同样的问题(例如,此代码的修改版本https://stackoverflow.com/a/679529/873217包含 recvfrom() 调用。使用此代码,Datagram (UDP)winsocket 的 C++ 问题sendto 和 recvfrom 通过环回适配器在同一个套接字上进行建议的更改)。我想我犯了一些简单而根本的错误;但我无法自己找到它。希望有更多经验的人可以为我解决这个问题。谢谢。

Additional notes:
I am running the server exe and then the client exe on the same computer. It is connected to the internet. And I have tried using a domain that resolves to my IP address as well. I have also tried disabling the firewall, though I have permitted both programs full access.

附加说明:
我在同一台计算机上运行服务器 exe,然后运行客户端 exe。它已连接到互联网。我也尝试使用解析为我的 IP 地址的域。我也尝试禁用防火墙,但我已允许两个程序完全访问。

direct links to original client and server code:
http://www.linuxhowtos.org/data/6/client_udp.c
http://www.linuxhowtos.org/data/6/server_udp.c

原始客户端和服务器代码的直接链接:
http://www.linuxhowtos.org/data/6/client_udp.c
http://www.linuxhowtos.org/data/6/server_udp.c

My current attempt:

我目前的尝试:

both linked to libws2_32.a

都链接到 libws2_32.a

client code:

客户端代码:

#include <stdio.h>
#include <winsock2.h>
#include <string.h>

void error(const char *);

int main()
{
    WSAData data;
    WSAStartup( MAKEWORD( 2, 2 ), &data );

    int sock, n;
    int length;
    struct sockaddr_in server, from;
    struct hostent *hp;
    char buffer[256];
    unsigned short serverPort = 27072;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) error("socket");
    server.sin_family = AF_INET;
    hp = gethostbyname("localhost");//have also tried my url
    if (hp==0) error("Unknown host");
    memmove((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length);
    server.sin_port = htons(serverPort);
    length = sizeof(struct sockaddr_in);
    printf("Please enter the message: ");
    memset(buffer, 0, 256);
    fgets(buffer, 255, stdin);
    n = sendto (sock, buffer, strlen(buffer), 0, (const struct sockaddr *)&server, length);
    if (n < 0) error("Sendto");
    printf("sent %i bytes\n", n);
    n = recvfrom(sock, buffer, 256, 0, (struct sockaddr *)&server, &length);
    if (n < 0) error("recvfrom");
    //write(1,"Got an ack: ", 12);
    //write(1, buffer, n);
    printf("Got msg: %s\n",buffer);

    closesocket(sock);
    WSACleanup();
    return(0);
}

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

server code:

服务器代码:

#include <stdio.h>
#include <winsock2.h>
#include <string.h>

void error(const char *);

int main()
{
    WSAData data;
    WSAStartup( MAKEWORD( 2, 2 ), &data );

    int sock, length, n;
    int fromlen;
    struct sockaddr_in server;
    struct sockaddr_in from;
    char buf[1024];
    unsigned short serverPort = 27072;
    sock=socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) error("Opening socket");
    length = sizeof(server);
    memset(&server, 0, length);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(serverPort);
    if (bind(sock,(struct sockaddr *)&server, length)<0) error ("binding");
    fromlen = sizeof(struct sockaddr_in);
    while(1)
    {
        n = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen);
        if (n<0) error("recvfrom");
        //write(1,"Received a datagram: ", 21);
        //write(1,buf,n);
        printf("Received a datagram: %s", buf);
        n = sendto(sock, "Got your message\n",17,0,(struct sockaddr *)&from,fromlen);
        if (n<0)error("sendto");
    }
    closesocket(sock);
    WSACleanup();
    return 0;
}


void error (const char *msg)
{
    perror(msg);
    exit(0);
}

回答by Peter R

The following UDP test application works correctly on my setup - Windows 7, VS 2013.

以下 UDP 测试应用程序在我的设置上正常工作 - Windows 7,VS 2013。

I created some light-weight wrapper classes to do resource management, and turns error codes into exceptions. This makes the client and server code easier to read. Please be sure to run the server first.

我创建了一些轻量级包装类来进行资源管理,并将错误代码转换为异常。这使得客户端和服务器代码更易于阅读。请务必先运行服务器。

Network.h

网络.h

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <system_error>
#include <string>
#include <iostream>

#pragma once

class WSASession
{
public:
    WSASession()
    {
        int ret = WSAStartup(MAKEWORD(2, 2), &data);
        if (ret != 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "WSAStartup Failed");
    }
    ~WSASession()
    {
        WSACleanup();
    }
private:
    WSAData data;
};

class UDPSocket
{
public:
    UDPSocket()
    {
        sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (sock == INVALID_SOCKET)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Error opening socket");
    }
    ~UDPSocket()
    {
        closesocket(sock);
    }

    void SendTo(const std::string& address, unsigned short port, const char* buffer, int len, int flags = 0)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = inet_addr(address.c_str());
        add.sin_port = htons(port);
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    void SendTo(sockaddr_in& address, const char* buffer, int len, int flags = 0)
    {
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&address), sizeof(address));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    sockaddr_in RecvFrom(char* buffer, int len, int flags = 0)
    {
        sockaddr_in from;
        int size = sizeof(from);
        int ret = recvfrom(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&from), &size);
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "recvfrom failed");

        // make the buffer zero terminated
        buffer[ret] = 0;
        return from;
    }
    void Bind(unsigned short port)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = htonl(INADDR_ANY);
        add.sin_port = htons(port);

        int ret = bind(sock, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Bind failed");
    }

private:
    SOCKET sock;
};

Server

服务器

#include "Network.h"

int main()
{
    try
    {
        WSASession Session;
        UDPSocket Socket;
        char buffer[100];

        Socket.Bind(100);
        while (1)
        {
            sockaddr_in add = Socket.RecvFrom(buffer, sizeof(buffer));

            std::string input(buffer);
            std::reverse(std::begin(input), std::end(input));
            Socket.SendTo(add, input.c_str(), input.size());
        }
    }
    catch (std::system_error& e)
    {
        std::cout << e.what();
    }
}

Client

客户

#include "Network.h"

int main()
{
    try
    {
        WSASession Session;
        UDPSocket Socket;
        std::string data = "hello world";
        char buffer[100];

        Socket.SendTo("127.0.0.1", 100, data.c_str(), data.size());
        Socket.RecvFrom(buffer, 100);
        std::cout << buffer;
    }
    catch (std::exception &ex)
    {
        std::cout << ex.what();
    }
    char c;
    std::cin >> c;
}

回答by Mr. Hell

Well, I'm not sure if winsock2.h works the same way in Windows and Linux, but, in Windows, when you create the socket, you must set the protocol you're using, either TCP or UDP:

好吧,我不确定 winsock2.h 在 Windows 和 Linux 中是否以相同的方式工作,但是,在 Windows 中,当您创建套接字时,您必须设置您正在使用的协议,TCP 或 UDP:

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

AF_INET = IPv4; SOCK_STREAM = Byte stream for TCP; IPPROTO_TCP = TCP Protocol.

AF_INET = IPv4;SOCK_STREAM = TCP 的字节流;IPPROTO_TCP = TCP 协议。

For UDP (which i've never used before), according to MSDN it would be:

对于 UDP(我以前从未使用过),根据 MSDN,它将是:

SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

SOCK_DGRAM = Byte stream for UDP; IPPROTO_UDP = UDP Protocol.

SOCK_DGRAM = UDP 的字节流;IPPROTO_UDP = UDP 协议。

That code would work in Windows. I guess in Linux it would be similar.

该代码将在 Windows 中工作。我想在 Linux 中它会是类似的。