getaddrinfo的應用

RGBMarco發表於2018-02-09

/***********************************************************************************
函式原型: int getaddrinfo(const char* host,const char* service,const struct addrinfo* hints,struct addrinfo** results);
函式說明:通過hints提示資訊得到滿足提示資訊的結果集,若成功.返回0,若出錯,返回錯誤值,可通過gai_strerror(int err)得到錯誤資訊
**********************************************************************************/

/***********************************************************************************
struct addrinfo {
char* ai_canonname; //主機規範名
int ai_flags;//標誌(AI_PASSIVE多用於伺服器
int ai_family;//網際協議族
int ai_socktype;//套接字型別
int ai_protocol;//套接字採用協議
struct sockaddr* ai_addr;//獲得的主機地址
struct addrinfo* ai_next;//滿足hints下一個addrinfo
}
***********************************************************************************/

小試牛刀:

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>

char* what_family(int family) {
    switch (family) {
        case AF_INET:
            return "AF_INET";
        case AF_INET6:
            return "AF_INET6";
        default:
            return "unknown family";
    }
}

char* what_protocol(int socktype,int protocol) {
    switch (protocol) {
        case IPPROTO_TCP:
            return "TCP";
        case IPPROTO_UDP:
            return "UDP";
        case IPPROTO_SCTP:
            return "SCTP";
        case 0:
            if (socktype == SOCK_DGRAM) {
                return "UDP";
            }else if (socktype == SOCK_STREAM) {
                return "TCP";
            }else if (socktype == SOCK_RAW) {
                return "SOCK_RAW TCP/UDP";
            }else if (socktype == SOCK_RDM) {
                return "SOCK_RDM TCP/UDP";
            }else if (socktype == SOCK_PACKET) {
                return "SOCK_PACKET TCP/UDP";
            }else {
                return "unknow protocol";
            }
        default:
            return "unknown protocol";
    }
}

int main(int argc,char **argv) {
    if (argc != 3) {
        printf("please add <hostname/ip address> <services-name/port>\n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_CANONNAME;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = 0;
    hints.ai_protocol = 0;

    struct addrinfo* res;
    int err;
    if ((err = getaddrinfo(argv[1],argv[2],&hints,&res)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    struct addrinfo* dummy;
    for (dummy = res; dummy != NULL; dummy = dummy->ai_next) {
        printf("------------------------------------\n");
        printf("host name: %s\n",dummy->ai_canonname);
        printf("using family: %s\n",what_family(dummy->ai_family));
        printf("using protocol: %s\n",what_protocol(dummy->ai_socktype,dummy->ai_protocol));
        char *str;

        if (dummy->ai_family == AF_INET) {
            str = (char*)malloc(INET_ADDRSTRLEN);
            struct sockaddr_in *addr = (struct sockaddr_in*)dummy->ai_addr;
            if (inet_ntop(AF_INET,&addr->sin_addr,str,INET_ADDRSTRLEN) == NULL) {
                printf("inet_ntop error: %s\n",strerror(errno));
                exit(1);
            }
            printf("using IP: %s Port: %d\n",str,ntohs(addr->sin_port));
        }else if (dummy->ai_family == AF_INET6) {
            str = (char*)malloc(INET6_ADDRSTRLEN);
            struct sockaddr_in6 *addr = (struct sockaddr_in6*)dummy->ai_addr;
            printf("using IP: %s Port: %d\n",inet_ntop(AF_INET6,&addr->sin6_addr,str,INET6_ADDRSTRLEN),ntohs(addr->sin6_port));
        }else {
            printf("unknown error\n");
            continue;
        }
        free(str);
        printf("------------------------------------\n\n\n");
    }

    freeaddrinfo(res);  
    return 0;
}

服務端程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/wait.h>
#include <time.h>

#define LISTENQ 100
#define BUFSIZE 4096

void sig_child(int signo) {
    int ppid;
    while ((ppid = waitpid(-1,NULL,WNOHANG)) > 0) {
        printf("child process id %d terminated\n",ppid);
    }
    return;
}

void server_echo(int sockfd) {
    char str[BUFSIZE];
    time_t t = time(NULL);
    strftime(str,BUFSIZE,"%Y %x %X",localtime(&t));
    size_t len = strlen(str);
    str[len] = '\n';
    str[len + 1] = '\0';
    if (write(sockfd,str,strlen(str)) != strlen(str)) {
        printf("write error: %s\n",strerror(errno));
        exit(1);
    }
}
int main(int argc,char **argv) {
    if (argc != 2) {
        printf("please add <services name>\n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_PASSIVE;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(NULL,argv[1],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    struct addrinfo* dummy = results;
    int sockfd;
    const int on = 1;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,dummy->ai_protocol)) < 0) {
            continue;
        }

        if (setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0) {
            printf("setsockopt error: %s\n",strerror(errno));
            exit(1);
        }

        if (bind(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
            break;
        }

    }

    if (dummy == NULL) {
        printf("all failed\n");
        exit(1);
    }

    freeaddrinfo(results);
    if (listen(sockfd,LISTENQ) < 0) {
        printf("listen error: %s\n",strerror(errno));
        exit(1);
    }

    if (signal(SIGCHLD,sig_child) == SIG_ERR) {
        printf("signal error: %s\n",strerror(errno));
        exit(1);
    }

    int connfd;
    int ppid;
    for (; ;) {
        if ((connfd = accept(sockfd,NULL,NULL)) < 0) {
            if (errno == EINTR) {
                continue;
            }
            printf("accept error: %s\n",strerror(errno));
            exit(1);
        }
        if ((ppid = fork()) < 0) {
            printf("fork error: %s\n",strerror(errno));
            exit(1);
        }else if (ppid == 0) {
            close(sockfd);
            server_echo(connfd);
            close(connfd);
            exit(0);
        }
        close(connfd);
    }
    return 0;
}

客戶端程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <errno.h>

#define BUFSIZE 4096

int main(int argc,char** argv) {
    if (argc != 3) {
        printf("please add <hostname/ip address> <service name>\n");
        exit(1);
    }

    struct addrinfo hints;
    bzero(&hints,sizeof(struct addrinfo));

    hints.ai_flags = AI_CANONNAME;
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    struct addrinfo* results;
    int err;
    if ((err = getaddrinfo(argv[1],argv[2],&hints,&results)) != 0) {
        printf("getaddrinfo error: %s\n",gai_strerror(err));
        exit(1);
    }
    struct addrinfo* dummy = results;
    int sockfd;
    for (; dummy != NULL; dummy = dummy->ai_next) {
        if ((sockfd = socket(dummy->ai_family,dummy->ai_socktype,0)) < 0) {
            continue;
        }
        if (dummy->ai_family == AF_INET) {
            if (connect(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
                break;
            }   
        }else if (dummy->ai_family == AF_INET6) {
            if (connect(sockfd,dummy->ai_addr,dummy->ai_addrlen) == 0) {
                break;
            }
        }else {
            printf("unknown error\n");
            exit(1);
        }
    }
    if (dummy == NULL) {
        printf("all connect failed\n");
        exit(1);
    }
    char recv[BUFSIZE];
    int n;
    while ((n = read(sockfd,recv,BUFSIZE)) > 0) {
        if (write(STDOUT_FILENO,recv,n) != n) {
            printf("write error: %s\n",strerror(errno));
            exit(1);
        }
    }
    return 0;
}