首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用getaddrinfo和bind发送具有固定源端口号的UDP数据包

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

Stack Overflow用户
提问于 2018-09-07 19:00:08
回答 1查看 4.4K关注 0票数 1

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

代码语言:javascript
复制
#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\n");
        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\n", 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\n");
        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\n", 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\n");

//============== 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特定的。

请有人告诉我如何使用固定的源端口号正确地将talker.c代码更新到sendto()和UDP服务器recvfrom()?假设服务器是不可变的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-09-07 20:08:14

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

这没有什么不寻常的。这就是大多数UDP服务器的工作方式。他们总是响应发送者的端口。他们不知道该端口是固定的还是短暂的,即由发送方来决定。除非一个特定的协议要求将响应发送到另一个端口,这是不常见的。

我所有的在线搜索都指向使用bind()

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

但这段代码通常在服务器端。

没有什么可以阻止客户端使用bind()

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

它与服务器端完全相同,只是您必须绑定到特定的IP地址,您不能像使用服务器套接字那样绑定到0.0.0.0::0

我试图在bind()设置之后添加一个socket(),但这行不通

是的,确实如此。问题是您在绑定和发送时都使用相同的IP地址,这是行不通的。您需要绑定到客户端的IP地址,然后发送到服务器的IP地址。

因为p是服务器端结构(来自使用服务器IP地址的hints结构)

你在滥用p。您不能将客户端套接字bind()到服务器的IP地址(为此您需要使用connect() )。您需要将客户端套接字bind()到客户机机器的本地IP地址。就像您必须将服务器套接字bind()到服务器机器本地的IP地址一样。

记住,套接字与一对IP地址相关联。bind()建立套接字的本地IP地址。connect()建立套接字的远程IP地址。

我想以一种对IPv4和IPv6都有效的方式来实现这一点。

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

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

是的,因为您将使用IPv4或IPv6发送数据包,所以不能同时使用两个协议发送数据包(不过,双堆栈套接字可以接收来自任何协议的数据包)。

请有人告诉我如何使用固定的源端口号正确地将talker.c代码更新到sendto()和UDP服务器recvfrom()。假设服务器是不可变的

试着做这样的事情:

代码语言:javascript
复制
#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>
#include <stdbool.h>

#define LOCALPORT  "4950"     // the port users will be sending from
#define SERVERPORT "4950"    // the port users will be connecting to
#define MAXBUFLEN  65535

int main(int argc, char *argv[])
{
    int sockfd;
    struct addrinfo hints, *myinfo, *servinfo, *pserv, *plocal;
    int rv;
    int numbytes;
    char buf[MAXBUFLEN]; 
    char ipstr[INET6_ADDRSTRLEN];
    fd_set readfds;
    struct timeval tv;
    bool stop = false;

    if (argc < 3) {
        fprintf(stderr, "usage: talker destaddr message [localaddr]\n");
        return 1;
    }

    // get all of the server addresses
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_protocol = IPPROTO_UDP;

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

    // loop through all the server addresses
    for(pserv = servinfo; (pserv != NULL) && (!stop); pserv = pserv->ai_next) {

        memset(ipstr, 0, sizeof(ipstr));
        switch (pserv->ai_family)
        {
            case AF_INET:
                inet_ntop(AF_INET, &(((struct sockaddr_in*)pserv->ai_addr)->sin_addr), ipstr, INET_ADDRSTRLEN);
                break;
            case AF_INET6:
                inet_ntop(AF_INET6, &(((struct sockaddr_in6*)pserv->ai_addr)->sin6_addr), ipstr, INET6_ADDRSTRLEN);
                break;
        }

        printf("talker: trying to send message to %s\n", ipstr);

        // get all of the matching local addresses
        memset(&hints, 0, sizeof hints);
        hints.ai_family = pserv->ai_family;
        hints.ai_socktype = pserv->ai_socktype;
        hints.ai_protocol = pserv->ai_protocol;

        if ((rv = getaddrinfo(argc > 3 ? argv[3] : NULL, LOCALPORT, &hints, &myinfo)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            continue;
        }

        // loop through all the local addresses, sending the
        // message from each one until a reply is received
        for(plocal = myinfo; (plocal != NULL) && (!stop); plocal = plocal->ai_next) {

            if ((sockfd = socket(plocal->ai_family, plocal->ai_socktype, plocal->ai_protocol)) == -1) {
                perror("socket");
                continue;
            }

            memset(ipstr, 0, sizeof(ipstr));
            switch (plocal->ai_family)
            {
                case AF_INET:
                    inet_ntop(AF_INET, &(((struct sockaddr_in*)plocal->ai_addr)->sin_addr), ipstr, INET_ADDRSTRLEN);
                    break;
                case AF_INET6:
                    inet_ntop(AF_INET6, &(((struct sockaddr_in6*)plocal->ai_addr)->sin6_addr), ipstr, INET6_ADDRSTRLEN);
                    break;
            }

            printf("talker: binding to %s\n", ipstr);

            if (bind(sockfd, plocal->ai_addr, plocal->ai_addrlen) == -1) {
                perror("bind");
                close(sockfd);
                continue;
            }

            // make sure this server address is the only one we talk to
            if (connect(sockfd, pserv->ai_addr, pserv->ai_addrlen) == -1) {
                perror("connect");
                close(sockfd);
                continue;
            }

            if ((numbytes = send(sockfd, argv[2], strlen(argv[2]), 0)) == -1) {
                perror("send");
                close(sockfd);
                continue;
            }

            printf("talker: sent %d bytes\n", numbytes);

            FD_ZERO(&readfds);
            FD_SET(sockfd, &readfds);

            tv.tv_sec = 5;
            tv.tv_usec = 0;

            rv = select(sockfd+1, &readfds, NULL, NULL, &tv);
            if (rv == -1)
            {
                perror("select");
                close(sockfd);
                continue;
            }

            if (rv == 0)
            {
                printf("talker: no reply for 5 seconds\n");
                close(sockfd);
                continue;
            }

            if ((numbytes = recv(sockfd, buf, MAXBUFLEN, 0)) == -1) 
            {
                perror("recv");
                close(sockfd);
                continue;
            }

            printf("talker: received %d bytes\n", numbytes);

            close(sockfd);

            stop = true;
            break;
        }

        freeaddrinfo(myinfo);
    }

    freeaddrinfo(servinfo);

    close(sockfd);

    if (!stop) {
        fprintf(stderr, "talker: failed to communicate with server\n");
        return 3;
    }

    return 0;
}
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/52228275

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档