《TCP/IP网络编程》读书笔记

第一章 理解网络编程和套接字

服务端套接字建立过程所用的几个函数

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//成功时返回文件描述符,失败时返回-1
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen);
//成功时返回文件描述符,失败时返回-1
#include <sys/socket.h>
int listen(int sockfd, int backlog);
//成功时返回文件描述符,失败时返回-1
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//成功时返回文件描述符,失败时返回-1

网络编程中接受链接请求的套接字创建过程如下:

  1. 调用socket函数创建套接字。
  2. 调用bind函数分配IP地址和端口号
  3. 调用listen函数转为可接受请求状态
  4. 调用accept函数受理链接请求

hello_server.c

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

void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sock;
    int clnt_sock;

    struct sockaddr_in serv_addr;
    struct sockaddr_in clnt_addr;
    socklen_t clnt_addr_size;

    char message[]="Hello World!";
    
    if(argc!=2){
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }
    
    serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    if(serv_sock == -1)
        error_handling("socket() error");
    
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_addr.sin_port=htons(atoi(argv[1]));
    
    if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr))==-1 )
        error_handling("bind() error"); 
    
    if(listen(serv_sock, 5)==-1)
        error_handling("listen() error");
    
    clnt_addr_size=sizeof(clnt_addr);  
    clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_addr,&clnt_addr_size);
    if(clnt_sock==-1)
        error_handling("accept() error");  
    
    write(clnt_sock, message, sizeof(message));
    close(clnt_sock);    
    close(serv_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

客户端套接字建立过程所用的几个函数

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//成功时返回文件描述符,失败时返回-1
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr* serv_addr, socklen_t addrlen);
//成功时返回文件描述符,失败时返回-1

hello_client.c

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

void error_handling(char *message);

int main(int argc, char* argv[])
{
    int sock;
    struct sockaddr_in serv_addr;
    char message[30];
    int str_len;
    
    if(argc!=3){
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    
    sock=socket(PF_INET, SOCK_STREAM, 0);
    if(sock == -1)
        error_handling("socket() error");
    
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_addr.sin_port=htons(atoi(argv[2]));
        
    if(connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1) 
        error_handling("connect() error!");
    
    str_len=read(sock, message, sizeof(message)-1);
    if(str_len==-1)
        error_handling("read() error!");
    
    printf("Message from server: %s \n", message);  
    close(sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

文件描述符

文件描述号对象
0标准输入:Standard Input
1标准输出:Standard Output
2标准错误:Standard Error

打开文件

#include <sys/types.h>
#include <sys.stat.h>
#include <fctnl.h>
int open(const char* path, int flag);
//成功时返回文件描述符,失败时返回-1
/*
param:
path: 文件名的地址
flag: 打开模式信息
*/

文件打开模式

打开模式含义
O_CREAT必要时创建文件
O_TRUNC删除全部现有数据
O_APPEND维持现有数据,保存到其后面
O_RDONLY只读打开
O_WRONLY只写打开
O_RDWR读写打开

关闭文件

#include <unistd.h>
int close(int fd);
//成功时返回0,失败时返回-1
/*
param:
fd: 需要关闭的文件或套接字的文件描述符
*/

写入文件

#include <unistd.h>
ssize_t write(int fd, const void* f, size_t nbytes);
//成功时返回写入的字节数,失败时返回-1
/*
param:
fd: 显示数据传输对象的文件描述符
buf: 保存要传输数据的缓冲地址值
nbytes: 要传输的字节数
*/
#include <unistd.h>
ssize_t read(int fd, const void* f, size_t nbytes);
//成功时返回接受的字节数,失败时返回-1,遇到文件尾则返回0
/*
param:
fd: 显示数据接收对象的文件描述符
buf: 要保存接收数据的缓冲地址值
nbytes: 要接收数据的最大字节数
*/

fd_seri.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>

int main(void)
{    
    int fd1, fd2, fd3;
    fd1=socket(PF_INET, SOCK_STREAM, 0);
    fd2=open("test.dat", O_CREAT|O_WRONLY|O_TRUNC);
    fd3=socket(PF_INET, SOCK_DGRAM, 0);
    
    printf("file descriptor 1: %d\n", fd1);
    printf("file descriptor 2: %d\n", fd2);
    printf("file descriptor 3: %d\n", fd3);
    
    close(fd1);
    close(fd2);
    close(fd3);
    return 0;
}

描述符从3开始以由小到达的顺序编号。

第二章 套接字类型与协议设置

关于协议

协议是对话中使用的通信规则,把上述概念拓展到计算机领域可整理为“计算机间对话必备通信准则”。

#include <sys/socket.h>
int socket(int domain, int type, int potocol);
//成功时返回文件描述符,失败时返回-1
/*
param:
domain: 套接字中使用的的协议族(Protocol Family)信息
type: 套接字数据传输类型信息
protocol: 计算机间通信中使用的协议信息
*/

协议族(Protocol Family)

名称协议族
PF_INETIPv4互联网协议族
PF_INET6IPv6互联网协议族
PF_LOCAL本地通信的UNIX协议族
PF_PACKET底层套接字的协议族
PF_IPXIPX Novell协议族

套接字类型(Type)

SOCK_STREAM:面向连接、传输过程中数据不会丢失、按序传输数据、传输的数据不存在数据边界

SOCK_DGRAM: 强调快速传输而非传输顺序、传输的数据可能丢失也可能损毁、传输的数据有边界、限制每次传输的数据大小

协议(Protocal)

int tcp = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
int udp = socket(PF_INET, SOCK_STREAM, IPPROTO_UDP);

协议存在的原因是防止同一协议族中存在多个数据传输方式相同的协议

第三章 地址族与数据序列

端口号

端口号由16位构成,可分配的端口号是0-65535。但0-1023是知名端口号,一般分配给特定应用。此外,虽然端口号不能重复,但TCP套接字和UDP套接字不会共用端口号,所以允许重复。例如:如果某TCP套接字使用9190号端口,则其他TCP套接字就无法使用该端口号,但是UDP可以使用。

结构体sockaddr_in的成员分析

struct sockaddr_in {
    sa_family_t sin_family;     //地址族
    uint16_t sin_port;          //16位TCP/UDP端口号,以网络字节序保存
    struct in_addr sin_addr;    //32位Ip地址
    char sin_zero[8];           //不使用
};

struct in_addr {
    in_addr_t s_addr;           //32位IPv4地址
}

为什么不用sockaddr呢?

struct sockaddr {
    sa_family_t sin_family;
    char sa_data[14]; //地址信息
};

此结构体成员sa_data保存的地址信息中需要包含IP地址和端口号,剩余部分填充0,这也是bind函数要求的。直接填充sa_data非常麻烦,所以便有了sockaddr_in。

字节序与网络字节序

大端序: 高位字节存放到低位地址

小端序:高位字节存放到高位地址

字节序转换

unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htol(unsigned long);
unsigned long ntohl(unsigned long);

h代表host,主机字节序列。

n代表network,代表网络字节序。

#include <stdio.h>
#include <arpa/inet.h>

int main(int argc, char *argv[])
{
    unsigned short host_port=0x1234;
    unsigned short net_port;
    unsigned long host_addr=0x12345678;
    unsigned long net_addr;
    
    net_port=htons(host_port);
    net_addr=htonl(host_addr);
    
    printf("Host ordered port: %#x \n", host_port);
    printf("Network ordered port: %#x \n", net_port);
    printf("Host ordered address: %#lx \n", host_addr);
    printf("Network ordered address: %#lx \n", net_addr);
    return 0;
}

网络地址的初始化与分配

#include <arpa/inet.h>
in_addr_t inet_addr(const char* string);
//成功时返回32位大端序整数型值,失败时返回INADDR_NONE

int inet_aton(const char* string, struct in_addr* addr);
//成功时返回1,失败时返回0
/*
param:
string: 需要转换的ip字符串
addr: 将保存转换结果的in_addr结构体变量的地址值
*/
char* inet_ntoa(struct in_addr adr);
//成功时返回转换的字符串地址值,失败时返回-1
/*
param:
adr:in_addr结构体
*/

INADDR_ANY

每次创建服务器端套接字都要输入IP地址会有些繁琐,此时可如下初始化地址信息。采用这种方式,则可自动获取运行服务器端的IP地址。

struct sockaddr_in addr;
char * serv_port = "9190";
memeset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(atoi(serv_port));

第四章 基于TCP的服务器端/客户端1

echo_client.c

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

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sock;
    char message[BUF_SIZE];
    int str_len;
    struct sockaddr_in serv_adr;

    if(argc!=3) {
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    
    sock=socket(PF_INET, SOCK_STREAM, 0);   
    if(sock==-1)
        error_handling("socket() error");
    
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_adr.sin_port=htons(atoi(argv[2]));
    
    if(connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
        error_handling("connect() error!");
    else
        puts("Connected...........");
    
    while(1) 
    {
        fputs("Input message(Q to quit): ", stdout);
        fgets(message, BUF_SIZE, stdin);
        
        if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))
            break;

        write(sock, message, strlen(message));
        str_len=read(sock, message, BUF_SIZE-1);
        message[str_len]=0;
        printf("Message from server: %s", message);
    }
    
    close(sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

echo_server.c

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

#define BUF_SIZE 1024
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sock, clnt_sock;
    char message[BUF_SIZE];
    int str_len, i;
    
    struct sockaddr_in serv_adr;
    struct sockaddr_in clnt_adr;
    socklen_t clnt_adr_sz;
    
    if(argc!=2) {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }
    
    serv_sock=socket(PF_INET, SOCK_STREAM, 0);   
    if(serv_sock==-1)
        error_handling("socket() error");
    
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));

    if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
        error_handling("bind() error");
    
    if(listen(serv_sock, 5)==-1)
        error_handling("listen() error");
    
    clnt_adr_sz=sizeof(clnt_adr);

    for(i=0; i<5; i++)
    {
        clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
        if(clnt_sock==-1)
            error_handling("accept() error");
        else
            printf("Connected client %d \n", i+1);
    
        while((str_len=read(clnt_sock, message, BUF_SIZE))!=0)
            write(clnt_sock, message, str_len);

        close(clnt_sock);
    }

    close(serv_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

第六章 基于UDP的服务器/客户端

  • UDP中的服务器端和客户端没有连接
  • UDP服务器端和客户端均只需1个套接字

基于UDP的数据I/O函数

#include <sys/socket.h>
ssize_t sendto(int sock, void* buff, size_t nbytes, int flags, struct sockaddr* to, socklen_t addrlen);
//成功时返回传输的字节数,失败时返回-1
/*
param:
sock: 用于传输数据的UDP套接字文件描述符
buff: 保存待传输数据的缓冲地址值
nbytes: 待传输的数据长度,以字节为单位
flags: 可选项参数,若没有则传递0
to: 存有目标地址信息的sockaddr结构体变量的地址值
addrlen: 传递给参数to的地址值结构体变量长度
*/

此函数与之前的TCP输出函数最大的区别在于,此函数需要向他传递目标地址信息。

#include <sys/socket.h>
ssize_t recvfrom(int sock, void* buff, size_t nbytes, int flags, struct sockaddr* from, socklen_t* addrlen);
/*
param:
sock: 用于接收件描述符
buff: 保存接收数据的缓冲地址值
nbytes: 可接收的最大字节数
flags: 可选项参数,若没有则传递0
to: 存有发送端地址信息的sockaddr结构体变量的地址值
addrlen: 保存参数from的结构体变量长度的变量地址值
*/

基于UDP的回声服务器端/客户端

uecho_server.c

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sock;
    char message[BUF_SIZE];
    int str_len;
    socklen_t adr_sz;
    
    struct sockaddr_in serv_adr, from_adr;
    if(argc!=3){
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    
    sock=socket(PF_INET, SOCK_DGRAM, 0);   
    if(sock==-1)
        error_handling("socket() error");
    
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_adr.sin_port=htons(atoi(argv[2]));
    
    while(1)
    {
        fputs("Insert message(q to quit): ", stdout);
        fgets(message, sizeof(message), stdin);     
        if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))    
            break;
        
        sendto(sock, message, strlen(message), 0, 
                    (struct sockaddr*)&serv_adr, sizeof(serv_adr));
        adr_sz=sizeof(from_adr);
        str_len=recvfrom(sock, message, BUF_SIZE, 0, 
                    (struct sockaddr*)&from_adr, &adr_sz);

        message[str_len]=0;
        printf("Message from server: %s", message);
    }    
    close(sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

uecho_server.c

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sock;
    char message[BUF_SIZE];
    int str_len;
    socklen_t clnt_adr_sz;
    
    struct sockaddr_in serv_adr, clnt_adr;
    if(argc!=2){
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }
    
    serv_sock=socket(PF_INET, SOCK_DGRAM, 0);
    if(serv_sock==-1)
        error_handling("UDP socket creation error");
    
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));
    
    if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1)
        error_handling("bind() error");

    while(1) 
    {
        clnt_adr_sz=sizeof(clnt_adr);
        str_len=recvfrom(serv_sock, message, BUF_SIZE, 0, 
                                (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
        sendto(serv_sock, message, str_len, 0, 
                                (struct sockaddr*)&clnt_adr, clnt_adr_sz);
    }    
    close(serv_sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

已连接UDP套接字和未连接UDP套接字

通过sendto函数传输数据的过程大致可分为以下3个阶段

  1. 向UDP套接字注册目标IP和端口号
  2. 传输数据
  3. 删除UDP套接字中注册的目标地址信息

每次调用sendto函数都会重复上述过程,而通过调用connect函数会直接消除掉1和3

的时间,大大提升性能。

uecho_con_client.c

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sock;
    char message[BUF_SIZE];
    int str_len;
    socklen_t adr_sz;
    
    struct sockaddr_in serv_adr, from_adr;
    if(argc!=3){
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    
    sock=socket(PF_INET, SOCK_DGRAM, 0);   
    if(sock==-1)
        error_handling("socket() error");
    
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_adr.sin_port=htons(atoi(argv[2]));
    
    connect(sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr));

    while(1)
    {
        fputs("Insert message(q to quit): ", stdout);
        fgets(message, sizeof(message), stdin);     
        if(!strcmp(message,"q\n") || !strcmp(message,"Q\n"))    
            break;
        /*
        sendto(sock, message, strlen(message), 0, 
                    (struct sockaddr*)&serv_adr, sizeof(serv_adr));
        */
        write(sock, message, strlen(message));

        /*
        adr_sz=sizeof(from_adr);
        str_len=recvfrom(sock, message, BUF_SIZE, 0, 
                    (struct sockaddr*)&from_adr, &adr_sz);
        */
        str_len=read(sock, message, sizeof(message)-1);

        message[str_len]=0;
        printf("Message from server: %s", message);
    }    
    close(sock);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

第七章 优雅地断开套接字连接

针对优雅断开的shutdown函数

#include <sys/socket.h>
int shutdown(int sock, int howto);
//成功时返回0,失败时返回-1
/*
sock: 需要断开的套接字文件描述符
howto: 传递断开方式信息
*/
howto解释
SHUT_RD断开输入端
SHUT_WR断开输出端
SHUT_RDWR同时断开I/O流

file_client.c

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sd;
    FILE *fp;
    
    char buf[BUF_SIZE];
    int read_cnt;
    struct sockaddr_in serv_adr;
    if(argc!=3) {
        printf("Usage: %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    
    fp=fopen("receive.dat", "wb");
    sd=socket(PF_INET, SOCK_STREAM, 0);   

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=inet_addr(argv[1]);
    serv_adr.sin_port=htons(atoi(argv[2]));

    connect(sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
    
    while((read_cnt=read(sd, buf, BUF_SIZE ))!=0)
        fwrite((void*)buf, 1, read_cnt, fp);
    
    puts("Received file data");
    write(sd, "Thank you", 10);
    fclose(fp);
    close(sd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

file_server.c

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

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sd, clnt_sd;
    FILE * fp;
    char buf[BUF_SIZE];
    int read_cnt;
    
    struct sockaddr_in serv_adr, clnt_adr;
    socklen_t clnt_adr_sz;
    
    if(argc!=2) {
        printf("Usage: %s <port>\n", argv[0]);
        exit(1);
    }
    
    fp=fopen("file_server.c", "rb"); 
    serv_sd=socket(PF_INET, SOCK_STREAM, 0);   
    
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family=AF_INET;
    serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    serv_adr.sin_port=htons(atoi(argv[1]));
    
    bind(serv_sd, (struct sockaddr*)&serv_adr, sizeof(serv_adr));
    listen(serv_sd, 5);
    
    clnt_adr_sz=sizeof(clnt_adr);    
    clnt_sd=accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
    
    while(1)
    {
        read_cnt=fread((void*)buf, 1, BUF_SIZE, fp);
        if(read_cnt<BUF_SIZE)
        {
            write(clnt_sd, buf, read_cnt);
            break;
        }
        write(clnt_sd, buf, BUF_SIZE);
    }
    
    shutdown(clnt_sd, SHUT_WR);    
    read(clnt_sd, buf, BUF_SIZE);
    printf("Message from client: %s \n", buf);
    
    fclose(fp);
    close(clnt_sd); close(serv_sd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

第八章 域名及网络地址

利用域名获取IP地址

#include <netdb.h>
struct hostent* gethostbyname(const char* hostname);
//成功时返回hostent结构体地址,失败时返回NULL指针
struct hostent {
    char* h_name;              //官方域名
    char** h_aliases;          //其他域名   
    int h_addrtype;            //获取地址族信息
    int h_length;              //保存IP地址长度 IPV4为4字节 IPV6为6字节
    char** h_addr_list;        //保存域名对应的Ip地址列表,利用负载均衡时会用到这个字段
};

gethostbyname.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int i;
    struct hostent *host;
    if(argc!=2) {
        printf("Usage : %s <addr>\n", argv[0]);
        exit(1);
    }
    
    host=gethostbyname(argv[1]);
    if(!host)
        error_handling("gethost... error");

    printf("Official name: %s \n", host->h_name);
    
    for(i=0; host->h_aliases[i]; i++)
        printf("Aliases %d: %s \n", i+1, host->h_aliases[i]);
    
    printf("Address type: %s \n", 
        (host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");

    for(i=0; host->h_addr_list[i]; i++)
        printf("IP addr %d: %s \n", i+1,
                    inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

利用IP地址获取域名

#include <netdb.h>
struct hostent* gethostbyaddr(cosnt char* addr, socklen_t len, int family);
//成功时返回hostent结构体变量变量地址值,失败时返回NULL指针
/*
param
addr: 含有IP地址信息的in_addr结构体变量值
len: 向第一个参数传递的地址信息的字节数,IPV4为4,IPV6为16
family: 地址家族信息,IPv4为AF_INET, IPv6为AF_INET6
*/

gethostbyaddr.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int i;
    struct hostent *host;
    struct sockaddr_in addr;
    if(argc!=2) {
        printf("Usage : %s <IP>\n", argv[0]);
        exit(1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr=inet_addr(argv[1]);
    host=gethostbyaddr((char*)&addr.sin_addr, 4, AF_INET);
    if(!host)
        error_handling("gethost... error");

    printf("Official name: %s \n", host->h_name);

    for(i=0; host->h_aliases[i]; i++)
        printf("Aliases %d: %s \n", i+1, host->h_aliases[i]);
    
    printf("Address type: %s \n", 
        (host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");

    for(i=0; host->h_addr_list[i]; i++)
        printf("IP addr %d: %s \n", i+1,
                    inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));    
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}
最后修改:2021 年 11 月 01 日 05 : 55 PM
如果觉得我的文章对你有用,请随意赞赏