【问题标题】:C socket problems - random client address and port numberC套接字问题-随机客户端地址和端口号
【发布时间】:2020-04-07 09:36:11
【问题描述】:

我有一个非常简单的练习要做:创建一个套接字,当客户端连接时,服务器会显示其信息(地址和端口号)。

这是我写的:

client.c

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

int main(int argc, char *argv[])
{
    // check arguments
    if (argc != 3)
    {
        fprintf(stderr, "Erreur argument\n");
        exit(EXIT_FAILURE);
    }
    char *nom_machine = argv[1];
    int port = atoi(argv[2]);

    int sock;
    struct sockaddr_in addr;

    // création socket
    sock = socket(AF_INET, SOCK_STREAM, 0);

    // remplissage struct de l'adresse
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port); // code les octets dans l'ordre réseau
    inet_aton(nom_machine, &addr.sin_addr);

    // connnexion à 1.2.3.4:4242
    connect(sock, (struct sockaddr *)&addr, sizeof addr);

    // display informations
    fprintf (stderr,
            "---------\nAdresse: %s\nPort: %hd\n---------\n",
            inet_ntoa(addr.sin_addr),
            ntohs(addr.sin_port));


    // envoie message
    int n = 7;
    int r = write(sock, &n, sizeof(int));
    if (r != sizeof(int)) {
        fprintf(stderr, "Echec de l'envoi de n\n");
        exit(EXIT_FAILURE);
    }

    // arrêt
    close(sock);

    exit(EXIT_SUCCESS);
}

服务器.c

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

int main(int argc, char *argv[])
{   
    if (argc != 2)
    {
        fprintf(stderr, "Erreur argument\n");
        exit(EXIT_FAILURE);
    }
    int port = atoi(argv[1]);

    int sock;
    struct sockaddr_in addr;

    // création socket
    sock = socket(AF_INET, SOCK_STREAM, 0);

    // remplissage struct de l'adresse
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port); // code les octets dans l'ordre réseau
    addr.sin_addr.s_addr = INADDR_ANY;

    // création connnexion
    bind(sock, (struct sockaddr *)&addr, sizeof addr);

    // écoute
    listen(sock, 2);

    // attend une connexion entrante
    struct sockaddr_in client_addr;
    socklen_t size;
    int lect = accept(sock, (struct sockaddr *)&client_addr, &size); // (2) structure entring connection

    // display client informations
    fprintf (stderr,
            "---------\nAdresse: %s\nPort: %d\n---------\n",
            inet_ntoa(client_addr.sin_addr),
            ntohs(client_addr.sin_port));

    // lecture message
    int n;
    int r = read(lect, &n, sizeof(int));
    if (r <= 0) {
                fprintf(stderr, "Le serveur n'a pas recu le message\n");
                close(sock);
                exit(EXIT_FAILURE);
    }
    // Affichage
    fprintf(stderr, "%d\n", n);

    // arrêt
    close(sock);

    exit(EXIT_SUCCESS);
}

不幸的是,当我测试我的代码时,服务器给出的客户端地址和端口号似乎是完全随机的(除了有时它给出了正确的地址,但我从未看到正确的端口号)。

这是一个例子:

客户端控制台

thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o client client.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./client 127.0.0.1 3333
---------
Adresse: 127.0.0.1
Port: 3333
---------

服务器控制台

thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ gcc -g -Wall -Wextra -Og -o serveur serveur.c
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 6.127.0.0
Port: 16578
---------
Le serveur n'a pas recu le message
thomas@thomasCoding:~/Code/2020/Reseau/TP_4$ ./serveur 3333
---------
Adresse: 127.0.0.1
Port: 55098
---------
7

如果有人可以帮助找出我的错误。谢谢:)

【问题讨论】:

  • 没有错误。查找术语“临时端口”
  • 好的,但它如何解释有时端口为负数并且客户端地址已更改?
  • 它不能变成负数。这是一个未签名的短片。 0-65535。你没有正确打印它。

标签: c sockets port


【解决方案1】:

您在 accept(2) 手册页中遗漏了这一点:

addrlen 参数是一个值结果参数:调用者必须 初始化它以包含指向的结构的大小(以字节为单位) 按地址;返回时它将包含对等方的实际大小 地址。

这意味着,你必须这样做

socklen_t size = sizeof client_addr;

否则,如果size(不确定)中的值太小,则返回的地址将被截断,这意味着内容可能保持未初始化(完全或部分)。这会导致您观察到奇怪的值。

另一个问题是,当端口最近被使用(TIME_WAIT 状态)时,bind() 可能会因地址已在使用中而失败。在这种情况下,端口会从某个特定的、实现定义的范围内自动绑定。为避免这种情况,您可以在bind() 之前设置选项SO_REUSEADDR

int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));

此外,最好检查所有返回值是否有错误;你会注意到 bind() 失败了。

【讨论】:

  • 哦,非常感谢,我没注意到。但是有没有办法将端口号固定为与传入参数相同的端口号?
  • @ZyxcelZoïd 我已经添加了一个解释,为什么有时可能会绑定另一个端口
猜你喜欢
  • 2015-02-16
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
  • 1970-01-01
  • 1970-01-01
  • 2014-12-29
相关资源
最近更新 更多