【问题标题】:How do I receive udp packets with winsock in c++?如何在 C++ 中使用 winsock 接收 udp 数据包?
【发布时间】:2013-02-02 19:20:51
【问题描述】:

为了让我了解 udp 套接字,我尝试将代码从本教程页面 http://www.linuxhowtos.org/C_C++/socket.htm 移植到 winsock(如果重要,可以在 win 8 上运行)。 [以下直接链接]

代码当前编译并运行,但我从未收到数据包,两个程序都只是等待并阻塞 recvfrom()。我一直遇到同样的问题(例如,使用包含 recvfrom() 调用的代码https://stackoverflow.com/a/679529/873217 的修改版本。并且使用此代码C++ problem with Datagram (UDP)winsocket to sendto and recvfrom on the same socket through the loopback adapter 进行建议的更改)。我想我犯了一些简单而根本的错误;但我无法自己找到它。希望有更多经验的人可以为我解决这个问题。谢谢。

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

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

我目前的尝试:

都链接到 libws2_32.a

客户端代码:

#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);
}

服务器代码:

#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);
}

【问题讨论】:

  • 在这里找到了一个工作示例:codeproject.com/articles/11740/…HTH
  • 谢谢,我去看看。
  • Wireshark 有时可以成为调试此类网络代码问题的有用工具,因为它会显示客户端和服务器之间交换了哪些数据包(如果有)。这应该会告诉你应该首先调试哪个程序。
  • 首先让它在 'localhost' (127.0.0.1) 上工作。
  • 谢谢,我昨天下载了wireshark。我今天会尝试使用它。

标签: c++ sockets udp winsock


【解决方案1】:

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

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

Network.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;
};

服务器

#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();
    }
}

客户

#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;
}

【讨论】:

  • 为什么不使用函数SendTo(const std::string&amp; address, unsigned short port, ...函数Sendto(sockaddr_in&amp; address, ...)
  • 很抱歉,我发现错误时没有更新我的问题。不幸的是,我现在还没有能力进行测试。如果您可以确认您的代码有效,而我的代码不能在您的机器上运行,我会接受您的回答。似乎端口转发是我问题的一部分。 (虽然现在我想起来了,我不确定这是否适用于 localhost)
【解决方案2】:

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

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

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

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

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

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

该代码可以在 Windows 中运行。我猜在 Linux 中它会是类似的。

【讨论】:

  • 感谢您的回复,我目前正在使用 sock=socket(AF_INET, SOCK_DGRAM, 0); “如果指定值 0,则调用者不希望指定协议,服务提供商将选择要使用的协议。”我尝试“IPPROTO_UDP”并得到相同的结果。
猜你喜欢
  • 2011-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多