day34——TCP和UDP的基础通信

news2024/11/25 4:17:46

一、网络通信之套接字

1.1 套接字通信原理

1.2 socket函数介绍

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int socket(int domain, int type, int protocol);
       功能:为通信创建一个端点,并返回该端点的文件描述符
       参数1:通信域
       Name                Purpose                          Man page
       AF_UNIX, AF_LOCAL   本地通信,同一主机之间进程通信     详情请看man 7 unix
       AF_INET             IPv4 提供的网络通信               详情请看man 7 ip
       AF_INET6            IPv6 提供的网络通信               详情请看man 7 ipv6
       参数2:指定通信语义,可以由多个宏值使用位或连接
       SOCK_STREAM:表示提供TCP协议的传输方式
       SOCK_DGRAM:表示提供UDP协议的传输方式
       SOCK_NONBLOCK:套接字设置非阻塞属性
       参数3:如果参数2中仅仅指定一个协议,那么参数3可以填0,如果指定多个,则参数3需要指定特定的协议
           TCP协议名称:IPPROTO_TCP
           UDP协议名称:IPPROTO_UDP
       返回值:成功返回创建的套接字文件描述符,失败返回     -1并置位错误码                 

二、TCP实现网络通信

2.1 TCP网络通信原理图

2.2 TCP相关函数介绍

1> bind绑定

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int bind(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
       功能:位套接字分配名称
       参数1:通过socket函数创建出来的套接字文件描述符
       参数2:通用地址信息结构体,需要根据具体使用的地址族而定, struct sockaddr仅仅只是为了类型的强制转换,防止出现警告
           跨主机间通信:man 7 ip
            struct sockaddr_in {
               sa_family_t    sin_family; /* 表示通信域 */
               in_port_t      sin_port;   /* 端口号的网络字节序 */
               struct in_addr sin_addr;   /* ip地址 */
           };

           /* Internet address. */
           struct in_addr {
               uint32_t       s_addr;     /* IP地址的网络字节序 */
           };
           同一主机间通信:man 7 uninx
          struct sockaddr_un {
               sa_family_t sun_family;               /* 表示通信域:AF_UNIX */
               char        sun_path[108];            /* 套接字文件的地址 */
           };

        参数3:参数2的大小
        返回值:成功返回0,失败返回-1并置位错误码
 注意关于bind的两个错误:
 1、 Cannot assign requested address:表示IP地址填写错误,检查IP是否有问题
 2、Address already in use:表示地址信息正在占用,可以调用函数快速重用,也可以等一会

2> listen监听

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int listen(int sockfd, int backlog);
       功能:将套接字设置成被动监听状态,已接受客户端的连接请求
       参数1:套接字文件描述符
       参数2:容纳连接的队列的最大长度,一般填128
       返回值:成功返回0,失败返回-1并置为错误码
       

3> accept接收连接请求

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
       功能:用于阻塞接收客户端连接请求
       参数1:服务器套接字文件描述符
       参数2:用于接收对端地址信息结构体的指针
       参数3:接收对端地址信息的长度
       返回值:成功返回一个新的用于通信的套接字文件描述符,失败返回-1并置位错误码

4> recv接收数据

       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       功能:从套接字中读取数据到buf中
       参数1:用于通信的套接字文件描述符
       参数2:接收数据后的容器地址
       参数3:接收的数据的大小
       参数4:是否阻塞接收
              0:表示阻塞接收消息
              MSG_DONTWAIT:表示非阻塞接收数据
        返回值:
            >0:表示成功读取的字符个数
            =0:表示通信对端已经下线
            =-1:表示出错,置位错误码                    

5> send发送数据

       #include <sys/types.h>
       #include <sys/socket.h>

       ssize_t send(int sockfd, const void *buf, size_t len, int flags);
       功能:向通信套接字文件描述符中写入数据
       参数1:通信的套接字文件描述符
       参数2:要发送数据的起始地址
       参数3:要发送数据的大小
       参数4:是否阻塞接收
              0:表示阻塞接收消息
              MSG_DONTWAIT:表示非阻塞接收数据
       返回值:成功返回发送字符的个数,失败返回-1并置位错误码       

6> connect连接函数

       #include <sys/types.h>          /* See NOTES */
       #include <sys/socket.h>

       int connect(int sockfd, const struct sockaddr *addr,
 socklen_t addrlen);
       功能:将套接字文件描述符连接到addr指向的地址空间中
       参数1:客户端套接字文件描述符
       参数2:对端地址信息结构体
       参数3:参数2的大小
       返回值:成功返回0,失败返回-1并置位错误码

2.3 TCP服务器端代码实现

#include<myhead.h>
#define SER_PORT 6666          //服务器端口号
#define SER_IP "192.168.2.191"    //服务器ip地址


int main(int argc, const char *argv[])
{
    //1、创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    //参数1:表示ipv4的网络通信
    //参数2:表示使用的是TCP通信方式
    //参数3:表示默认使用一个协议
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("socket success, sfd = %d\n", sfd);        //3

    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    //3、将套接字设置成被动监听状态
    if(listen(sfd, 128)==-1)
    {
        perror("listen error");
        return -1;
    }
    printf("listen success\n");

    //4、阻塞等待客户端的连接请求
    //4.1 定义变量用于接收客户端的信息
    struct sockaddr_in cin;          //用于接收地址信息
    socklen_t addrlen = sizeof(cin);  //用于接收长度
    
    //4.2 接收连接
    int newfd = accept(sfd, (struct sockaddr*)&cin, &addrlen) ;
    if(newfd == -1)
    {
        perror("accept error");
        return -1;
    }
    printf("[%s:%d]: 已成功连接一个客户端\n", \
            inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));

    //5、数据收发
    char buf[128] = "";

    while(1)
    {
        //清空容器
        bzero(buf, sizeof(buf));

        //从客户端套接字中读取数据
        int res = recv(newfd, buf, sizeof(buf), 0);
        if(res == -1)
        {
            perror("read error");
            return -1;
        }else if(res == 0)
        {
            printf("客户端已经下线\n");
            close(newfd);             //关闭客户端套接字
            break;
        }

        //正常收到客户端发来的消息
        printf("[%s:%d] : %s\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf);

        //对收到的消息进行一顿操作
        //此处省略一万行代码
        strcat(buf, "*_*");

        //将消息回复给客户端
        if(send(newfd, buf, strlen(buf), 0) == -1)
        {
            perror("发送error");
            return -1;
        }
        printf("发送成功\n");
    }

    //6、关闭监听
    close(sfd);

    return 0;
}

2.4 TCP服务器通信模型

1、sfd = socket();                //创建一个用于连接的套接字文件描述符
2、bind();                        //为服务器套接字绑定ip地址和端口号,为了让客户端额能够找到服务器
3、listen();                       //将服务器套接字设置成被动监听状态,用于接收客户端的连接请求
4、newfd = accept();              //阻塞等待客户端的连接请求,如果有客户端发来连接请求,创建一个新的用于通信的套接字文件描述符
5、while(1)
{
    send\recv\read\write;           //数据收发工作
}
6、close();                              //关闭套接字、关闭监听

2.5 TCP客户端实现

#include<myhead.h>

#define SER_PORT 6666             //与服务器保持一致
#define SER_IP  "192.168.2.191"    //服务器ip地址
#define CLI_PORT 8888               //客户端端口号
#define CLI_IP  "192.168.2.191"     //客户端ip地址


int main(int argc, const char *argv[])
{
    //1、创建用于通信的套接字文件描述符
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("cfd = %d\n", cfd);             //3
    
    //2、绑定IP地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

    //2.2 绑定工作
    if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");

    
    //3、连接到服务器
    //3.1 填充服务器地址信息结构体
    struct sockaddr_in sin;
    sin.sin_family = AF_INET;          //通信域
    sin.sin_port = htons(SER_PORT);      //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

    //3.2 连接服务器
    if(connect(cfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("connect error");
        return -1;
    }
    printf("连接服务器成功\n");
    
    //4、数据收发
    char buf[128] = "";
    while(1)
    {
        printf("请输入>>>");
        fgets(buf, sizeof(buf), stdin);         //从终端获取一个字符串
        buf[strlen(buf)-1] = 0;

        //将数据发送给服务器
        send(cfd, buf, strlen(buf), 0);
        printf("发送成功\n");

        //接受服务器发来的数据
        //清空容器
        bzero(buf, sizeof(buf));
        recv(cfd, buf, sizeof(buf), 0);
        printf("收到服务器消息为:%s\n", buf);
    }
    
    //5、关闭套接字
    close(cfd);

    return 0;
}

三、UDP实现网络通信

3.1 UDP网络通信模型

3.2 UDP相关函数

       ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
       //功能:从套接字文件描述符中读取数据,并将对端地址信息结构体接收
       参数1:套接字文件描述符
       参数2:要接收数据的起始地址
       参数3:要接收的数据大小
       参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
       参数5:接收对端地址信息结构体
       参数6:参数5的大小
       返回值:成功返回读取的字节的大小,失败返回-1并置位错误码
       
        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
      //功能:向套接字文件描述符中读取数据,写给指定的对端接收
       参数1:套接字文件描述符
       参数2:要发送数据的起始地址
       参数3:要发送的数据大小
       参数4:是否阻塞,0表示阻塞,MSG_NOWAIT表示非阻塞
       参数5:接收对端地址信息结构体
       参数6:参数5的大小
       返回值:成功返回发送的字节的大小,失败返回-1并置位错误码
                                             

3.2 UDP服务器端实现

#include<myhead.h>

#define SER_PORT 9999          //服务器端口号
#define SER_IP "192.168.2.191"   //服务器ip地址

int main(int argc, const char *argv[])
{
    //1、创建用于通信的服务器套接字文件描述符
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(sfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", sfd);      //3


    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in sin;       
    sin.sin_family = AF_INET;       //通信域
    sin.sin_port = htons(SER_PORT);    //端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);    //ip地址

    //2.2 绑定工作
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、数据收发
    char buf[128] = "";
    struct sockaddr_in cin;            //接受对端地址信息
    socklen_t addrlen = sizeof(cin);   //接受地址长度

    while(1)
    {
        //清空容器
        bzero(buf, sizeof(buf));

        //从套接字中读取数据
        recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
        printf("收到消息为:%s\n", buf);

        //将收到的消息进行处理
        //此处省略一万行
        strcat(buf, "*_*");

        if(sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cin, sizeof(cin)) == -1)
        {
            perror("发送error");
            return -1;
        }
        printf("发送成功\n");
    }
    

    //4、关闭文件描述符
    close(sfd);

    return 0;
}

3.3 UDP客户端实现

#include<myhead.h>

#define SER_PORT 9999          //服务器端口号
#define SER_IP "192.168.2.191"   //服务器ip地址
#define CLI_PORT 5555             //客户端端口号
#define CLI_IP  "192.168.2.191"    //客户端ip地址

int main(int argc, const char *argv[])
{
    //1、创建用于通信的服务器套接字文件描述符
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        return -1;
    }
    printf("sfd = %d\n", cfd);      //3


    //将端口号快速重用
    int reuse = 1;
    if(setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))==-1)
    {
        perror("setsockopt error");
        return -1;
    }
    printf("端口号快速重用成功\n");

    //2、为套接字绑定ip地址和端口号
    //2.1 填充地址信息结构体
    struct sockaddr_in cin;       
    cin.sin_family = AF_INET;       //通信域
    cin.sin_port = htons(CLI_PORT);    //端口号
    cin.sin_addr.s_addr = inet_addr(CLI_IP);    //ip地址

    //2.2 绑定工作
    if(bind(cfd, (struct sockaddr*)&cin, sizeof(cin)) == -1)
    {
        perror("bind error");
        return -1;
    }
    printf("bind success\n");
    
    //3、数据收发
    char buf[128] = "";
    //填充服务器的地址信息结构体
    struct sockaddr_in sin;            //接受对端地址信息
    sin.sin_family = AF_INET;      //服务器的通信域
    sin.sin_port = htons(SER_PORT);    //服务器端口号
    sin.sin_addr.s_addr = inet_addr(SER_IP);     //服务器ip地址

    while(1)
    {
        //从终端获取数据
        printf("请输入>>>>");
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf)-1] = 0;

        //将数据发送给服务器
        sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&sin, sizeof(sin));

        printf("发送成功\n");

        //清空容器
        bzero(buf, sizeof(buf));
        //接受服务器发来的消息
        recvfrom(cfd, buf, sizeof(buf), 0, NULL, NULL);
        printf("收到服务器消息为:%s\n", buf);

    }
    

    //4、关闭文件描述符
    close(cfd);

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2033251.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Llama 3.1中文微调数据集已上线,超大模型一键部署

7 月的 AI 圈真是卷完小模型卷大模型&#xff0c;精彩不停&#xff01;大多数同学都能体验 GPT-4o、Mistral-Nemo 这样的小模型&#xff0c;但 Llama-3.1-405B 和 Mistral-Large-2 这样的超大模型让很多小伙伴犯了难。 别担心&#xff01;hyper.ai 官网在教程板块为大家提供了…

从AGV到立库,物流自动化的更迭与未来

AGV叉车 随着柔性制造系统的广泛应用&#xff0c;小批量、多批次的生产需求不断增强&#xff0c;“订单导向”生产已经成为趋势。这也让越来越多的企业认识到&#xff0c;产线的智能设备导入只是第一步&#xff0c;要想达到生产效率的最优解&#xff0c;物流系统的再优化必须提…

【redis的大key问题】

在使用 Redis 的过程中&#xff0c;如果未能及时发现并处理 Big keys&#xff08;下文称为“大Key”&#xff09;&#xff0c;可能会导致服务性能下降、用户体验变差&#xff0c;甚至引发大面积故障。 本文将介绍大Key产生的原因、其可能引发的问题及如何快速找出大Key并将其优…

Z 字形遍历二叉树

假设一个二叉树上各结点的权值互不相同。 我们就可以通过其后序遍历和中序遍历来确定唯一二叉树。 请你输出该二叉树的 ZZ 字形遍历序列----也就是说&#xff0c;从根结点开始&#xff0c;逐层遍历&#xff0c;第一层从右到左遍历&#xff0c;第二层从左到右遍历&#xff0c;…

Linux文本处理shell脚本

文本处理 在进行文本处理时&#xff0c;我们有一些常见的需求&#xff1a; 获取文本的行数、字数比较两段文本的不同之处查看文本的开头几行和最后几行在文本中查找字符串在文本中替换字符串 下面介绍如何在 shell 中做到这些事情。 文本统计&#xff1a;wc wc 是文本统计…

了解经典的 MPLS L3VPN 网络架构

1.多协议标签交换技术MPLS的概念 MPLS&#xff08;Multi-Protocol Label Switching&#xff0c;多协议标签交换技术&#xff09;,传统网络中就拥有了 3 种经典转发实现&#xff0c;它们分别是&#xff1a; L2 交换转发L2.5 标签转发L3 路由转发 MPLS 协议则作用于 L2.5 层&…

大数据-80 Spark 简要概述 系统架构 部署模式 与Hadoop MapReduce对比

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

布隆过滤器将应用Redis缓存使用量降低100倍

文章目录 背景布隆过滤器介绍定义工作原理数据结构&#xff1a; Redis布隆过滤器实战总结 背景 由于在业务中用到了Redis用于存储一些关系信息&#xff0c;且对应的请求量比较大&#xff0c;为了防止缓存击穿导致数据库压力过大&#xff0c;一般我们都会采用将不存在的内容存储…

头狼择校小程序

综述介绍 头狼择校&#xff0c;是头狼择™高校的简称&#xff0c;我们专注高校、大学的择校。倡导先嗅就业再择校&#xff0c;是预约工具和对话平台。帮您嗅招办、嗅教授、嗅学姐&#xff0c;预约择校有关的老师、顾问&#xff0c;助力考大学和考研的“双考”学生及家长了解就…

C# OnnxRuntime部署LivePortrait实现快速、高质量的人像驱动视频生成

目录 效果 说明 项目 模型信息 代码 下载 效果 LivePortrait实现快速、高质量的人像驱动视频生成 说明 官网地址&#xff1a;https://github.com/KwaiVGI/LivePortrait 代码实现参考&#xff1a;https://github.com/hpc203/liveportrait-onnxrun 模型下载&#xff1a;…

【健康革命】让AI成为你的私人健身教练!

本文由 ChatMoney团队出品 现在市面上有很多男生和女生为了保持身材都进行疯狂的减肥&#xff0c;有些是靠吃减肥药来保持身材&#xff0c;有些是晚上不吃饭要控制&#xff0c;还有些疯狂且漫无目的健身&#xff1b;但往往以上几种都很伤身体的&#xff0c;毕竟身体健康是革命的…

历史库,成本与性能如何兼得?| OceanBase应用实践

随着数据量的迅猛增长&#xff0c;企业和组织在数据库管理方面遭遇的挑战愈发凸显。数据库性能逐渐下滑、存储成本节节攀升&#xff0c;以及数据运维复杂性的增加&#xff0c;这些挑战使得DBA和开发者在数据管理上面临更大的压力。 为了应对这些挑战&#xff0c;对数据生命周期…

简单的docker学习 第13章 CI/CD与Jenkins(下)

第13章 CI/CD 与 Jenkins 13.13 自由风格的 CI 操作(最终架构) 前面的架构存在的问题是&#xff0c;若有多个目标服务器都需要使用该镜像&#xff0c;那么每个目标服务器都需要在本地构建镜像&#xff0c;形成系统资源浪费。若能够在 Jenkins 中将镜像相撞构建好并推送到 Har…

Java属性重写问题

目录 属性重写 案例演示 多态练习 练习1 练习2 属性重写 ​​​​​​​ 属性没有重写之说&#xff0c;属性的值看编译类型。 instanceOf 比较操作符&#xff0c;用于判断对象的运行类型是否为某类型或者某类型的子类。 案例演示 父类base&#xff0c;有一个count属…

Collection和List集合

1.Collection集合 1.1数组和集合的区别【理解】 相同点 都是容器,可以存储多个数据 不同点 数组的长度是不可变的,集合的长度是可变的 数组可以存基本数据类型和引用数据类型 集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类 1.2集合类体系结构【理解】 …

旧手机拍摄的视频模糊可以修复清晰吗?

你是否时常“考古”一些老电影、老动漫来回忆旧日时光&#xff1f;你是否也有一些珍贵的录像&#xff0c;带你重温过去的美好&#xff1f;然而&#xff0c;我们已经习惯了高清体验&#xff0c;回头再看曾经的旧影像&#xff0c;画质或许“渣”的让人不忍直视。 旧手机像素不好&…

【论文笔记】Dual-Balancing for Multi-Task Learning

Abstract 多任务学习(Multi-task learning, MTL)中&#xff0c;任务平衡问题仍然是重要的挑战&#xff0c;损失、梯度尺度的不同&#xff0c;会导致性能的折中。 本文提出Dual-Balancing for Multi-Task Learning (DB-MTL)&#xff0c;从损失和梯度两个角度缓解任务均衡问题。…

成功解决:IDEA导入java项目 或 建包的时候com.不分开 【详细原理解释说明】

我 | 在这里 ⭐ 全栈开发攻城狮、全网10W粉丝、2022博客之星后端领域Top1、专家博主。 &#x1f393;擅长 指导毕设 | 论文指导 | 系统开发 | 毕业答辩 | 系统讲解等。已指导60位同学顺利毕业 ✈️个人公众号&#xff1a;热爱技术的小郑。回复 Java全套视频教程 或 前端全套视频…

子串 前缀和 | Java | (hot100) 力扣560. 和为K的子数组

560. 和为K的子数组 暴力法&#xff08;连暴力法都没想出来……&#xff09; class Solution {public int subarraySum(int[] nums, int k) {int count0;int len nums.length;for(int i0; i<len; i) {int sum0;for(int ji; j<len; j) {sumnums[j];if(sum k) {count;}…

C/C++复习 day2(模板,继承,多态)

C/C复习 day2 文章目录 C/C复习 day2前言一、模板1.模板的原理2.非类型模板参数3.模板的特化a. 函数模板的特化b. 类模板的特化1.全特化2.偏特化 4.模板的分离编译 二、继承1.继承的概念2.继承与派生类对象赋值转化3.隐藏1.成员变量的隐藏2. 成员函数的隐藏 4.继承中的友元5.继…