使用getaddrinfo和bind发送具有固定源端口号的UDP数据包

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用getaddrinfo和bind发送具有固定源端口号的UDP数据包相关的知识,希望对你有一定的参考价值。

使用BJ的talker.c代码作为模板:http://beej.us/guide/bgnet/examples/talker.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define SERVERPORT "4950"    // the port users will be connecting to

int main(int argc, char *argv[])
{
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    int rv;
    int numbytes;
    struct sockaddr_storage their_addr;
    socklen_t addr_len;
    addr_len = sizeof their_addr;

    if (argc != 3) {
        fprintf(stderr,"usage: talker hostname message
");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;

    if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s
", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and make a socket
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("talker: socket");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "talker: failed to create socket
");
        return 2;
    }

    if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0,
             p->ai_addr, p->ai_addrlen)) == -1) {
        perror("talker: sendto");
        exit(1);
    }

    freeaddrinfo(servinfo);

    printf("talker: sent %d bytes to %s
", numbytes, argv[1]);


//============== Added Code for recvfrom() (pseudocode-ish) =============


    if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN , 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) 
    {
        close(sockfd);
        perror("talker: recvfrom");    
        exit(1);
    }

    close(sockfd);

    printf("Got packet
");

//============== End Added Code for recvfrom() =============


    close(sockfd);

    return 0;
}

我有一个要求,即与服务器通信的客户端UDP进程必须使用固定的,已知的source端口号。在这种情况下,假设它是SERVERPORT(4950)。然后服务器响应该端口号。是的,这很不寻常,因为大多数服务器都会响应系统分配给发件人的短暂端口号。

在使用sendto()发送数据包后,我使用recvfrom()听取响应。这是我在上面的例子中添加的(伪)代码。

我在网上的所有搜索都指向使用bind(),但该代码通常在服务器端。我还没有找到使用现代getaddrinfo()方法在客户端绑定的方法。我试图在bind()设置之后添加一个socket(),但这不起作用,因为p是一个服务器端结构(从使用服务器IP地址的提示结构派生),我得到一个绑定错误:

Error 99 (Cannot assign requested address)

代码添加:

bind(sockfd, p->ai_addr, p->ai_addrlen)

我希望以适用于IPv4和IPv6的方式执行此操作。

我已经看到了其他示例,其中本地/源sockaddr_in结构填充了客户端的信息,并在绑定中使用,但这些是IPv4或IPv6特定的。

有人可以告诉我如何使用固定源端口号正确更新qazxsw poi代码到qazxsw poi和qazxsw poi UDP服务器?假设服务器是不可变的。

答案

然后服务器响应该端口号。是的,这很不寻常

这没什么不寻常的。这就是大多数UDP服务器的工作方式。他们总是回应发件人的端口。他们没有关于该端口是固定的还是短暂的概念,而是由发送者决定。除非特定协议规定将响应发送到不同的端口,这不常见。

我在网上的所有搜索都指向使用talker.c

正确,这就是你在这种情况下所需要的。

但该代码通常在服务器端。

没有什么能阻止客户使用sendto()

我还没有找到使用现代recvfrom()方法在客户端绑定的方法。

它与服务器端完全相同,除了你必须绑定到特定的IP地址,你不能像服务器套接字那样绑定到bind()bind()

我试图在getaddrinfo()设置之后添加一个0.0.0.0,但这不起作用

是的,它确实。问题是您使用SAME IP地址进行绑定和发送,这是行不通的。您需要绑定到CLIENT的IP地址,然后发送到SERVER的IP地址。

因为::0是服务器端结构(源自使用服务器IP地址的bind()结构)

你在滥用socket()。你不能p客户端套接字到服务器的IP地址(你需要使用hints代替)。您需要将客户端套接字p到客户端计算机本地的IP地址。就像你必须bind()服务器套接字到服务器机器本地的IP地址。

请记住,套接字与一对IP地址相关联。 connect()建立套接字的LOCAL IP地址。 bind()建立套接字的REMOTE IP地址。

我希望以适用于IPv4和IPv6的方式执行此操作。

您无法为这两种协议创建单个客户端套接字。每个协议都需要单独的套接字(在服务器端,如果您的平台支持双栈套接字,则可以为两个协议创建单个套接字)。

我已经看到了其他一些示例,其中本地/源bind()结构填充了客户端的信息,并在绑定中使用,但那些是IPv4或IPv6特定的。

是的,因为您将使用EITHER IPv4或IPv6发送数据包,所以不能同时使用两种协议发送数据包(双栈插槽可以接收来自任一协议的数据包)。

有人可以告诉我如何使用固定源端口号正确更新qazxsw poi代码到qazxsw poi和qazxsw poi UDP服务器。假设服务器是不可变的

尝试这样的事情:

bind()

以上是关于使用getaddrinfo和bind发送具有固定源端口号的UDP数据包的主要内容,如果未能解决你的问题,请参考以下文章

Rails 邮件发送:getaddrinfo:名称或服务未知

Python 发送邮件 socket.gaierror: [Errno 11004] getaddrinfo failed

Postman发送请求失败报错“Error: getaddrinfo ENOTFOUND xxx.xxx.xxx.xxx“

python发送udp报文并修改源ip

fsockopen():php_network_getaddresses:getaddrinfo失败:名称或服务未知

PHP 发送纯文本邮件并强制使用固定宽度的字符集