Как открыть сокет на конкретный IP - PullRequest
0 голосов
/ 01 февраля 2019

Я пытаюсь создать простую серверную программу через C, но я не хочу, чтобы она использовала мой текущий IP, скорее я хочу предоставить конкретный IP.Но все не работает.

В качестве самого первого шага по предоставлению конкретного IP-адреса я удалил из кода следующее утверждение: [ПРИМЕЧАНИЕ]: Здесь 'hints' - это объект struct addrinfo* 1004.*

hints.ai_flags = AI_PASSIVE;

Это потому, что он автоматически заполняет наш IP, не позволяя нам указать конкретный IP.

Затем я попытался использовать inet_pton(), чтобы предоставить конкретный IP для sin_addr (memberструктуры sockaddr_in) с использованием инструкции:

inet_pton(AF_INET , host_to_connect  , &(((struct sockaddr_in *)hints.ai_addr)->sin_addr));

Здесь 'host_to_connect' - это строка, содержащая требуемый IP-адрес в десятичном формате с точками (например, char * host_to_connect = "192.168.22.23")

Поскольку в структуре sockaddr нет члена с именем sin_addr, я типизировал его в структуру sockaddr_in, а затем получил доступ к sin_addr.

Вот фактический код, в котором я работаюon:

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

int main(int argc , char **argv)
{
    char *host_to_connect = argv[1];
    char *port = argv[2];
    struct addrinfo hints , *server_info;

    memset(&hints , 0 , sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    inet_pton(AF_INET , host_to_connect  , &(((struct sockaddr_in *)hints.ai_addr)->sin_addr)); 

        //hints.ai_flags = AI_PASSIVE;

    int status;
    if((status = getaddrinfo(NULL , port , &hints, &server_info)) != 0)
    {
        fprintf(stderr , "Error in getaddrinfo(): %s\n" , gai_strerror(status));
        return 2;
    }

    //creating a socket
    int socketfd;
    if((socketfd = socket(server_info->ai_family , server_info->ai_socktype , server_info->ai_protocol)) == -1)
    {
        fprintf(stderr , "Error in socket() : %s\n " , gai_strerror(socketfd));
        return 2;
    }

    //binding port to socket
    int bind_status;
    if((bind_status = bind(socketfd , server_info->ai_addr , server_info->ai_addrlen)) == -1)
    {
        fprintf(stderr , "Error in bind() : %s\n " , gai_strerror(bind_status));
        return 2;
    }

    //listining on the socket
    int listen_status;
    if((listen_status = listen(socketfd , 5)) == -1 )
    {
        fprintf(stderr , "Error in listen() : %s\n " , gai_strerror(listen_status));
        return 2;
    }

    //accepting connections
    int client_fd;
    struct sockaddr_storage client_info;
    socklen_t address_length = sizeof(client_info);
    if((client_fd = accept(socketfd , (struct sockaddr *)&client_info , &address_length)) == -1)
    {
        fprintf(stderr , "Error in accept() : %s\n " , gai_strerror(client_fd));
        return 2;
    }

    //sending message to client
    char *message = "\n\nYou are successfully connected to the server!\n\n";
    int bytes_sent;
    bytes_sent = send(client_fd , message , strlen(message) , 0);
    printf("\nBytes sent: %d\n" , bytes_sent);

    close(socketfd);
    return 0;
}

Я ожидал, что он будет работать хорошо, но когда я запускаю его, предоставляя необходимые аргументы, такие как:

gcc server.c -o ./server
./server "192.168.22.23" "8989"

Я получаю Ошибка сегментации каквыход.

Когда я пытаюсь отладить его с помощью отладчика GNU, выясняется, что что-то не так с inet_pton(), использованным в коде, но я не могу понять это.

Буду рад, если кто-то сможет все прояснить!

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Вы неправильно используете getaddrinfo() и gai_strerror().

Вы не должны заполнять hints.ai_addr вообще.В hints допустимо только несколько полей, и ai_addr не является одним из них:

Аргумент подсказки указывает на addrinfo структура, которая задает критерии для выбора структур адреса сокета, возвращаемых в списке, на который указывает res. Если подсказки не равен NULL, это указывает на структуру addrinfo , чьи ai_family , ai_socktype и ai_protocol укажите критерии, которые ограничивают набор адресов сокетов, возвращаемых getaddrinfo() ... Все остальные поля в структуре, на которые указывает подсказки , должны содержать либо 0, либо указатель NULL, в зависимости от ситуации.

Чтобы передать IP в качестве ввода в getaddrinfo(), вам нужно вместо этого передать его в параметре node и указать флаг AI_NUMERICHOST в параметре hints:

узел задает либо числовой сетевой адрес (для IPv4, нотация цифр и точек, как поддерживается inet_aton (3); для IPv6, шестнадцатеричный формат строки, поддерживаемый inet_pton(3)) , или имя сетевого узла, чьи сетевые адреса ищутся и разрешаются. Если hints.ai_flags содержит флаг AI_NUMERICHOST, тогда узел должен быть числовым сетевым адресом. Флаг AI_NUMERICHOST подавляет любой потенциально длинный адрес сетевого узлаlookups.

В случае успеха вы можете использовать sockaddr*, предоставленный server_info->ai_addr при вызове bind().

И звоните gai_strerror() только тогда, когда getaddrinfo() не удается.Недопустимо вызывать при сбое других функций.Используйте perror() для них.

Попробуйте это:

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

int main(int argc , char **argv)
{
    // Validate the parameters
    if (argc != 3)
    {
        printf("usage: %s <ip> <port>\n", argv[0]);
        return 1;
    }

    struct addrinfo hints, *server_info;
    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_NUMERICHOST; //AI_PASSIVE;

    int status = getaddrinfo(argv[1], argv[2], &hints, &server_info);
    if (status != 0)
    {
        fprintf(stderr, "Error in getaddrinfo(): %s\n", gai_strerror(status));
        return 2;
    }

    //creating a socket
    int socketfd = socket(server_info->ai_family, server_info->ai_socktype, server_info->ai_protocol);
    if (socketfd == -1)
    {
        perror("Error in socket()");
        freeaddrinfo(server_info);
        return 2;
    }

    //binding port to socket
    status = bind(socketfd, server_info->ai_addr, server_info->ai_addrlen);
    if (status == -1)
    {
        perror("Error in bind()");
        close(socketfd);
        freeaddrinfo(server_info);
        return 2;
    }

    freeaddrinfo(server_info);

    //listening on the socket
    status = listen(socketfd, 5);
    if (status == -1)
    {
        perror("Error in listen()");
        close(socketfd);
        return 2;
    }

    //accepting connections
    struct sockaddr_storage client_info;
    socklen_t address_length = sizeof(client_info);
    int client_fd = accept(socketfd, (struct sockaddr *)&client_info, &address_length);
    if (client_fd == -1)
    {
        perror("Error in accept()");
        close(socketfd);
        return 2;
    }

    //sending message to client
    char *message = "\n\nYou are successfully connected to the server!\n\n";
    int bytes_sent, bytes_to_send = strlen(message);
    do
    {
        bytes_sent = send(client_fd, message, bytes_to_send, 0);
        if (bytes_sent == -1)
        {
            perror("Error in send()");
            close(client_fd);
            close(socketfd);
            return 2;
        }
        printf("Bytes sent: %d\n", bytes_sent);
        message += bytes_sent;
        bytes_to_send -= bytes_sent;
    }
    while (bytes_to_send > 0);

    close(client_fd);
    close(socketfd);

    return 0;
}
0 голосов
/ 01 февраля 2019

Причина сбоя в том, что вы пытаетесь разыменовать нулевой указатель.

hints.ai_addr имеет значение NULL после вашего memset, но вы пытаетесь разыменовать его с помощью выражения &(((struct sockaddr_in *)hints.ai_addr)->sin_addr))когда вы звоните inet_pton.Это вызывает неопределенное поведение , которое в этом случае проявляется как сбой.

Даже если вы исправите это, вы не должны помещать значения в hints.ai_addr.При передаче ненулевого значения в hints структура может содержать значения только для полей ai_family, ai_socktype или ai_protocol.Остальное должно быть 0 или NULL.

Для того, что вы делаете, вам лучше пропустить использование getaddrinfo и заполнить struct sockaddr_in напрямую, чтобы перейти к bind.Кроме того, gai_strerror используется только для получения строк ошибок для getaddrinfo.Для других функций сокетов используйте strerror или perror:

struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));

sin.sin_family = AF_INET;
if  (!inet_pton(AF_INET, host_to_connect, &sin_sin_addr)) {
    fprintf(stderr, "invalid address\n");
    return 2;
}
sin.sin_port = htons((uint16_t)atoi(argv[2])); 

//creating a socket
int socketfd;
if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
    perror("Error in socket()");
    return 2;
}

//binding port to socket
int bind_status;
if((bind_status = bind(socketfd, (struct sockaddr *)&sin , sizeof(sin))) == -1)
{
    perror("Error in bind()");
    return 2;
}

Кроме того, вы можете привязать только IP-адрес, который присутствует в вашей системе.Если у вас более одного сетевого интерфейса, вы можете использовать его для прослушивания только одного из них, в отличие от всех, если вы хотите связать с 0.0.0.0.

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