Linux系统应用编程(五)Linux网络编程(上篇)

这篇具有很好参考价值的文章主要介绍了Linux系统应用编程(五)Linux网络编程(上篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Linux系统应用编程(五)Linux网络编程(上篇)

一、网络基础

1.两个网络模型和常见协议

(1)OSI七层模型(物数网传会表应)
  • 物理层、数据链路层、网络层、传输层、会话层、表示层、应用层(自下到上)
(2)TCP/IP四层模型(网网传应)
  • 网络接口层(链路层)、网络层、传输层、应用层
(3)常见网络协议所属层

Linux系统应用编程(五)Linux网络编程(上篇)

2.字节序

(1)两种字节序

Linux系统应用编程(五)Linux网络编程(上篇)

(2)字节序转换函数

Linux系统应用编程(五)Linux网络编程(上篇)

3.TCP通信时序(三次握手、四次挥手)

以下均为简述,仅针对面试时能够有东西掰扯

(1)什么是"三次握手"和"四次挥手"
  • "三次握手"意思是TCP客户端和服务器建立连接需要3次通信的过程;
  • "四次挥手"意思是TCP客户端和服务器断开连接需要4次通信的过程。
(2)"三次握手"和"四次挥手"的过程
  • “三次握手”:客户端主动向服务器发起连接请求,也就是发送建立连接的标志位SYN,服务器收到该请求同意后回复一个SYN和ACK(应答标志位),表示服务器收到客户端的连接请求,客户端收到服务器SYN+ACK后,再向服务器发送ACK应答标志位,等到服务器收到后就完成了三次握手建立连接。

  • “四次挥手”:一般由客户端主动断开,发送FIN标志位给服务器后,客户端处于半关闭状态(也就是只能接收服务器数据,而不能发送数据);服务器接收到FIN后回复客户端ACK应答;接着服务器也会发送FIN给客户端,同时服务器也进入半关闭状态,直到客户端回复ACK给到服务器,连接断开。

    实际上,套接字在内核中实现了读、写两个缓冲区,半关闭就是关闭了写缓冲区

  • 【补充】上面说到客户端处于半关闭,为什么可以在第四挥手时给服务器回复ACK?

    半关闭只是关闭socket中的写缓冲区,此时客户端和服务器的socket连接并没有关闭,因此,在半关闭状态下,客户端仍然可以通过已经建立好的TCP连接给服务器回复ACK确认包来完成四次挥手的过程。

(3)为什么断开连接需要"四次挥手"
  • 导致TCP连接关闭需要四次挥手的直接原因:半关闭
  • 为什么:为了确保双方在关闭连接之前都能够完成必要的操作,并尽可能地减少因网络不稳定性造成的影响,以保证数据的可靠性。

二、Socket网络编程

1.网络地址结构体

Linux系统应用编程(五)Linux网络编程(上篇)

2.Socket编程API

(1)创建套接字socket( )

Linux系统应用编程(五)Linux网络编程(上篇)

(2)绑定地址bind( )

Linux系统应用编程(五)Linux网络编程(上篇)

(3)设置监听listen( )

Linux系统应用编程(五)Linux网络编程(上篇)

(4)等待连接accept( )

Linux系统应用编程(五)Linux网络编程(上篇)

(5)发起连接connect( )

Linux系统应用编程(五)Linux网络编程(上篇)

(6)设置地址复用setsockopt( )

Linux系统应用编程(五)Linux网络编程(上篇)

三、案例程序

本案例参考于抖音up@小飞有点东西《python全栈高级篇》,up的python视频很nb;以下为笔者学习后用C语言描述的版本

1.简易"模拟Linux终端"v1.0

【开发环境】 ubuntu22.04、CLion

【核心技术】 TCP网络编程、服务器多进程/多线程并发、解决粘包问题

【案例描述】 client接入server后,通过命令行输入Linux命令,由server执行后的结果发送给client。

【v1.0代码】 多进程实现服务器并发,父进程回收子进程避免僵尸进程,子进程和客户端通信。

至此,程序还有BUG未解决——粘包问题

#include "temp.h"   //many head files in it

/* 服务器socket结构体 */
struct ServerSocket{
    int sockfd;       //服务器socket文件描述符
    void (* socketBind)(int ,char *,int);   //给sockfd绑定地址函数
    void (* serverListen)(int , int);       //监听sockfd函数
    struct ClientSocket (* serverAccept)(int);  //建立连接函数
};

/* 客户端socket结构体 */
struct ClientSocket{
    int cfd;    //建立连接的socket文件描述符
    char ip[32];    //客户端IP
    int port;   //客户端Port
};

/* 服务器socket绑定地址信息函数实现 */
void socketBind(int sockfd,char *ip,int port){
    int retn;
    /* 初始化地址结构体sockaddr_in */
    struct sockaddr_in serAddr = {
            .sin_port = htons(port),
            .sin_family = AF_INET
    };
    inet_pton(AF_INET,ip,&serAddr.sin_addr.s_addr);
    /* 调用bind()绑定地址 */
    retn = bind(sockfd,(struct sockaddr *)&serAddr,sizeof(serAddr));
    if(retn == -1){
        perror("bind");
        exit(-1);
    }
    printf("<Server> bind address: %s:%d\n",ip,port);
}

/* 服务器socket监听函数实现 */
void serverListen(int sockfd,int n){
    int retn;
    retn = listen(sockfd,n);
    if(retn == -1){
        perror("listen");
        exit(-1);
    }
    printf("<Server> listening...\n");
}

/* 服务器建立连接函数实现,返回值为struct ClientSocket结构体 *
 * (包括建立连接的socket文件描述符、客户端信息) */
struct ClientSocket serverAccept(int sockfd){
    struct sockaddr_in clientAddr;
    socklen_t addrLen = sizeof(clientAddr);
    struct ClientSocket c_socket;
    c_socket.cfd = accept(sockfd,(struct sockaddr *)&clientAddr,&addrLen);
    if(c_socket.cfd == -1){
        perror("accept");
        exit(-1);
    }else{
        c_socket.port = ntohs(clientAddr.sin_port);
        inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,c_socket.ip,sizeof(clientAddr));
        return c_socket;
    }
}

/* 信号处理函数:回收子进程 */
void waitChild(int signum){
    wait(NULL);
}

int main(){
    /* 初始化服务器socket */
    struct ServerSocket ss = {
            .serverAccept = serverAccept,
            .socketBind = socketBind,
            .serverListen = serverListen
    };
    /* 设置端口复用 */
    int optval = 1;
    setsockopt(ss.sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));

    ss.sockfd = socket(AF_INET,SOCK_STREAM,0);
    ss.socketBind(ss.sockfd,"192.168.35.128",8880);
    ss.serverListen(ss.sockfd,128);
    
    /* 多进程实现服务器并发 */
    struct ClientSocket cs; //客户端socket
    pid_t pid = 1;
    int nread;
    while(1){   //循环等待客户端接入
        cs = ss.serverAccept(ss.sockfd);
        printf("<Server> client connected.(%s:%d)\n",cs.ip,cs.port);
        pid = fork();   //创建父子进程
        if(pid > 0){    //父进程
            close(cs.cfd);  //关闭通信的套接字
            signal(SIGCHLD,waitChild);  //注册信号
            continue;
        }else if(pid == 0){     //子进程
            close(ss.sockfd);  //关闭建立连接的socket
            while(1){
                char *writeBuff = (char *) malloc(2048);    //写buff
                char *readBuff = (char *) malloc(128);      //读buff
                FILE *buffFile = NULL;          //文件流
                while(1) {
                    nread = read(cs.cfd, readBuff, 128);   //读取客户端发过来的命令
                    /* 对read判空,防止客户端退出后一直收空数据的死循环 */
                    if (nread == 0) {
                        printf("<server> client disconnected (%s:%d)\n",cs.ip,cs.port);
                        break;
                    }
                    /* 执行客户端发过来的命令 */
                    buffFile = popen(readBuff, "r");
                    fread(writeBuff, 2048, 1, buffFile);    //命令执行成功结果读取到writeBuff
                    if (strlen(writeBuff) == 0) {
                        write(cs.cfd, "\n", 1);
                    }else{
                        write(cs.cfd, writeBuff, strlen(writeBuff));   //结果写回给客户端
                    }
                    /* 清空缓存数据,关闭流 */
                    memset(writeBuff, '\0', strlen(writeBuff));
                    memset(readBuff, '\0', strlen(readBuff));
                    pclose(buffFile);
                }
                return 0;
            }
        }else{
            perror("fork");
            exit(-1);
        }
    }
}

Linux系统应用编程(五)Linux网络编程(上篇)

Linux系统应用编程(五)Linux网络编程(上篇)

2.TCP粘包问题

(1)粘包问题引入
  • v1.0的服务器代码,只执行了ls、dir执行结果较短的命令,看似没有BUG,但是如果执行的是像ps -aux命令结果较长的,就可以发现,由于返回的结果较长,客户端一次读取并没有读取完(或者读取太快、缓存太小),当下一条命令执行后,结果就会和上一条命令没有读取完的内容连在一起。如图:

Linux系统应用编程(五)Linux网络编程(上篇)

  • 针对客户端读取数据太快,或客户端设置的缓存太小,虽然我们在代码中,用延时避免读取数据太快、设置较大的缓存区可以一定程度避免粘包问题,但是这种解决方法并不好,延时难免影响用户体验,过大的缓存区也不切实际。所以,需要从其他角度解决TCP的粘包问题。
(2)TCP粘包产生原因
  • TCP协议基于字节流传输数据,并不是基于消息,数据类似水流传输着,数据之间难以区分,所以不可避免出现将多个独立的数据包粘成一个数据包的情况;
  • TCP为了避免网络拥塞,减少网络负载而设计的底层优化算法Nagle算法,通过将多个小数据包合并成一个大数据包进行发送,以减少网络流量和传输延迟。当有大量小数据包需要发送时,Nagle算法会将这些数据包先缓存起来,并在缓存区中尝试组装成一个更大的数据包再进行发送。所以如果接收方不能及时地处理接收到的数据包,或者发送方的缓存区未被填满,那么就会导致TCP粘包问题的产生。
(3)解决粘包问题
  • 固定数据包的长度:每次发送读取都固定大小
  • 在数据头部加入数据的总长度:接收方先读取消息头中的长度信息,再根据长度信息读取对应长度的数据(实际上也就是<自定义协议>)
  • 特殊分割符:使用特殊的分割符(如\n或者\r\n)来分割每条数据
(4)自定义协议
  • 自定义协议通常包含两部分内容:

    1. 消息头:用于描述数据包的基本信息,如数据包类型、数据包长度等。

      例如:<文件传输>头部可以包括文件类型、文件的md5值、文件的大小等

    2. 消息体:用于存储具体的数据,如文本、图片、音频等。

  • 设计自定义协议时,需要遵循以下几个原则:

    1. 协议必须是可扩展的,能够容易地添加新的消息类型或字段。
    2. 消息的格式必须明确并符合规范,可以使用固定长度、分隔符、标记等方式来辨别消息的开始和结束。
    3. 在消息头中要包含足够的元信息,能够让接收方对消息进行正确的处理。
    4. 协议设计必须考虑网络上的安全问题,避免数据泄露和信息篡改等风险。
  • 自定义协议通常用于特定领域的应用,如游戏开发、嵌入式系统、金融交易等场景。自定义协议的设计和实现需要结合具体场景进行考虑,需要对网络协议有一定的了解,并且需要注意协议的可靠性、可扩展性和安全性等问题。

3.简易"模拟Linux终端"v2.0

【Server v2.0】 通过在数据头部加入数据的总长度,客户端先读取数据的总长度,决定本次读取的大小,解决粘包问题

#include "temp.h"   //many head files in it

/* 服务器socket结构体 */
struct ServerSocket{
    int sockfd;       //服务器socket文件描述符
    void (* socketBind)(int ,char *,int);   //给sockfd绑定地址函数
    void (* serverListen)(int , int);       //监听sockfd函数
    struct ClientSocket (* serverAccept)(int);  //建立连接函数
};

/* 客户端socket结构体 */
struct ClientSocket{
    int cfd;    //建立连接的socket文件描述符
    char ip[32];    //客户端IP
    int port;   //客户端Port
};

/* 数据结构体 */
struct Data{
    int headerLenth;	//数据头部长度
    long dataLenth;	//数据长度(命令执行成功的结果长度)
    char *dataBody;	//数据正文(命令执行成功的结果)
};

/* 服务器socket绑定地址信息函数实现 */
void socketBind(int sockfd,char *ip,int port){
    int retn;
    /* 初始化地址结构体sockaddr_in */
    struct sockaddr_in serAddr = {
            .sin_port = htons(port),
            .sin_family = AF_INET
    };
    inet_pton(AF_INET,ip,&serAddr.sin_addr.s_addr);
    /* 调用bind()绑定地址 */
    retn = bind(sockfd,(struct sockaddr *)&serAddr,sizeof(serAddr));
    if(retn == -1){
        perror("bind");
        exit(-1);
    }
    printf("<Server> bind address: %s:%d\n",ip,port);
}

/* 服务器socket监听函数实现 */
void serverListen(int sockfd,int n){
    int retn;
    retn = listen(sockfd,n);
    if(retn == -1){
        perror("listen");
        exit(-1);
    }
    printf("<Server> listening...\n");
}

/* 服务器建立连接函数实现,返回值为struct ClientSocket结构体 *
 * (包括建立连接的socket文件描述符、客户端信息) */
struct ClientSocket serverAccept(int sockfd){
    struct sockaddr_in clientAddr;
    socklen_t addrLen = sizeof(clientAddr);
    struct ClientSocket c_socket;
    c_socket.cfd = accept(sockfd,(struct sockaddr *)&clientAddr,&addrLen);
    if(c_socket.cfd == -1){
        perror("accept");
        exit(-1);
    }else{
        c_socket.port = ntohs(clientAddr.sin_port);
        inet_ntop(AF_INET,&clientAddr.sin_addr.s_addr,c_socket.ip,sizeof(clientAddr));
        return c_socket;
    }
}

/* 信号处理函数:回收子进程 */
void waitChild(int signum){
    wait(NULL);
}

/* 处理数据的函数,返回值为struct Data */
struct Data dataDealWith(FILE *file){
    char *tempBuff = (char *)malloc(8192);		//临时buff
    long readBytes = 0;			//读取的字节数
    struct Data data = {
            .dataLenth = 0,
            .dataBody = NULL
    };
    /* 处理数据:计算数据正文大小,并保留管道中的数据到data.dataBody(需要动态调整大小) */
    while(fread(tempBuff,sizeof(char),8192,file) > 0){
        readBytes = strlen(tempBuff)+1;   //读到临时buff的字节数
        data.dataLenth += readBytes;      //数据长度累加readBytes
        if(data.dataLenth <= readBytes){	//如果数据长度小于设置的tempBuff大小,直接拷贝
            data.dataBody = (char *)malloc(readBytes);	
            strcpy(data.dataBody,tempBuff);
        }else if(data.dataLenth > readBytes){	//如果数据长度大于设置的tempBuff大小,扩容后拼接到后面
            data.dataBody = realloc(data.dataBody,data.dataLenth);
            strcat(data.dataBody,tempBuff);
        }
        data.dataBody[strlen(data.dataBody)+1] = '\0';
        memset(tempBuff,'\0',8192);
    }
    free(tempBuff); //释放临时buff
    return data;
}

int main(){

    /* 初始化服务器socket */
    struct ServerSocket ss = {
            .serverAccept = serverAccept,
            .socketBind = socketBind,
            .serverListen = serverListen
    };

    /* 设置端口复用 */
    int optval = 1;
    setsockopt(ss.sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));

    ss.sockfd = socket(AF_INET,SOCK_STREAM,0);
    ss.socketBind(ss.sockfd,"192.168.35.128",8880);
    ss.serverListen(ss.sockfd,128);

    /* 多进程实现服务器并发 */
    struct ClientSocket cs; //客户端socket
    pid_t pid = 1;
    int nread;
    while(1){   //循环等待客户端接入
        cs = ss.serverAccept(ss.sockfd);
        printf("<Server> client connected.(%s:%d)\n",cs.ip,cs.port);
        pid = fork();   //创建父子进程
        if(pid > 0){    //父进程
            close(cs.cfd);  //关闭通信的套接字
            signal(SIGCHLD,waitChild);  //注册信号
            continue;
        }else if(pid == 0){     //子进程
            close(ss.sockfd);  //关闭建立连接的socket
            while(1){
                char *readBuff = (char *) malloc(128);      //读buff
                FILE *buffFile = NULL;          //文件流
                struct Data data;
                char head[8];
                while(1) {
                    nread = read(cs.cfd, readBuff, 128);   //读取客户端发过来的命令
                    /* 对read判空,防止客户端退出后一直收空数据的死循环 */
                    if (nread == 0) {
                        printf("<server> client disconnected (%s:%d)\n",cs.ip,cs.port);
                        break;
                    }
                    /* 执行客户端发过来的命令 */
                    buffFile = popen(readBuff, "r");    //命令执行成功结果读取到writeBuff
                    data = dataDealWith(buffFile);
                    sprintf(head,"%ld",data.dataLenth);
                    write(cs.cfd,head, 8);
                    write(cs.cfd,data.dataBody,data.dataLenth);
                    memset(readBuff, '\0', strlen(readBuff));
                    memset(&data,0,sizeof(data));
                    pclose(buffFile);
                }
                exit(1);
            }
        }else{
            perror("fork");
            exit(-1);
        }
    }
}

【Client v2.0】

#include "temp.h"

int main(){
    int fd = socket(AF_INET,SOCK_STREAM,0);
    if(fd == -1){
        perror("socket");
        exit(-1);
    }

    struct sockaddr_in serAddr = {
            .sin_family = AF_INET,
            .sin_port = htons(8880)
    };
    inet_pton(AF_INET,"192.168.35.128",&serAddr.sin_addr.s_addr);

    int retn = connect(fd,(struct sockaddr *)&serAddr,sizeof(serAddr) );
    if(retn == -1){
        perror("connect");
        exit(-1);
    }

    char *writeBuff = (char *)malloc(128);
    char *readBuff = (char *)malloc(1024);
    char *header = (char *)malloc(8);
    int nread = 0;
    int dataLength = 0;
    while(1){
        printf("user@ubuntu-22.04:");
        fgets(writeBuff,128,stdin);
        if(*writeBuff == ' ' || *writeBuff == '\n'){
            continue;
        }
        write(fd,writeBuff, strlen(writeBuff));
        read(fd,header,8);
        if(atol(header) == 0)continue;
        printf("header:%ld\n", atol(header));
        while(dataLength <= atol(header)){
            read(fd,readBuff,1024);
            dataLength += strlen(readBuff)+1;
            printf("%s",readBuff);
            memset(readBuff,'\0', 1024);
            if(dataLength >= atol(header)){
                dataLength = 0;
                break;
            }
        }
        memset(header,'\0', strlen(header));
        memset(writeBuff,'\0', strlen(writeBuff));
        printf("done\n");
    }
}

Linux系统应用编程(五)Linux网络编程(上篇)
Linux系统应用编程(五)Linux网络编程(上篇)
Linux系统应用编程(五)Linux网络编程(上篇)文章来源地址https://www.toymoban.com/news/detail-424762.html

到了这里,关于Linux系统应用编程(五)Linux网络编程(上篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 谈谈linux网络编程中的应用层协议定制、Json序列化与反序列化那些事

    由于socket api的接口,在读写数据的时候是以字符串的方式发送接收的,如果需要传输 结构化的数据 ,就需要制定一个协议 结构化数据在发送到网络中之前需要完成序列化 接收方收到的是序列字节流,需要完成反序列化才能使用(如ChatInfo._name) 当我们进行网络通信的的时

    2024年02月06日
    浏览(61)
  • 【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现

    这里我们会实现一个项目:在linux操作系统下基于OpenCV和Socket的人脸识别系统。 目录 🌞前言 🌞一、项目介绍 🌞二、项目分工 🌞三、项目难题 🌞四、实现细节 🌼4.1 关键程序 🌼4.2 运行结果 🌞五、程序分析 🌷5.1 wkcv.link 🌷5.2 客户端client.cpp 🌷5.3 服务端server.cpp 项目

    2024年03月12日
    浏览(59)
  • C++ 网络编程项目fastDFS分布式文件系统(四)-fastCGI项目相关技术以及linux搜狗输入法相关问题。

    目录 1. Nginx作为web服务器处理请求 2. http协议复习       Get方式提交数据 Post方式提交数据 3. fastCGI   3.1 CGI  3.2 fastCGI 3.3 fastCGI和spawn-fcgi安装         1. 安装fastCGI     2. 安装spawn-fcgi 3.4 nginx fastcgi     4其他知识点 1. fastCGI环境变量 - fastcgi.conf  2. 客户端使用Post提交数据

    2024年02月12日
    浏览(45)
  • 【Linux网络编程】网络编程套接字二

    喜欢的点赞,收藏,关注一下把! TCP和UDP在编程接口上是非常像的,前面我们说过TCP是面向连接的,UDP我们上篇博客也写过了,我们发现UDP服务端客户端写好启动直接就发消息了没有建立连接。TCP是建立连接的,注定在写的时候肯定有写不一样的地方。具体怎么不一样,我们

    2024年04月15日
    浏览(63)
  • Linux网络编程——UDP编程

    1、UDP通信协议,服务器端和客户端无需建立连接,只需要知道对方套接字的地址信息就可以发送数据 2、UDP通信流程图: 功能:创建套接字并返回套接字描述符 功能:将套接字与IP地址和端口号绑定 功能:发送数据 功能:接收数据 功能:关闭套接字 1、代码功能:两个进程

    2023年04月19日
    浏览(45)
  • 【网络编程】Linux网络编程基础与实战第三弹——网络名词术语

    数据包从源地址到目的地址所经过的路径,由一系列路由节点组成。 某个路由节点为数据包选择投递方向的选路过程。 路由器工作原理 路由器是连接因特网中各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按前后顺序发送信号的设备。

    2024年02月08日
    浏览(46)
  • 高并发应用:TCP网络编程

    Socket 很多系统都提供Socket作为TCP网络连接的抽象 Linux- internet domain socket - SOCK_STREAM Linux中Socket以“文件描述符”FD作为标识。 IO模型 IO模型指的是同时操作Socket的方案。 阻塞 非阻塞 多路复用 阻塞IO 同步读写Socket时,线程陷入内核态。 当读写成功后,切换回用户态,继续执行

    2024年03月14日
    浏览(45)
  • Linux:概述 、安装 、文件与目录结构 、vim编辑器 、网络配置 、远程登录 、系统管理 、基础命令 、软件包管理 、克隆虚拟机 、shell编程

    2.1.1、Linux是什么? Linux是一个操作系统(OS) 所谓的操作系统就是直接用来操作计算机底层硬件的软件。 2.1.2、Linux的出现 官网: https://www.centos.org/ 进入官网进行下载 有很多的镜像,以阿里云的为例: 3.3.1、下载 官网: https://www.vmware.com/ 这是下载的企业版,30天试用期,可

    2024年02月05日
    浏览(65)
  • Linux网络编程:网络基础

    文章目录: 一:协议   二:网络应用设计模式_BS模式和CS模式 三:网络分层模型(OSI七层 TCP/IP四层) 四:通信过程 五:协议格式  1.数据包封装 2.以太网帧格式和ARP数据报格式  3.IP段格式  4.UDP数据报格式 5.TCP数据报格式 六:TCP协议 1.TCP通信时序(面向连接的可靠数据通

    2024年02月12日
    浏览(83)
  • 【Linux网络编程】网络编程套接字(TCP服务器)

    作者:爱写代码的刚子 时间:2024.4.4 前言:本篇博客主要介绍TCP及其服务器编码 只介绍基于IPv4的socket网络编程,sockaddr_in中的成员struct in_addr sin_addr表示32位 的IP地址 但是我们通常用点分十进制的字符串表示IP地址,以下函数可以在字符串表示和in_addr表示之间转换 字符串转in

    2024年04月14日
    浏览(79)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包