IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 《TCP/IP网络编程》第6、7、8章知识点汇总 -> 正文阅读

[网络协议]《TCP/IP网络编程》第6、7、8章知识点汇总

6. 基于UDP的服务器端/客户端

6.1 理解UDP

TCP和UDP最重要的区别在于 流控制
这里的流控制应该包含了TCP的可靠传输、流量控制、拥塞控制等机制,这些机制都是在流上实现的
TCP更可靠,UDP更高效(TCP速度一般低于UDP,当每次传输数据很大时,两者速率会接近一点)
TCP在传输数据之前要建立连接(三次握手),而UDP不用。后者不询问接收方,直接发送数据

image-20221003223725291

IP层将数据包传给主机B,而UDP就是将自己主机收到的数据包交给正确的套接字

TCP慢于UDP的原因:
(1)收发数据前后进行的连接设置及清除过程
(2)收发数据过程中为保证可靠性而添加的流控制
因此,当收发的数据量小但需要频繁连接时,UDP比TCP高效

深入学习:TCP/IP协议的内部构造

6.2 基于UDP的服务器端和客户端

UDP无需建立连接,因此不需要TCP中的listen和accept这两个步骤

TCP是一对一的,有多少客户端套接字,服务器端就需要创建多少个对应的套接字
UDP中,服务器端和客户端都仅需要1个套接字,就可以应对所有的数据传输请求(相当于每个家只需要有一个邮筒)

6.2.1 I/O函数

在TCP中,使用read和write时,只有3个参数,不需要指定对方套接字地址。因为之前在建立连接中就已经沟通过双方地址了
在UDP中,使用的是sendto和recvfrom函数,各6个参数。因此没有连接,所以在收发时要指明对方地址

  1. sendto()
#include <sys/socket.h>
ssize_t sendto(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *to, socklen_t addrlen);
//成功时返回传输的字节数,失败时返回-1

sock 用于传输数据的UDP套接字文件描述符。
buff 保存待传输数据的缓冲地址值。
nbytes 待传输的数据长度,以字节为单位。
flags 可选项参数,若没有则传递0。
to 存有目标地址信息的sockaddr结构体变量的地址值。
addrlen 传递给参数to的地址值结构体变量长度。

  1. recvfrom()
#include <sys/socket.h>
ssize_t recvfrom(int sock, void *buff, size_t nbytes, int flags, struct sockaddr *from, socklen_t *addrlen);
//成功时返回传输的字节数,失败时返回-1

sock 用于接收数据的UDP套接字文件描述符。
buff 保存接收数据的缓冲地址值。
nbytes 可接收的最大字节数,故无法超过参数buff所指的缓冲大小。
flags 可选项参数,若没有则传递0。
from 存有发送端地址信息的sockaddr结构体变量的地址值。
addrlen 保存参数from的结构体变量长度的变量地址值。

6.2.2 echo服务器端/客户端

UDP没有连接,从某种角度来说无法明确区分服务器端和客户端。这里将提供服务的一方成为服务器端

uecho_server.c

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

#define BUF_SIZE 1024

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

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

    int serv_sock, clnt_sock;
    struct sockaddr_in serv_addr, clnt_addr;
    int clnt_addr_size;
    int i, str_len;
    char message[BUF_SIZE];

    if(argc != 2)
    {
        error_handling("wrong argc");
        exit(1);
    }
    //1. socket
    serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
    if(serv_sock == -1)
        error_handling("socket() error!");
    //2. bind
    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!");
    //3. recvfrom和sendto直接收发数据
    while(1)
    {
        clnt_addr_size = sizeof(clnt_addr);
        str_len=recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
        printf("receive: %s", message);
        sendto(serv_sock, message, str_len, 0, (struct sockaddr *)&clnt_addr, clnt_addr_size);
    }
    close(serv_sock);
    return 0;
}

uecho_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)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

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

    if(argc != 3)
    {
        error_handling("wrong argc");
        exit(1);
    }
	//1. socket
	sock = socket(PF_INET, SOCK_DGRAM, 0);
    if(sock == -1)
        error_handling("socket() error!");
	//2. 设置服务器端的地址,但不再需要connect
	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]));
    //3. sendto和recvfrom收发数据,和服务器端一样
    while(1)
    {
        fputs("Input message(Q to quit): ", stdout);
        fgets(message, BUF_SIZE, stdin);
        //strcmp相等返回0
        if(!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
            break;
        
        sendto(sock, message, strlen(message), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
        from_addr_size = sizeof(from_addr);
        str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr *)&from_addr, &from_addr_size);
        message[str_len] = 0;
        printf("Message from server: %s", message);
    }
	close(sock);
	return 0;
}

在客户端代码中:
str_len = recvfrom(sock, message, BUF_SIZE, 0, (struct sockaddr *)&from_addr, &from_addr_size);
这一句也可以将from_addr修改为serv_addr。用另一个from_addr是为了避免收到其他方的信息,而将serv_addr覆盖掉
服务器端没有这个问题

UDP客户端套接字地址分配

在UDP中,服务器端和客户端没有那么明显的区别

sendto函数在调用的时候,如果发现之前没有使用bind函数,那么将自动分配IP地址和随机的端口号

一般服务器端会使用bind,客户端不使用

6.3 UDP的数据边界

TCP中不存在数据边界,但UDP是具有数据边界的协议
也就是说一方多少次sendto,另一方就应该多少次recvfrom

测试

UDP服务器端

for(i=0; i<3; ++i){
    sleep(5);//延迟5秒接收,等待客户端3次发送完毕
    clnt_addr_size = sizeof(clnt_addr);
    str_len=recvfrom(serv_sock, message, BUF_SIZE, 0, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
    printf("Message %d: %s\n", i+1, message);
}

UDP客户端

char msg1[] = "Hi";
char msg2[] = "This is udp client";
char msg3[] = "Nice to meet you";
sendto(sock, msg1, strlen(msg1), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
sendto(sock, msg2, strlen(msg2), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
sendto(sock, msg3, strlen(msg3), 0, (struct sockaddr *)&serv_addr, sizeof(serv_addr));

UDP结果

image-20221004000632384

每隔5秒,读入一次,和客户端的发送结果一样

TCP结果

image-20221004001722124

用一样含义的代码,使用TCP时,会发现服务器端一次性读完了3次传来的数据。这就是没有数据边界的限制

6.4 UDP使用connect

sendto传输数据有3个阶段
(1)向UDP套接字注册目标IP和端口号
(2)传输数据
(3)删除UDP套接字中注册的目标地址信息
其中第1和第3个步骤占整个通信过程的约1/3
UDP默认无连接,但对于要多次sendto,这样的方式影响性能

可以创建连接的套接字,只需要对UDP套接字使用 connect

connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
//如果对UDP使用了connect,那么不仅可以使用sendto和recvfrom,还可以使用write和read
write(sock, message, strlen(message));

同样的,客户端3次发送,服务器端3次接收

注意:即便使用了connect,UDP也不会进行三次握手

6.5 windows平台

使用的是sendto和readfrom,其他类似

#include <winsock2.h>
int sendto(SOCKET s, const char* buf, int len, int flags, const struct sockaddr *to, int tolen);
//成功时返回传输的字节数,失败时返回SOCKET_ERROR
#include <winsock2.h>
int recvfrom(SOCKET s, char* buf, int len, int flag, struct sockaddr *from,int *fromlen) ;
//成功时返回接收的字节数,失败时返回SOCKET_ERROR

注意:

  1. recvfrom接收的大小一点要大于等于sendto/send发送的大小

  2. 关闭服务器端后,客户端按理不能再收发数据。但一个有意思的现象是,在windows中,正在连接的服务器端关闭后,客户端会找相同地址的服务器端继续连接

例如uecho_server_win断开后,这里连接到了echo_server_win(可能是之前的echo_server_win没能关掉?)

uecho_server_win.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE 1024

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

int main(int argc, char *argv[])
{   
    WSADATA wsaData;
    SOCKET serv_sock;
    SOCKADDR_IN serv_addr, clnt_addr;
    char buf[BUF_SIZE];
    int recv_len, clnt_addr_size;

    if(argc != 2)
        error_handling("wrong argc");
    //WSAStartup
    if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
        error_handling("WSAStartup() error");
    //socket
    serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
    if(serv_sock == INVALID_SOCKET)
        error_handling("socket() error");
    //bind
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[1]));
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(serv_sock, (SOCKADDR *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
        error_handling("bind() error");
    //收发数据
    while(1)
    {
        clnt_addr_size = sizeof(clnt_addr);
        //接收
        recv_len = recvfrom(serv_sock, buf, BUF_SIZE-1, 0, (SOCKADDR *)&clnt_addr, &clnt_addr_size);
        if(recv_len == SOCKET_ERROR)
            error_handling("recvfrom() error");
        buf[--recv_len] = 0;//删除末尾的换行符(客户端输入时的那个回车符会被传过来),增加结尾符
        printf("received from client: %s\n", buf);
        //发送
        strcat(buf, "_return");
        sendto(serv_sock, buf, recv_len+7, 0, (SOCKADDR *)&clnt_addr, sizeof(clnt_addr));
    }
    closesocket(serv_sock);
    WSACleanup();
    return 0;
}

uecho_client_win.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>

#define BUF_SIZE 1024

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

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET clnt_sock;
    SOCKADDR_IN serv_addr, clnt_addr;
    char buf[BUF_SIZE];
    int recv_len;

    if(argc != 3)
        error_handling("wrong argc");
    //WSAStartup
    if(WSAStartup(MAKEWORD(2, 2), &wsaData) == -1)
        error_handling("WSAStartup() error");
    //socket
    clnt_sock = socket(PF_INET, SOCK_DGRAM, 0);
    if(clnt_sock == INVALID_SOCKET)
        error_handling("socket() error");
    //connect
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);
    if(connect(clnt_sock, (SOCKADDR *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)
        error_handling("connect() error");
    //收发数据
    int cln_size = 0;
    while(1)
    {
        fputs("Input message (Q to quit): ", stdout);
        fgets(buf, BUF_SIZE, stdin);
        if(!strcmp(buf, "q\n") || !strcmp(buf, "Q\n"))
            break;
        send(clnt_sock, buf, strlen(buf), 0);
        recv_len = recv(clnt_sock, buf, BUF_SIZE-1, 0);
        buf[recv_len] = 0;
        printf("Message from server: %s\n", buf);
    }
    closesocket(clnt_sock);
    WSACleanup();
    return 0;
}

7. 优雅地断开套接字连接

7.1 基于TCP的半关闭

1. 单方面断开连接带来的问题

场景:主机A B正在通信。主机A发送完最后的数据后就调用close()断开了连接。此时主机A无法再接收数据,于是在收到“断开”信息之前,这期间主机B传过去的数据也就销毁了。

解决方法:Half-close,连接的半关闭。指可以传输,但无法接收。或者可以接收,但无法传输

2. 套接字和流(Stream)

A B两个主机建立连接之后,各拥有2个流,分别是输入流和输出流。A的输入流和B的输出流对接,A的输出流和B的输入流对接
Half-close 就是指断开其中的一个流连接
close()和closesocket()都是同时断开2个流

shutdwon() 函数

用于半关闭的函数shutdown,关闭其中一个流

int <sys/socket.h>
int shutdown(int sock, int howto);
//成功时返回0,失败时返回-1

sock 需要断开的套接字文件描述符
howto 传递断开方式信息

image-20221004162232113

半关闭的理解

场景:服务器端向客户端发送一个文件,客户端收到之后返回给服务器端 “thank you”

问题1:服务器端只需要不断发送数据直至完毕,但客户端不知道接收完毕,一直 read(),等得不到数据将陷入阻塞状态
解决:约定一个文件结束符(这个结束符不能和出现在文件内容,因此只能通过单独传输一次结束符来避免)

问题2:服务器端发送了 EOF 文件结束符后,调用close()关闭连接。客户端返回的 “thank you” 无法被接收
解决:客户端在发送EOF后,只关闭输出流,保留输入流

基于半关闭的文件传输程序

file_server.c

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

#define BUF_SIZE 1024

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

    int serv_sock, clnt_sock;
    FILE *fp;
    struct sockaddr_in serv_addr, clnt_addr;
    int clnt_addr_size;
    int i, read_cnt;
    char buf[BUF_SIZE];

    if(argc != 2)
    {
        printf("wrong argc\n");
        exit(1);
    }
    //打开文件
    fp = fopen("file_server.c", "rb");//r表示只读,b表示二进制(文件必须已存在)
    //socket
    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    //2. bind
    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]));
    bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    //listen
    listen(serv_sock, 5);
    //accept
    clnt_addr_size = sizeof(clnt_addr);
    clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_addr, &clnt_addr_size);
    //读文件,发送数据
    while(1)
    {
        read_cnt = fread((void *)buf, 1, BUF_SIZE, fp);
        printf("send message %d\n", read_cnt);
        if(read_cnt<BUF_SIZE)
        {
            write(clnt_sock, buf, read_cnt);
            break;
        }
        write(clnt_sock, buf, BUF_SIZE);
    }
    //半关闭socket
    shutdown(clnt_sock, SHUT_WR);//关闭输出流,保留输入流
    read(clnt_sock, buf, BUF_SIZE);
    printf("Message from client: %s\n", buf);
    //关闭socket
    fclose(fp);
    close(clnt_sock);
    close(serv_sock);
    return 0;
}

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 1024

int main(int argc, char* argv[])
{
	int clnt_sock;
    FILE *fp;
	struct sockaddr_in serv_addr, from_addr;
	char buf[BUF_SIZE];
    int from_addr_size;
	int read_cnt;

    if(argc != 3)
    {
        printf("wrong argc");
        exit(1);
    }
    //打开文件
    fp = fopen("receive.dat", "wb");//不存在则创建,存在则覆盖
	//socket
	clnt_sock = socket(PF_INET, SOCK_STREAM, 0);
	//connect
	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]));
	connect(clnt_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    //接收文件,并返回 thank you
    while((read_cnt = read(clnt_sock, buf, BUF_SIZE)) != 0)
        fwrite((void *)buf, 1, read_cnt, fp);
    puts("Received file data");
    write(clnt_sock, "Thank you", 10);
    //关闭
    fclose(fp);
	close(clnt_sock);
    
	return 0;
}

结果和说明

上述程序能够完成之前的场景要求
如果服务器端删除如下语句:

shutdown(clnt_sock, SHUT_WR);//关闭输出流,保留输入流

那么客户端将陷入阻塞,直到服务器端调用close()函数,才会read()完毕。但这样就导致服务器端无法接收到发来的 ”Thank you“

7.2 windows下的Half_close

#include <winsock2.h>
int shutdown(SOCKET sock, int howto);
//成功时返回0,失败时返回SOCKET_ERROR

注意第二个参数的值写法有所不同

image-20221004215422743

分别以0,1,2表示,和linux下的是一样的,只是名字不同

实现:略

8. 域名及网络地址

DNS,Domain Name System,域名系统,用于IP地址和域名的相互转换,核心是DNS服务器

百度的IP地址:39.156.66.18(不唯一)
百度的域名:www.baidu.com

输入以上的任意一个都可以打开百度。但实际上,输入域名时,需要通过DNS服务器将域名转换为IP地址
所有计算机中都记录着默认DNS服务器地址,就是通过这个默认DNS服务器得到相应域名的IP地址信息

一般不会更改域名,但会更改IP地址。编写程序时应当多使用域名而非IP地址
查看域名对应的IP地址:ping www.baidu.com
查看默认DNS服务器地址:nslookup (linux中还要根据提示信息输入 server)

image-20221004221156949

默认DNS如果找不到该域名,则逐级向上询问。最后根DNS会知道向哪个DNS服务器请求解析域名。最后解析得到的IP地址原路返回
DNS是一种分布式数据库系统

当默认DNS找到该域名时:

image-20221004234713481

DNS是将域名转为IP地址,路由器根据IP地址选择路径。DNS和路由器是不同的概念
DNS和操作系统无关

8.1 利用域名获取IP地址 hostent

根据字符串格式的域名获取到ip地址

#include <netdb.h>
struct hostent * gethostbyname(const char *hostname);
//成功时返回hostent结构体地址,失败时返回NULL指针

hostent结构体

struct hostent
{
    char *h_name;		//official name
    char **h_aliases;	//alias list
    int h_addrtype;		//host address type
    int h_length;		//address length
    char **h_addr_list;	//address list
};
//重点是h_addr_list
h_name	官方域名,但有些公司并未用官方域名注册
h_aliases	可以通过多个城名访问同一主页。同一IP可以绑定多个域名,因此,除官方域名外还可指定其他域名
h_addrtype	获取保存在h_addr_list中的IP地址的地址族信息,如果是IPv4,则保存的是AF_INET
h_length	IP地址长度,如果是IPv4,就是4;如果是IPv6,就是16
h_addr_list	通过此变量以整数形式保存域名对应的IP地址
image-20221004223027184

其中,h_addr_list其实是一个指针数组,数组中每个元素都是in_addr型指针

没有写成 in_addr** 是为了提高通用性,因为char *和in_addr *都是4字节的指针
现在一般用void *来处理这种情况,但当时定义套接字时是在void指针标准化之前,那时使用char *指代不明确类型

image-20221004225059723

测试

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

int main(void)
{
    int i;
    char domain[30] = "www.baidu.com";
    struct hostent *host;

    host = gethostbyname(domain);
    if(!host)
    {
        printf("gethostbyname() error\n");
        exit(1);
    }

    //查看IP地址信息
    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");
    printf("Address length: %d\n", host->h_length);
    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;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NDkZN5g-1665554664082)(C:\Users\jiang\AppData\Roaming\Typora\typora-user-images\image-20221004224859132.png)]

8.2 利用IP地址获取域名

#include <netdb.h>
struct hostent * gethostbyaddr(const char *addr, socklen_t len, int family);
//成功时返回hostent结构体地址,失败时返回NULL指针
addr	含有IP地址信息的in_addr结构体指针。为了同时传递IPv4地址之外的其他信息,该变量的类型声明为char指针
len		向第一个参数传递的地址信息的字节数, IPv4时为4,IPv6时为16
family	传递地址族信息,IPv4时为AF_INET,IPv6时为AF_INET6

测试

问题:
使用gethostbyaddr时,使用网上查到的百度ip地址202.108.22.5可以成功解析
但是使用gethostbyname得到的ip地址39.156.66.18返回的是NULL,尽管这一ip能ping通(原因?)

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

int main(void)
{
    int i;
    //char ip[30] = "39.156.66.18";
    //char ip[30] = "74.125.19.106";
    char ip[30] = "202.108.22.5";
    struct hostent *host;

    struct in_addr addr;
    addr.s_addr = inet_addr(ip);

    host = gethostbyaddr((char *)&addr, 4, AF_INET);
    if(!host)
    {
        printf("gethostbyname() error\n");
        perror("gethostbyaddr");
        exit(1);
    }

    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");
    printf("Address length: %d\n", host->h_length);
    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;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FYW4kxfH-1665554664084)(C:\Users\jiang\AppData\Roaming\Typora\typora-user-images\image-20221004232629918.png)]

8.3 windows平台

有差不多的函数

#include <winsock2.h>
struct hostent * gethostbyname(const char * name);
//成功时返回hostent结构体变量地址值,失败时返回NULL指针。
struct hostent * gethostbyaddr(const char *addr,int len, int type);
//成功时返回hostent结构体变量地址值,失败时返回NULL指针。

在windows平台下,代码上除了要修改头文件和添上WSA语句,其他一样;
结果也一样

9. 套接字的多种可选项

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 13:08:02  更:2022-10-17 13:08:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 21:16:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码