网络编程初学者必备:从零开始的详细教程与资源汇总

简介: 网络编程初学者必备:从零开始的详细教程与资源汇总

目录

1. IP地址特点

2. IP地址分类

2.1 普通地址

2.2 特殊地址

3. 子网掩码

3.1 特点

3.2 作用

4. 地址划分

5. 网络模型

5.1 OSI模型

5.2 TCP/IP模型

UDP与TCP传输协议及Socket套接字详解

6. UDP与TCP对比

6.1 共同点

6.2 TCP传输协议

6.3 UDP传输协议

7. Socket套接字

7.1 概念

7.2 功能

7.3 类型

7.4 端口号

7.5 字节序

端口分配

7.6 端口号字节序转换

7.7 IP地址字节序转换

7.8 TCP编程

7.8.1 socket 创建套接字

7.8.2 bind 绑定套接字

7.8.3 listen 监听

7.8.4 accpt 阻塞等待连接

7.8.5 recv 接受缓存区的内容

7.9 UDP 编程

7.9.1 函数接口(recvfrom  sendto)

9.1 循环服务器

9.2 并发服务器

线程实现并发的代码

并发服务器总结:

12.1 概念:同样也是基于setsockopt实现的文件属性设置

12.2 组播的地址

12.3 组播的客户端

12.4 组播的服务器

14.1 特性


IP地址与网络通信基础详解

1. IP地址特点

  • IP地址是互联网中唯一标识主机的地址,采用二进制形式表示。
  • 主机间通信需具备IP地址,用于区分不同主机。
  • 分为IPv4(32位)和IPv6(128位)。
  • 每个数据包需携带源IP和目标IP信息。
  • 表示方法为点分十进制,由四个0-255的十进制数组成,每组对应8位二进制。

2. IP地址分类

2.1 普通地址
  • A类:范围0.0.0.0-127.255.255.255,子网掩码255.0.0.0。
  • B类:范围128.0.0.0-191.255.255.255,子网掩码255.255.0.0。
  • C类:范围192.0.0.0-223.255.255.255,子网掩码255.255.255.0。
  • D类:范围224.0.0.0-239.255.255.255,用于多播。
  • E类:保留地址,范围240.0.0.0-255.255.255.255。
2.2 特殊地址
  • 0.0.0.0:匹配本机所有IPv4地址。
  • 127.0.0.1:回环地址,用于本机测试。
  • 网络地址:主机号全为0的地址,如192.168.50.0。
  • 广播地址:主机号最大的地址,用于向同一网段内所有主机发送信息。
  • 全网广播地址:255.255.255.255,慎用,可能影响整个网络。

3. 子网掩码

3.1 特点
  • 与IP地址长度相同,共32位。
  • 网络号全为1,主机号全为0。
3.2 作用
  • 划分IP地址的网络部分和主机部分。

4. 地址划分

  • 二级地址:IP = 网络号 + 主机号
  • 三级地址:引入子网掩码,细化网络划分。

5. 网络模型

5.1 OSI模型
  • 物理层:转换二进制为电信号。
  • 数据链路层:封装数据为帧,进行错误检测。
  • 网络层:负责IP寻址。
  • 传输层:标识进程,通过端口号寻址。
  • 会话层:建立和维护主机间通信。
  • 表示层:数据加密和格式化。
  • 应用层:提供各种应用服务。

image.gif 编辑

5.2 TCP/IP模型
  • 应用层:应用程序和协议集合。
  • 传输层:端口寻址,决定数据流向。
  • 网络层:IP寻址。
  • 网络接入层:实现物理和数据链路层功能,提供统一接口。

UDP与TCP传输协议及Socket套接字详解

6. UDP与TCP对比

6.1 共同点
  • 位于传输层,支持全双工通信。
6.2 TCP传输协议
  • 面向连接,提供可靠通信。
  • 数据传输过程中若网络中断,需重新开始。
  • 适用于对数据可靠性要求高的场景,如QQ、微信的登录或密码修改。
6.3 UDP传输协议
  • 无连接,数据传输不可靠。
  • 网络中断后可继续传输未完成部分。
  • 适用于实时性要求高但对数据丢失容忍度高的场景,如QQ、微信视频通话。

7. Socket套接字

7.1 概念
  • 编程接口,用于网络通信。
  • 特殊的文件描述符,用于数据收发。
  • 不仅限于TCP/IP协议,支持多种通信机制。
  • 包括面向连接和无连接两种类型。
7.2 功能
  • 内核中创建接收和发送缓存区。
  • 提供用户空间访问内核空间的文件描述符。
7.3 类型
  • 流式套接字:面向连接,保证数据的可靠、无差错传输。
  • 数据报套接字:无连接,数据传输不可靠,适用于不需要保证数据完整性的场景。
  • 原始套接字:底层协议访问,灵活性高,但使用复杂。
7.4 端口号
  • 用于区分主机上的不同进程,确保数据包正确传递。
  • TCP与UDP端口号独立,允许同一端口号用于不同协议。
  • 端口号由IANA管理,用于标准化服务分配。
  • 占用2字节,与IP地址共同定位网络中的具体进程。
7.5 字节序
  • 小端模式:低地址存储低位字节。
  • 大端模式:低地址存储高位字节。

端口分配

  • 众所周知端口(1~1023):系统级服务使用,如TFTP端口69。
  • 已登记端口(1024~49151):常用服务端口,用于建立会话。
  • 动态或私有端口(49152~65535):临时分配,用于特定服务或私有应用。

image.gif 编辑

7.6 端口号字节序转换

  • 网络传输采用网络字节序,终端输入输出使用主机字节序。
  • 主机字节序到网络字节序转换函数:
  • htonl: 转换32位长整型(u_long)数据。
  • htons: 转换16位短整型(u_short)数据,通常用于端口号转换。
  • 网络字节序到主机字节序转换函数:
  • ntohl: 转换32位长整型数据。
  • ntohs: 转换16位短整型数据,适用于端口号转换。
  • 头文件:#include <arpa/inet.h>

7.7 IP地址字节序转换

  • inet_addr()函数用于将点分十进制的IP地址(主机字节序)转换为网络字节序。
  • 头文件:#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>
  • 数据类型定义:typedef uint32_t in_addr_t;
  • 结构体定义:struct in_addr { in_addr_t s_addr; };
  • 函数原型:in_addr_t inet_addr(const char *strptr);
  • 功能:将点分十进制的IP地址字符串转换为网络字节序的无符号32位整型数。
  • 参数:const char *strptr为点分十进制IP地址字符串。
  • 返回值:成功转换返回无符号32位整型数,转换失败返回INADDR_NONE

image.gif 编辑

image.gif 编辑

7.8 TCP编程
7.8.1 socket 创建套接字
int socket(int domain, int type, int protocol);
头文件: #include <sys/types.h>   #include <sys/socket.h>
功能:创建套接字
参数:
   domain:协议族
     AF_UNIX, AF_LOCAL  本地通信
     AF_INET            ipv4
     AF_INET6           ipv6
   type:套接字类型
     SOCK_STREAM:流式套接字
     SOCK_DGRAM:数据报套接字
   protocol:协议 - 填0 自动匹配底层 ,根据type
      系统默认自动帮助匹配对应协议
       传输层:IPPROTO_TCP、IPPROTO_UDP、IPPROTO_ICMP
       网络层:htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL)
返回值:
    成功 文件描述符 0 -> 标准输入  1->标准输出  2->标准出错  3->socket
    失败 -1,更新errno

image.gif

7.8.2 bind 绑定套接字
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
头文件: #include<sys/types.h>  #include<sys/socket.h> 
         #include<netinet/in.h>  #include<netinet/ip.h>
功能:绑定  协议? ip? 端口 ? 让别人识别
参数:
    sockfd:套接字
    addr:用于通信结构体 (提供的是通用结构体,需要根据选择通信方式,填充对应结构体-通信当时socket第一个参数确定,需要强转)
         (结构体之间的强转,不可以直接使用普通数据类型强转,因为结构体字节对齐原则,具体大小不一致,会数据丢失,所以可使用结构体指针,对结构体地址进行强转)   
    addrlen:结构体大小   
返回值:成功 0   失败-1,更新errno
  
 通用结构体:
  struct sockaddr {
     sa_family_t  sa_family;
     char        sa_data[14];
 }
ipv4通信结构体:
struct sockaddr_in {
    sa_family_t    sin_family;  ----协议族
    in_port_t      sin_port;    ----端口
    struct in_addr sin_addr;    ----ip结构体
};
struct in_addr {
    uint32_t       s_addr;     --ip地址
};
本地通信结构体:
 struct sockaddr_un {
     sa_family_t sun_family;      /* AF_UNIX 本地通信 */
     char        sun_path[108];   /* 在本地创建的套接字路径 */
 };

image.gif

7.8.3 listen 监听
int listen(int sockfd, int backlog);
功能:监听,将主动套接字变为被动套接字
参数:
 sockfd:套接字
 backlog:同一时间可以响应客户端请求链接的最大个数,不能写0.
  不同平台可同时链接的数不同,一般写6-8个
返回值:成功 0   失败-1,更新errno

image.gif

7.8.4 accpt 阻塞等待连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:阻塞函数,阻塞等待客户端的连接请求,如果有客户端连接,
             则accept()函数返回,返回一个用于通信的套接字文件;
参数:
   Sockfd :套接字
   addr: 链接客户端的ip和端口号
      如果不需要关心具体是哪一个客户端,那么可以填NULL;
   addrlen:结构体的大小
     如果不需要关心具体是哪一个客户端,那么可以填NULL;
返回值: 
     成功:文件描述符; //用于通信
     失败:-1,更新errno新errno

image.gif

7.8.5 recv 接受缓存区的内容
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
功能: 接收数据 
参数: 
    sockfd: acceptfd ;
    buf  存放位置
    len  大小
    flags  一般填0,相当于read()函数
    MSG_DONTWAIT  非阻塞
返回值: 
   < 0  失败出错  更新errno
   ==0  表示客户端退出
   >0   成功接收的字节个数

image.gif

7.9 UDP 编程
7.9.1 函数接口(recvfrom  sendto)
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
          struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收数据
参数:
  sockfd:套接字描述符
  buf:接收缓存区的首地址
  len:接收缓存区的大小
  flags:0  接收数据 并 阻塞
                 MSG_DONTWAIT: 设置非阻塞
  src_addr: 发送端的网络信息结构体的指针(对方的 caddr)
  addrlen:发送端的网络信息结构体的大小的指针(对方的 caddr)
返回值:
  成功接收的字节个数
       接收到的数据为0 : 0
  失败:-1
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                  const struct sockaddr *dest_addr, socklen_t addrlen);
功能:发送数据
参数:
  sockfd:套接字描述符
  buf:发送缓存区的首地址
  len:发送缓存区的大小
  flags:0  发送消息并阻塞
  src_addr:接收端的网络信息结构体的指针
  addrlen:接收端的网络信息结构体的大小
返回值: 
  成功发送的字节个数
  失败:-1

image.gif

服务器模型

9.1 循环服务器

【1】 循环服务器  (同一时刻,只能连接一个客户端,进行通信)

特点:  循环服务器,同一时刻,只能处理一个客户端请求;

缺点:  循环处理客户端, 不能做耗时动作(一直循环一直工作);



TCP服务器流程:

     socket()

      bind();

      listen()

    while(1)

        {

                accept()

                while(1)

                 {

                       recv();

               }

               close(accpetfd);

       }      


UDP服务器流程:

    socket();

      bind();

    while(1)

      {    

              recvfrom();

      }

   close();

9.2 并发服务器

1.多进程实现并发(最好不用)


socket()

bind();

listen();

while(1)

{

accept();

if(fork() == 0)  //子进程

{

 while(1)

 {

  process();

 }

 close(client_fd);

 exit();

}

else

{

}

}



2. 线程实现

每来一个客户端连接, 开一个子线程来专门处理客户端的数据, 实现简单, 资源占用少;


socket()

bind();

listen();

while(1)

{

accept();

pthread_create();

}


函数接口:


创建线程

#include<pthread.h>

原型:  

int pthread_create

(pthread_t *thread , const pthread_attr_t*arr , void*(* routine)(void *) , void *arg)

功能: 创建线程

参数: pthread_t *thread: 指针,要指向一个地址。创建的线程对象,地址

     pthread_attr_t: 线程的属性,为NULL表示默认属性

     void*(* routine)(void *):  线程函数(函数指针),传入函数名

     void*arg:     给线程函数传参,以地址的形式,不需要传参可以设NULL

返回值: 成功 :  0       失败 : err no



退出线程

格式:

Int pthread_exit(void *retval)

功能:   用于退出线程的执行

参数:  retval:  线程退出时返回的值

返回值:   成功:   0

          失败:  errno

使用:   pthread_exit(NULL);



将线程设置为游离态,自动回收线程资源,不阻塞

pthread_detach

int pthread_detach(pthread_t thread);

功能:将线程设置为游离态,等线程退出系统自动回收线程资源

参数:

thread:线程tid

返回值:成功0,失败非0

使用: pthread_detach(tid);

线程实现并发的代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
void *mythread(void *arg)//int acceptfd
{
    //先把void * 强转为 int *, 然后取该指针的值
     int acceptfd =  *((int *)arg); //acceptfd
     char buf[128] = "";
     int recvbyte;
     while(1)
     {
         recvbyte = recv(acceptfd,buf,sizeof(buf),0);
         if(recvbyte < 0)
         {
             perror("recv is err:");
             return NULL;
         }
         else if(recvbyte == 0)
         {
             printf("client is exit\n");
             break;
         }
         else
         {
            printf("%s\n",buf);
         }
     }
     close(acceptfd);
     //线程退出函数
     pthread_exit(NULL)
;}
int main(int argc, char const *argv[])
{
    if(argc != 2)
    {
        printf("please input %s <port>",argv[0]);
        return -1;
    }
    //1.创建流式套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        perror("sockfd is err:");
        return -1;
    }
    //2.填充ipv4结构体
    struct sockaddr_in saddr,caddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(atoi(argv[1]));
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
    int len = sizeof(caddr);
    if(bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0)
    {
        perror("bind is err:");  
        return -1;
    }
    //3.监听
    if(listen(sockfd,5)<0)
    {
        perror("listen is err:");
        return -1;
    }
    while(1)
    {
        int acceptfd = accept(sockfd,(struct sockaddr *)&caddr,&len);
        if(acceptfd < 0)
        {
            perror("accept is err:");
            return -1;
        }
        printf("client: ip %s  port %d\n",\
                            inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port));
        pthread_t tid;
        pthread_create(&tid,NULL,mythread,&acceptfd);
        //将子线程设置为游离态, 不阻塞等待该线程的退出;
        //一旦该线程退出, 则会自动回收自己的资源
        pthread_detach(tid);
    }
    close(sockfd);
    return 0;
}

image.gif

并发服务器总结:

多进程:  

    优点:  服务器更稳定 , 父子进程资源独立,  安全性高一点

    缺点:  消耗资源


多线程:

   优点:   资源开销小,  线程共享同一个进程的资源

   缺点:   安全性较差


IO多路复用:

  优点:  节省资源, 减小系统开销,性能高;

  缺点:  代码复杂性高

组播 -UDP

12.1 概念:同样也是基于setsockopt实现的文件属性设置
12.2 组播的地址

二级划分   D类IP:   224.0.0.1 - 239.255.255.255


//多播结构体

struct ip_mreq{

struct  in_addr  imr_multiaddr;   //指定多播组IP

struct  in_addr  imr_interface;   //本地IP,通常指定为 INADDR_ANY--0.0.0.0

}


 struct in_addr{

 _be32  s_addr;  //IP地址(大端)

}


//核心代码 ------------------------------------

struct ip_mreq mreq;

bzero(&mreq, sizeof(mreq));

mreq.imr_multiaddr.s_addr = inet_addr("224.10.10.1"); //填充多播组IP

mreq.imr_interface.s_addr = inet_addr("0.0.0.0");  //自动获取本机IP

//改变套接字属性

setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));

12.3 组播的客户端

1.创建套接字

2.加入多播组

3.接收者绑定组播IP地址

4.等待接收数据


简易代码实现


socket()


struct ip_mreq mreq;   //组播的结构体变量

 mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);   //组IP

 mreq.imr_interface.s_addr = inet_addr("0.0.0.0"); //本地IP


setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));

//3. 填充结构体: 绑定组播ip和端口

struct sockaddr_in saddr,caddr;

saddr.sin_family = AF_INET;

saddr.sin_port = htons(atoi(argv[2]));

saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //组IP

socklen_t len = sizeof(caddr);

12.4 组播的服务器

1.创建数据报套接字

2.指定接收方地址为 组播地址 如 224.0.0.10  以及端口信息

3.发送数据包

  1. 广播和组播的区别

广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响通信。

组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。

组播方式既可以发给多个主机,又能避免像广播那样带来过多的负载

本地套接通信字

14.1 特性
·  unix网络编程最开始有的编程都是一台主机内进程和进程之间的编程。(本地通信)
·  socket可以用于本地间进程通信,创建套接字时使用本地协议AF_LOCAL或AF_UNIX
      本地通信不需要ip和端口,无法进行两个主机通信
·  分为流式套接字和数据报套接字    //可以使用流式套接字或者数据包套接字
·  和其他进程间通信相比使用方便、效率更高,常用于前后台进程通信。
·   unix域套接字编程,实现本间进程的通信,依赖的是s类型的文件;
核心流程
#include <sys/socket.h>
#include <sys/un.h>
struct sockaddr_un {
sa_family_t sun_family;               /* 本地协议 AF_UNIX */
char        sun_path[UNIX_PATH_MAX];  /* 本地路径 s类型的套接字文件 */
};
unix socket = socket(AF_UNIX, type, 0); //type可以为流式套接字或数据包套接字
                                        //unix 写为 int 就可以
struct sockaddr_un myaddr;
myaddr.sun_family = AF_UNIX; //填充UNIX域套接字
strcpy(saddr.sun_path,"./myunix"); // 创建套接字的路径
recv代码
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    //1.创建套接字
    int sockfd = socket(AF_UNIX,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        perror("sock is err:");
        return -1;
    }
    system("rm ./myunix -f");// 两者都是删除地址的,二选一即可
    unlink("./myunix");
    //2. 填充结构体
    struct sockaddr_un saddr;
    saddr.sun_family = AF_UNIX;
    strcpy(saddr.sun_path,"./myunix");
    if(bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0)
    {
        perror("bind is err:");    return -1;
    }
    if(listen(sockfd,5) < 0)
    {
        perror("listen is err:");  return -1;
    }
    int acceptfd = accept(sockfd,NULL,NULL);
    if(acceptfd < 0)
    {
        perror("acceptfd < 0");    return -1;
    }
    char buf[128] = "";
    int recvbyte;
    while(1)
    {
          recvbyte = recv(acceptfd,buf,sizeof(buf),0);
        if(recvbyte < 0)
        {
            perror("recv is err:");   return -1;
        }
        else if(recvbyte == 0)
        {
            printf("exit\n");
            break;
        }
        else
        {
            printf("buf: %s\n",buf);
        }
    }
    close(sockfd);
    close(acceptfd);
    return 0;
}
send代码:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdlib.h>
#include <stdio.h>
#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char const *argv[])
{
    //1.创建套接字
    int sockfd = socket(AF_UNIX,SOCK_STREAM,0);
    if(sockfd < 0)
    {
        perror("sock is err:");
        return -1;
    }
    //2. 填充结构体
    struct sockaddr_un saddr;
    saddr.sun_family = AF_UNIX;
    strcpy(saddr.sun_path,"./myunix");
    if(connect(sockfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0)
    {
        perror("connect is err:");
        return -1;
    }
    char buf[128] = "";
    int recvbyte;
    while(1)
    {
       fgets(buf,sizeof(buf),stdin);
         if(buf[strlen(buf) -1] == '\n')
            buf[strlen(buf) -1] = '\0';
      send(sockfd,buf,sizeof(buf),0);
    }
    close(sockfd);
    return 0;
}

image.gif

总结就到这里啦,希望可以对大家有帮助~
相关文章
|
2月前
|
存储 UED
从零开始构建个人网站:初学者指南
【5月更文挑战第9天】本文是初学者构建个人网站的指南,包括明确目标、选择域名和主机、挑选网站建设工具(如WordPress、Wix、Squarespace)、设计网站结构和布局、创建内容、优化测试以及推广维护。按照这些步骤,新手也能轻松建立自己的个人网站。记得在过程中不断学习和改进,祝你建站成功!
|
9月前
|
存储 安全 编译器
【C++】C++入门必备知识详细讲解
【C++】C++入门必备知识详细讲解
201 0
|
8月前
|
存储 运维 算法
嵌入式进阶从小白到大神学习全攻略(学习路线+课程+学习书籍+练习项目)
嵌入式进阶从小白到大神学习全攻略(学习路线+课程+学习书籍+练习项目)
|
9月前
|
安全 编译器 C语言
C++入门必备知识
C++入门必备知识
46 0
|
9月前
|
设计模式 前端开发 JavaScript
前端Web开发学习,入门到进阶,推荐几本很不错的书籍
前端Web开发学习,入门到进阶,推荐几本很不错的书籍
112 0
|
11月前
|
XML IDE 编译器
【C++】C++ 基础进阶【二】开发技巧
C++基础进阶,关于开发环境开发工具的一些便捷使用方式,提高生产力
129 0
【C++】C++ 基础进阶【二】开发技巧
|
小程序 开发者
微信小游戏开发基础系列教程开篇
做个游戏并不简单,或者其实比想象中的还要难,我想对于很多学习游戏开发的新手来讲都会这样觉得。即使是再简单的工具,甚至连代码都不需要写,但是用起来还是一个样,跟着教程一步一步的知道怎么做,脱离了教程就两眼一抹黑,无从下手。
87 0
|
存储 安全 编译器
【C++入门必备知识】
库里的名称我们无法改变,那我们可以将自己定义的名称放在命名空间里,当需要时指令访问即可。 那怎么定义命名空间呢?
71 0
|
域名解析 缓存 JavaScript
手把手教你从零开始搭建个人博客,20分钟上手
手把手教你从零开始搭建个人博客,20分钟上手
349 0
手把手教你从零开始搭建个人博客,20分钟上手
|
运维 小程序
全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序
全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序
119 1
全栈工程师之路-中级篇之小程序开发-第一章第一节注册小程序