【问题标题】:UDP socket port allocation failureUDP套接字端口分配失败
【发布时间】:2009-10-04 17:48:46
【问题描述】:

我正在创建一个 winsock UDP 程序。我正在使用的代码如下所示。

我总是收到端口分配错误。

我无法理解为什么端口总是分配为零。如果有人可以帮助我解决这个问题....

void UDPecho(const char *, const char *);

void errexit(const char *, ...);

#define LINELEN  128
#define WSVERS  MAKEWORD(2, 0)

void main(int argc, char *argv[])
{
    char *host = "localhost";
    char *service = "echo";
    WSADATA wsadata;
    switch (argc) {
    case 1:
        host = "localhost";
        break;

    case 3:
        service = argv[2];
        /* FALL THROUGH */

    case 2:
        host = argv[1];
        break;

    default:
        fprintf(stderr, "usage: UDPecho [host [port]]\n");
        exit(1);

    }

    if (WSAStartup(WSVERS, &wsadata))
        errexit("WSAStartup failed\n");

    UDPecho(host, service);

    WSACleanup();

    exit(0);
}

void UDPecho(const char *host, const char *service)
{
    char buf[LINELEN+1];
    SOCKET s;
    int nchars;
    struct hostent *phe;
    struct servent *pse;
    struct protoent *ppe;
    struct sockaddr_in sin, my_sin;
    int type, status, client_port, size;
    char *transport = "udp";

    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;

    /* Map service name to port number */
    if ( pse = getservbyname(service, transport) )
        sin.sin_port = pse->s_port;
    else if ( (sin.sin_port = htons((u_short)atoi(service)))== 0)
        errexit("can't get \"%s\" service entry\n", service);

    /* Map host name to IP address, allowing for dotted decimal */
    if ( phe = gethostbyname(host) )
        memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
    else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
        errexit("can't get \"%s\" host entry\n", host);

    printf("Our target server is at address %s\n", inet_ntoa(sin.sin_addr));
    printf("The size of an FD set is %d\n", sizeof(FD_SET));

    /* Map protocol name to protocol number */
    if ( (ppe = getprotobyname(transport)) == 0)
        errexit("can't get \"%s\" protocol entry\n", transport);

    /* Use protocol to choose a socket type */
    if (strcmp(transport, "udp") == 0)
        type = SOCK_DGRAM;
    else
        type = SOCK_STREAM;

    /* Allocate a socket */
    s = socket(PF_INET, type, ppe->p_proto);
    if (s == INVALID_SOCKET)
        errexit("can't create socket: %d\n", GetLastError());

    size = sizeof(sin);
    memset(&my_sin, 0, sizeof(sin));
    getsockname (s, (struct sockaddr *) &my_sin, &size);
    client_port = ntohs(my_sin.sin_port);

    if (client_port != 0)
        printf ("We are using port %2d\n", client_port);
    else {
        printf("No port assigned yet\n");
    }
}


void errexit(const char *format, ...)
{
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    WSACleanup();

    exit(1);
}

【问题讨论】:

  • 格式化很糟糕。请检查我是否没有犯任何错误编辑。而且 - 你调试了吗?
  • 附带说明 - 它是 int main,而不是 void main
  • 您考虑过使用 ASIO 库吗? asio.sourceforge.net
  • 我想在这里我会推荐使用异常处理程序进行清理。

标签: c++ sockets udp winsock port


【解决方案1】:

在您在套接字上发出sendto()bind() 之前,UDP 不会绑定到侦听端口。后者使您可以选择要侦听的端口。另一方面,Sendto() 将为您选择一个临时端口。我希望端口将保持为零,直到您执行这两件事之一。

澄清

在一些评论之后,我对此进行了更多调查。根据Single UNIX Specification 调用socket() 的结果是一个未绑定 套接字。通过调用bind() 或隐式调用sendto()绑定 套接字。

将套接字的名称视为包含其(地址族、协议、本地 IP 地址和本地端口号)的元组。前两个在socket() 调用中指定,后两个通过调用bind() 指定。在无连接协议的情况下,在断开连接的套接字上调用 sendto() 将导致隐式绑定到操作系统选择的端口号。

最令人惊讶的是,我能找到的关于这种行为的唯一参考是在Microsoft documentation for sendto() 的备注部分。

如果套接字未绑定,系统会为本地关联分配唯一值,然后将套接字标记为已绑定。在这种情况下,应用程序可以使用 getsockname(Windows 套接字)来确定本地套接字名称。

getsockname() 的单一 UNIX 规范声明:

如果套接字没有绑定到本地名称,则存储在地址指向的对象中的值是未指定的。

似乎以未指定结果成功返回是“标准”行为......嗯......我尝试过的实现都成功返回,套接字地址为0.0.0.0 :0 对应于INADDR_ANY,带有未指定的端口。在调用bind()sendto() 之后,getsockname() 返回一个填充的套接字地址,尽管地址部分可能仍然是INADDR_ANY

【讨论】:

  • 我还要补充一点,由于上述原因,认为端口 0 必然是“错误”是错误的。除非另有说明,否则检查 winsock 错误的唯一方法是使用 WSAGetLastError() -- 如果您获得端口 0,但 WSAGetLastError 返回“操作成功”消息,则可能有 not 出错了。
  • @D.Shawley:这是 Windows 特有的还是大多数 UDP 实现通用的?
  • 我相信这对所有实现都是通用的。我将在回复中添加更多 cmets 以澄清。
猜你喜欢
  • 2010-12-30
  • 1970-01-01
  • 2015-08-02
  • 2013-03-18
  • 1970-01-01
  • 2014-02-06
  • 1970-01-01
  • 2017-05-22
  • 1970-01-01
相关资源
最近更新 更多