Как добавить номер порта по умолчанию, определенный в программе, в getaddrinfo () - PullRequest
0 голосов
/ 19 октября 2018

Я хочу определить номер порта в моем заголовке (например, #define port 9191) и использовать его для вызова getaddrinfo().Но я получаю сообщение об ошибке, поскольку служба параметров char const*.Как использовать номер порта #define d при вызове на getaddrinfo()?

Ниже I #define d порт как 9191 и пытался преобразовать его в char* с помощью sprintf(),Затем я попытался использовать его в вызове getaddrinfo():

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netdb.h>

#define PORT 9191 // here the define port number
#define LEN 500

int
main(int argc, char *argv[])
{
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    struct sockaddr_in cliAddr;  // the from address of a client
    int sock, s, clilen;
    char *service; // declared variable name
    struct sockaddr_in storage;

    ssize_t n;
    char receive[LEN], message[LEN];
    sprintf(service, "%d", PORT); // i have tried convert it to char

    if (argc != 1) {
        fprintf(stderr, "Usage: %s \n", argv[0]);
        exit(EXIT_FAILURE);
    }

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_INET;    /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
    hints.ai_flags = 0;    /* For wildcard IP address */
    hints.ai_protocol = 0;          /* Any protocol */
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;

    s = getaddrinfo(NULL, &service, &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }

    /* getaddrinfo() returns a list of address structures.
       Try each address until we successfully bind(2).
       If socket(2) (or bind(2)) fails, we (close the socket
       and) try the next address. */

    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sock = socket(rp->ai_family, rp->ai_socktype,
            rp->ai_protocol);
        if (sock == -1)
            continue;

        if (bind(sock, rp->ai_addr, rp->ai_addrlen) == 0)
            break;                  /* Success */

        close(sock);
    }

    if (rp == NULL) {               /* No address succeeded */
        fprintf(stderr, "Could not bind\n");
        exit(EXIT_FAILURE);
    }

    freeaddrinfo(result);           /* No longer needed */

    /* Read datagrams and echo them back to sender */

    while (1)
    {
        //bzero(receive, LEN);
        clilen = sizeof(cliAddr);
        n = recvfrom(sock, receive, strlen(receive), 0, (struct sockaddr*)&cliAddr, &clilen);
        if (n < 0)
        {
            fprintf(stderr, "error in reading\n");
            exit(EXIT_FAILURE);
        }
        printf("The client message: %s\n ", receive);
        bzero(message, LEN);

        fgets(message, sizeof(message), stdin);

        message[LEN] = '\0';
        printf("%s\n", message);
        n = sendto(sock, message, strlen(message), 0, (struct sockaddr*)&cliAddr, sizeof(cliAddr));
        printf("%ld\n", n);
        if (n < 0)
        {
            fprintf(stderr, "error in replying\n");
            exit(EXIT_FAILURE);
        }
        int i = strncmp("Exit", receive, 4);
        if (i == 0)
            break;

        printf("The server message: %s\n", message);

    }

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 19 октября 2018

Поскольку Рыба-меч уже ответила, вы пытаетесь sprintf() к неопределенному указателю.Вместо этого используйте, например,

#define  STRINGIFY_(x)  #x
#define  STRINGIFY(x)   STRINGIFY_(x)

service = STRINGIFY(PORT);

.


Рассмотрите следующую вспомогательную функцию:

#define  DEFAULT_HOST "*"
#define  DEFAULT_PORT "9191"

/* Return a server-side UDP/IP(v4/v6) socket bound
   to the specified address. If addr is non-NULL,
   and *addrlen is initialized to its length and
   is large enough, the bound-to address will be
   stored there.  Returns -1 with errno set if
   an error occurs. */       
int server_socket(struct sockaddr *addr, socklen_t *addrlen,
                  const char *host, const char *port)
{
    struct addrinfo  hints, *list, *curr;
    int              err, fd;

    /* Set defaults, if NULL or empty. */
    if (!host || !*host)
        host = DEFAULT_HOST;
    if (!port || !*port)
        port = DEFAULT_PORT;

    /* "" or "*" host refer to wildcard address. */
    if (!host[0] || !strcmp(host, "*")
        host = NULL;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;       /* Allow IPv4 or IPv6 */
    hints.ai_socktype = SOCK_DGRAM;  /* Datagram socket */
    hints.ai_flags = 0;              /* For wildcard IP address */
    hints.ai_protocol = 0;           /* Any protocol */
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;

    err = getaddrinfo(host, port, &hints, &list);
    if (err)
        switch (err) {
        case EAI_ADDRFAMILY:
        case EAI_FAMILY:
            errno = EAFNOSUPPORT;
            return -1;
        case EAI_AGAIN:
            errno = EAGAIN;
            return -1;
        case EAI_FAIL:
        case EAI_NODATA:
        case EAI_NONAME:
        case EAI_SERVICE:
            errno = EADDRNOTAVAIL;
            return -1;
        case EAI_MEMORY:
            errno = ENOMEM;
            return -1;
        case EAI_SOCKTYPE:
            errno = ESOCKTNOSUPPORT;
            return -1;
        case EAI_SYSTEM:
            /* errno already set */
            return -1;
        default:
            errno = EINVAL;
            return -1;
        }

    /* Find first valid socket we can construct and bind to */
    for (curr = list; curr != NULL; curr = curr->ai_next) {
        fd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
        if (fd == -1)
            continue;

        if (bind(fd, curr->ai_addr, curr->ai_addrlen) == 0)
            break; /* This one works. */

        /* Cannot bind. Clean it up, and try the next one. */
        close(fd);
    }

    if (!curr) {
        /* No suitable socket found. */
        errno = EADDRNOTAVAIL;
        return -1;
    }       

    /* Copy bound-to address, if enough room */
    if (addrlen) {
        if (!addr || *addrlen < curr->ai_addrlen) {
            *addrlen = 0;
        } else {
            memcpy(addr, curr->ai_addr, curr->ai_addrlen);
            *addrlen = curr->ai_addrlen;
        }
    }

    /* Discard the linked list getaddrinfo() provided. */
    freeaddrinfo(list);

    return fd;
}

И числовые порты, и именованные службы (как указано в getent services) может использоваться как для порта по умолчанию, так и для параметра порта.

Типичная служба считывает их из файла конфигурации (или файлов).

Если вышеприведенный server_socket() завершается неудачно,он вернет -1 с errno, приблизительно равным сообщению об ошибке.(Если getaddrinfo() терпит неудачу, он отображает ошибку на errno константы как можно лучше, вместо использования gai_strerror().) В случае успеха, он вернет номер дескриптора связанного сокета.

Причина, по которой вы захотите записать их как отдельные функции, в основном лень и простота обслуживания .Видите ли, если вы запишите вышеприведенное (с необходимыми #include и т. Д.) В отдельную программу тестирования, вы можете протестировать эту часть отдельно, как единое целое: таким образом, модульное тестирование это с несколькими различнымипараметры, включая указатели NULL и недопустимые значения.Например,

struct sockaddr_storage  addr;
socklen_t                addrlen;
const char              *node, *service;
int                      fd;

addrlen = sizeof addr;
fd = server_socket(&addr, &addrlen, node, service);
if (fd == -1) {
    fprintf(stderr, "Cannot create server socket: %s.\n", strerror(errno));
    exit(EXIT_FAILURE);
}

, чтобы сообщить об ошибках.Помните, что тестирование - это не просто проверка работоспособности функции при заданных вменяемых параметрах;он также проверяет, что происходит, если сами параметры неверны / плохи / противоречивы и т. д., и проверяет, что , что тоже приемлемо.

Причина, по которой этот вид кусочного тестирования экономит время и усилияв том, что когда возникают ошибки - и они будут, независимо от того, насколько вы хороши программистом - есть только небольшая часть кода (добавленные части еще не протестированы модульно) и их общее взаимодействиелогика (логика «более высокого уровня»), которую нужно изучить и обдумать, чтобы найти ошибки.Кроме того, вам не нужно тратить силы на то, чтобы держать все части кода в уме одновременно;вы можете просто забыть о проверенных модулем деталях, помня только то, что они делают в общих чертах.

Это также означает, что вы захотите научиться писать комментарии, описывающие, что ваше намерение какпрограммист - это то, что должен делать код, а не то, что делает код.Последнее очевидно из кода, но только программист знает первое;чтобы эффективно находить ошибки, нам нужно знать и то, и другое.(Я все еще изучаю эту часть после более чем двух десятилетий платной работы программистом.)

0 голосов
/ 19 октября 2018
char *service; // declared variable name
// ...
sprintf(service, "%d", PORT); // i have tried convert it to char

Поскольку service не инициализирован и, скорее всего, содержит недопустимое значение указателя, вызов sprintf() вызывает неопределенное поведение.

Либо выделите память на свободномхранить и иметь service, указывающую на него, или использовать массив достаточного размера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...