如何在特定IP上打开套接字

时间:2019-02-01 19:45:28

标签: c sockets

我想创建到C一个简单的服务器程序,但我不希望它使用我目前的IP,而我想提供一个具体的IP。但是事情不起作用。

作为提供特定IP的第一步,我从代码中删除了以下语句: [注意]:这里的“提示”是struct addrinfo

的对象
hints.ai_flags = AI_PASSIVE;

那是因为它会自动填满我们的IP,不允许我们提供特定的IP。

然后我尝试使用inet_pton()通过以下语句向sin_addrsockaddr_in结构的成员)提供特定IP:

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”)

由于在sin_addr结构中没有名为sockaddr的成员,我将其强制转换为sockaddr_in结构,然后访问sin_addr

这是我正在使用的实际代码:

#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 :(得分:3)

原因你崩溃是因为你想取消引用空指针。

hints.ai_addr有你的memset后的NULL值,但你试图用drerefrence它 调用&(((struct sockaddr_in *)hints.ai_addr)->sin_addr))时使用表达式inet_pton。这会调用glibc reference,在这种情况下,它会显示为崩溃。

即使您已解决此问题,也不应将值放入hints.ai_addr中。向hints传递非NULL值时,该结构只能包含ai_familyai_socktypeai_protocol字段的值。其余的必须为0或NULL。

对于您正在做的事情,最好不要使用getaddrinfo并直接填充struct sockaddr_in来传递给bind。另外,gai_strerror仅用于得到错误串为getaddrinfo。对于其他套接字功能,请使用strerrorperror

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的所有接口。

答案 1 :(得分:1)

您滥用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标志:< / p>

  

节点指定数字网络地址(对于IPv4,inet_aton(3)支持数字和点表示法;对于IPv6,inet_pton(3)支持的十六进制字符串格式))或一个网络主机名,其网络地址被查找和解决。 如果 hints.ai_flags 包含AI_NUMERICHOST标志,则 node 必须是数字网络地址。 AI_NUMERICHOST标志禁止任何可能冗长的网络主机地址查找。

一旦成功,则可以使用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;
}