C++ 同时接收 2 个或更多 UDP 消息

Posted

技术标签:

【中文标题】C++ 同时接收 2 个或更多 UDP 消息【英文标题】:C++ Receiving 2 or more UDP Messages at same time 【发布时间】:2015-01-16 03:24:24 【问题描述】:

我正在尝试在 main.cxx 中接收 UDP 消息。我创建了一个 UDP 服务器方法 getUdp(char *buffer) 来在我的 while(true) 无限循环中侦听传入的 UDP 消息。

这是我面临的问题。此 UDP 服务器一次能够侦听一条消息。当两个或多个 UDP 消息同时到达时,它不会排队进入缓冲区。我想是因为socket每次在无限循环中调用方法,getUdp()方法打开一个socket,获取消息并关闭socket,导致服务器无法对消息进行排队。

如何调整此代码以接收 2 条或更多 UDP 消息? 感谢任何建议。

谢谢。

UdpServer.cpp

#include <stdio.h>
#include <iostream>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <winsock.h>
#include <time.h>

#include "UdpServer.h"
#include "stdafx.h"

using namespace std;

#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 4096

void getUDP(char *buffer);

void UDPServer::MyUDP::getUDP(char *buffer)

    WSADATA w;                          /* Used to open windows connection */
    int client_length;                  /* Length of client struct */
    int bytes_received;                 /* Bytes received from client */
    SOCKET sd;                          /* Socket descriptor of server */
    struct sockaddr_in server;          /* Information about the server */
    struct sockaddr_in client;          /* Information about the client */
    struct hostent *hp;                 /* Information about this computer */
    char host_name[256];                /* Name of the server */
    time_t current_time;                /* Current time */

    /* Open windows connection */
    if (WSAStartup(0x0101, &w) != 0)
    
        fprintf(stderr, "Could not open Windows connection.\n");
    

    /* Open a datagram socket */
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd == INVALID_SOCKET)
    
        fprintf(stderr, "Could not create socket.\n");
        WSACleanup();
    

    /* Clear out server struct */
    memset((void *)&server, '\0', sizeof(struct sockaddr_in));

    /* Set family and port */
    server.sin_family = AF_INET;
    server.sin_port = htons(11000);

    /* Set address automatically if desired */
    /* Get host name of this computer */
    gethostname(host_name, sizeof(host_name));
    hp = gethostbyname(host_name);

    /* Check for NULL pointer */
    if (hp == NULL)
    
        fprintf(stderr, "Could not get host name.\n");
        closesocket(sd);
        WSACleanup();
    

    unsigned int a = 127;
    unsigned int b = 0;
    unsigned int c = 0;
    unsigned int d = 1;

    /* Assign the address */
    server.sin_addr.S_un.S_un_b.s_b1 = a;
    server.sin_addr.S_un.S_un_b.s_b2 = b;
    server.sin_addr.S_un.S_un_b.s_b3 = c;
    server.sin_addr.S_un.S_un_b.s_b4 = d;

    /* Bind address to socket */
    if (bind(sd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) == -1)
    
        fprintf(stderr, "Could not bind name to socket.\n");
        closesocket(sd);
        WSACleanup();
    

    /* Print out server information */
    printf("Server running on %u.%u.%u.%u\n", (unsigned char)server.sin_addr.S_un.S_un_b.s_b1,
                                                (unsigned char)server.sin_addr.S_un.S_un_b.s_b2,
                                                (unsigned char)server.sin_addr.S_un.S_un_b.s_b3,
                                                (unsigned char)server.sin_addr.S_un.S_un_b.s_b4);
    printf("Press CTRL + C to quit\n");

    /* Loop and get data from clients */
    client_length = (int)sizeof(struct sockaddr_in);

    /* Receive bytes from client */
    bytes_received = recvfrom(sd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client, &client_length);

    if (bytes_received < 0)
    
        fprintf(stderr, "Could not receive datagram.\n");
        closesocket(sd);
        WSACleanup();
    

    current_time = time(NULL);
    closesocket(sd);
    WSACleanup();

main.cxx

int main(int argc, char** argv)

    while(true)
    
        //! wait for UDP message
        UDPServer::MyUDP myudp;
        myudp.getUDP(buffer);

        if(buffer[0] != 0)
        
            string udpMsg(buffer);

            if(udpMsg == "ProcessThisMessage")
            
                memset(&buffer[0], 0, sizeof(buffer));

                cout << "UDP Message: " + udpMsg;
            

            ...
        
    

【问题讨论】:

打开套接字一次,然后无限循环读取。当你关闭套接字时,你告诉操作系统你不再对接收数据感兴趣了。 请注意:您确实在错误路径上付出了很多努力,但在任何WSACleanup() 调用之后,您实际上并没有returnexit()!因此,您将继续使用未初始化的 Winsock API 和关闭的套接字进行网络调用! 【参考方案1】:

我认为这是因为每次调用方法时都会使用套接字 在无限循环中,getUdp() 方法打开一个套接字,获取 消息并关闭套接字,导致服务器无法 将消息排队。

你的直觉是正确的。当您将 UDP 套接字绑定到端口时,网络堆栈将为您缓冲(有限数量)传入的 UDP 数据包,因此(假设您以相对及时的方式调用 recv()),不应收到传入的数据包丢失。但是,当您 closesocket() 套接字时,该缓冲区被释放,当然,在没有套接字绑定到 UDP 端口并且接收到 UDP 数据包的时候,传入的 UDP 数据包将被简单地丢弃(即根本不缓冲) 因为没有套接字绑定到该端口。

如何调整此代码以接收 2 个或更多 UDP 消息? 感谢任何建议。

至少从概念上讲,您需要将 getUdp() 方法分成三个独立的部分:一个 Setup() 部分,您在程序启动时调用一次,一个 Receive() 部分(仅包含 recv () 调用),您可以调用任意多次,以接收下一个数据包,最后是 Cleanup() 部分,它关闭套接字并关闭 TCP 堆栈(仅当您的程序大约退出)。这样,UDP 套接字在您的程序运行的整个过程中保持有效并绑定到端口,以便操作系统可靠地缓冲传入的 UDP 数据包,以通过 recv() 提供给您的程序。

【讨论】:

感谢您的建议。是的,我已将 getUdp() 方法分成三个独立的部分。我之前调用过的 Setup() 部分,内部的 Receive() 部分和循环后的 Cleanup()。效果很好!现在,我可以接收到多条 UDP 消息了。谢谢!

以上是关于C++ 同时接收 2 个或更多 UDP 消息的主要内容,如果未能解决你的问题,请参考以下文章

如何接收 2 个或更多不同的异常?

发送UDP广播,接收多条消息

QT 网络编程二(UDP版本)

接收 UDP 消息

UDP C++接收

如何在 Raspberry Pi 上使用 C++ 将接收到的 UDP 音频数据正确写入 ALSA