linux系统编程 socket part2

报式套接字

    • 1.动态报式套接字
    • 2.报式套接字的广播
    • 3.报式套接字的多播
    • 4.UDP协议分析
      • 4.1.丢包原因
      • 4.2.停等式流量控制

接linux系统编程 socket part1

1.动态报式套接字

在之前的例子上,发送的结构体中的名字由定长改变长。可以用变长结构体
变长结构体是由gcc扩展的一种技术,它是指其最后一个成员的长度不固定(flexible array member,也叫柔性数组)。
使用范围:数据长度不固定,例如协议对接中有固定的头结构体,数据结构体不固定。

struct Var_Len_Struct
{
    int nsize;
    char buffer[0];
    // 或者不指定大小 char buffer[];
};

结构体中的最后一个元素:一个没有元素的数组(柔性数组)。我们可以通过动态开辟一个比结构体大的空间,然后让buffer去指向那些额外的空间,这样就可以实现可变长的结构体了。更为巧妙的是,我们甚至可以用nsize存储字符串buffer的长度。

修改代码如下

proto.h
定义NAMEMAX接收名字的最大长度,因为接收方不知道大小,需要按照最大长度接收
使用变长数组,最后一个元素为0或者为空

#ifndef __PROTO_H_
#define __PROTO_H_

#define RECVPORT 1986
#define NAMEMAX (512-8-8) // 512为udp包推荐的字节数,8为udp的报头大小,8为结构体中固定长度的大小,即math和chinese

struct msg_t
{
    uint32_t chinese;
    uint32_t math;
    //变长
    uint8_t name[0];
}__attribute__((packed));


# endif

snder.cpp
1.发送的结构体改为结构体指针
2.计算发送的结构体大小 sizeof(struct) + strlen(name)
3.动态申请内存malloc
4.sendto函数相应修改

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>

#include "proto.h"

int main(int argc, char **argv) {
    int sd;
    //结构体改为结构体指针
    msg_t* sendmssg;
    struct sockaddr_in raddr;
    
    if (argc < 3) {
        perror("Usage");
        exit(1);
    }

    //strlen()不包括字符串结尾的空字符 '\0'
    //sizeof(argv[2]) 返回的是指针的大小,而不是字符串的长度
    if (strlen(argv[2]) > NAMEMAX) {
        std::cout << "name id too long" << std::endl;
        exit(1);
    }

    //创建socket
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socker()");
        exit(1);
    }
    //本地绑定(可以省略)
    
    //填写发送消息
    //变长,先计算结构体的长度
    int size = sizeof (msg_t) + strlen(argv[2]);
    //申请内存
    sendmssg = (msg_t*)malloc(size);
    if (sendmssg == NULL) {
        perror("malloc()");
        exit(1);
    }

    memcpy(sendmssg->name, argv[2], strlen(argv[2])+1);
    sendmssg->chinese = ntohs(100);
    sendmssg->math = ntohs(100);
    
    //对端地址
    raddr.sin_family = AF_INET;
    raddr.sin_port = ntohs(RECVPORT);
    inet_pton(AF_INET, argv[1], &raddr.sin_addr);

    //发送
    if (sendto(sd, sendmssg, size, 0, (const sockaddr*)&raddr, sizeof(raddr)) == -1) {
        perror("sendto()");
        exit(1);
    }
    //关闭
    close(sd);
}

rcver.cpp
1.接收的结构体改结构体指针
2.按最大长度NAMEMAX计算接收结构体的长度
3.动态分配内存
4.recvfrom函数相应修改

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>

#include "proto.h"
#define IPSTRSIZE        64

int main() {
    // 套接字描述符
    int sd;
    
    // laddr -- local address -- 本机地址
    // raddr -- remote address -- 对端地址
    sockaddr_in laddr, raddr;
    socklen_t raddr_len;
    
    // 结构体指针,存储接收到的结构体
    msg_t* mssg;

    // 存储对端地址,点分式
    char ipstr[IPSTRSIZE];
    
    //创建socket,创建协议为ipv4的报式套接字,0为默认协议,即UDP
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socker()");
        exit(1);
    }

    //填写本机的地址信息
    laddr.sin_family = AF_INET;
    //ip地址和网络端口号是要通过网络发送过去的,所以需要考虑字节序的问题,也就是htons
    laddr.sin_port = htons(RECVPORT);
    // 因为本机的ip地址有可能会变化,为了避免ip地址每一次变化,都要进来修改,所以给它匹配一个万能地址0.0.0.0
    // 对"0.0.0.0"的定义是any address.就是说在当前绑定阶段,本机的ip地址是多少,这四个0就会自动换成当前的ip地址.
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr.s_addr);
    
    //绑定接收的ip地址和端口号
    if (bind(sd, (const sockaddr*)&laddr, sizeof(laddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    
    // 接收
    // !!!!这里一定要初始化对端地址的大小!!!
    raddr_len = sizeof(raddr);
    
    //不知道对端的地址大小,按最大的来接
    int size = sizeof(msg_t) + NAMEMAX - 1; 
    mssg = (msg_t*)malloc(size);
    
    while (1) {
        if (recvfrom(sd, mssg, size, 0, (sockaddr*)&raddr, &raddr_len) < 0) {
            perror("recvfrom()");
            exit(1);
        }
        inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
        std::cout << "---------recive message from " << std::string(ipstr) << ":" << ntohs(raddr.sin_port) << "---------" << std::endl;
        // 单字节传输不涉及到大端小端的存储情况
        std::cout << "name" << ":" << mssg->name << std::endl;
        std::cout << "math" << ":" << ntohs(mssg->math) << std::endl;
        std::cout << "chinese" << ":" << ntohs(mssg->chinese) << std::endl;
    }
    
    //关闭
    close(sd);
    exit(1);
}
vratdrh7771.rsv.ven.veritas.com [60]: ./snder 10.85.171.130 "hahhaahha"
vratdrh7771.rsv.ven.veritas.com [61]: ./snder 10.85.171.130 "44kkkkk"


vratdrh7771.rsv.ven.veritas.com [71]: ./rcver
---------recive message from 10.85.171.130:33261---------
name:hahhaahha
math:100
chinese:100
---------recive message from 10.85.171.130:36716---------
name:44kkkkkha
math:100
chinese:100

2.报式套接字的广播

在使用TCP/IP 协议的网络中,主机标识段host ID 为全1 的 IP 地址为广播地址。

广播数据有如下特点:

  • TCP/IP协议栈中,传输层只有UDP可以广播,TCP没有广播的概念
  • UDP广播不需要经过路由器转发,因为路由器不会转发广播数据

套接字机制提供了两个套接字选项接口来控制套接字行为。一个接口用来设置选项,另一个接口可以查询选项的状态。

GETSOCKOPT(2)                                          Linux Programmer's Manual                                         GETSOCKOPT(2)

NAME
       getsockopt, setsockopt - get and set options on sockets

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

       int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);

DESCRIPTION
       getsockopt()  and  setsockopt() manipulate options for the socket referred to by the file descriptor sockfd.  Options may exist
       at multiple protocol levels; they are always present at the uppermost socket level.
  • level: 标识了选项应用的协议。
    • 如果选项是通用的套接字层次选项,则 level 设置成SOL_SOCKET。否则,level设置成控制这个选项的协议编号
    • 对于TCP,level是IPPROTO_TCP
    • 对于IP,level是IPPROTO_IP
  • optname: 需设置的选项
  • optval:根据选项的不同指向一个数据结构或者一个整数。一些选项是on/off开关。如果整数非0,则启用选项。如果整数为0,则禁止选项。
  • optlen:指定了optval指向的对象的大小。

代码示例

snder.cpp:设置套接字,打开广播选项,并向广播地址255.255.255.255发送数据报

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>

#include "proto.h"

int main() {
    int sd;
    msg_t sendmssg;
    struct sockaddr_in raddr;
    
    //创建socket
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socker()");
        exit(1);
    }
    //本地绑定(可以省略)

    //设置套接字,打开广播
    //不同的层面封装了不同的属性,可以用man 7查看
    //man 7 socket 找 socket option
    //man 7 udp udp层可以改进的socket option
    //man 7 ip...
    //man 7 tcp
    int val = 1;
    if(setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) < 0) {
        perror("setsockopt()");
        exit(1);
    }
    
    //填写发送消息
    memset(&sendmssg, '\0', sizeof(sendmssg));
    //strcpy(sendmssg.name, "tracy");
    memcpy(sendmssg.name, "tracy", sizeof("tracy"));
    sendmssg.chinese = ntohs(100);
    sendmssg.math = ntohs(100);
    
    //对端地址
    raddr.sin_family = AF_INET;
    raddr.sin_port = ntohs(RECVPORT);
    inet_pton(AF_INET, "255.255.255.255", &raddr.sin_addr);

    //发送
    if (sendto(sd, &sendmssg, sizeof(sendmssg), 0, (const sockaddr*)&raddr, sizeof(raddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    //关闭
    close(sd);
}

rcver.cpp:设置套接字,打开广播选项,如果不打开,可能收到,可能收不到

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>

#include "proto.h"
#define IPSTRSIZE        64

int main() {
    // 套接字描述符
    int sd;
    
    // laddr 本机地址
    // raddr 对端地址
    sockaddr_in laddr, raddr;
    
    // 存储接收到的结构体
    msg_t mssg;

    // 存储对端地址,点分式
    char ipstr[IPSTRSIZE];
    
    // 创建socket,创建协议为ipv4的报式套接字,0为默认协议,即UDP
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socker()");
        exit(1);
    }

    int val = 1;
    if(setsockopt(sd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)) < 0) {
        perror("setsockopt()");
        exit(1);
    }

    // 填写本机的地址信息
    laddr.sin_family = AF_INET;
    // ip地址和网络端口号是要通过网络发送过去的,所以需要考虑字节序的问题,也就是htons
    laddr.sin_port = htons(RECVPORT);
    // 因为本机的ip地址有可能会变化,为了避免ip地址每一次变化,都要进来修改,所以给它匹配一个万能地址0.0.0.0
    // 对"0.0.0.0"的定义是any address.就是说在当前绑定阶段,本机的ip地址是多少,这四个0就会自动换成当前的ip地址.
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr.s_addr);
    
    //绑定接收(本机)的ip地址和端口号
    if (bind(sd, (const sockaddr*)&laddr, sizeof(laddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    
    // 接收
    // !!!!这里一定要初始化对端地址的大小!!!
    socklen_t addr_len = sizeof(raddr);
    while (1) {
        if (recvfrom(sd, (void*)&mssg, sizeof(mssg), 0, (sockaddr*)&raddr, &addr_len) < 0) {
            perror("recvfrom()");
            exit(1);
        }
        inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
        std::cout << "---------recive message from " << std::string(ipstr) << ":" << ntohs(raddr.sin_port) << "---------" << std::endl;
        // 单字节传输不涉及到大端小端的存储情况
        std::cout << "name" << ":" << mssg.name << std::endl;
        std::cout << "math" << ":" << ntohs(mssg.math) << std::endl;
        std::cout << "chinese" << ":" << ntohs(mssg.chinese) << std::endl;
    }
    
    //关闭
    close(sd);
    exit(1);
}

proto.h

#ifndef __PROTO_H_
#define __PROTO_H_
#define RECVPORT 1986
#define NAMESIZE 11

struct msg_t
{
    //定长,不可能有负值
    uint8_t name[NAMESIZE];
    uint32_t chinese;
    uint32_t math;
}__attribute__((packed));

# endif

注意:如果仍然接收不到,可以查看防火墙

vratdrh7771.rsv.ven.veritas.com [70]: ./rcver
---------recive message from 10.85.171.130:43497---------
name:tracy
math:100
chinese:100

3.报式套接字的多播

多播地址,也叫组播地址,组播报文的目的地址使用D类IP地址, D类地址不能出现在IP报文的源IP地址字段。组播地址可以分为四类:

  • 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
  • 224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
  • 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
  • 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。

代码示例

proto.h:设置一个约定的多播地址

#ifndef __PROTO_H_
#define __PROTO_H_

#define MGROUP   "224.2.2.2"
#define RECVPORT 1986
#define NAMESIZE 11

struct msg_t
{
    //定长,不可能有负值
    uint8_t name[NAMESIZE];
    uint32_t chinese;
    uint32_t math;
}__attribute__((packed));

# endif

snder.c:创建多播组

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>

#include "proto.h"

int main(int argc, char **argv) {
    int sd;
    msg_t sendmssg;
    struct sockaddr_in raddr;
    
    if (argc < 2) {
        perror("uasge()");
        exit(1);
    }

    //创建socket
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socker()");
        exit(1);
    }
    //设置套接字,创建多播组
    //man 7 ip
    ip_mreqn mreq;
    //multicast group address
    inet_pton(AF_INET, MGROUP, &mreq.imr_multiaddr);
    //local本机address
    inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
    //网络索引号
    mreq.imr_ifindex = if_nametoindex("ens192");
    //设置套接字,创建多播组
    if(setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt()");
        exit(1);
    }
    
    //填写发送消息
    memset(&sendmssg, '\0', sizeof(sendmssg));
    //strcpy(sendmssg.name, "tracy");
    memcpy(sendmssg.name, argv[1], sizeof(argv[1]) + 1);
    sendmssg.chinese = ntohs(100);
    sendmssg.math = ntohs(100);
    
    //对端地址
    raddr.sin_family = AF_INET;
    raddr.sin_port = ntohs(RECVPORT);
    inet_pton(AF_INET, MGROUP, &raddr.sin_addr);

    //发送
    if (sendto(sd, &sendmssg, sizeof(sendmssg), 0, (const sockaddr*)&raddr, sizeof(raddr)) < 0) {
        perror("sendto()");
        exit(1);
    }
    //关闭
    close(sd);
}

其中,可以使用命令查看网络设备的索引号:

ip ad sh

如下,1、2为索引号,lo, ens192为设备名

vratdrh7771.rsv.ven.veritas.com [60]: ip ad sh
1: lo: <LOOPBACK,UP,LOWER_UP> .....
........
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> ..... 
........

或者可以通过下列函数来获取网络设备名的索引编号:

#include <net/if.h>
unsigned int if_nametoindex(const char *ifname);

rcver.cpp:加入多播组

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>

#include "proto.h"
#define IPSTRSIZE        64

int main() {
    // 套接字描述符
    int sd;
    
    // laddr 本机地址
    // raddr 对端地址
    sockaddr_in laddr, raddr;
    
    // 存储接收到的结构体
    msg_t mssg;

    // 存储对端地址,点分式
    char ipstr[IPSTRSIZE];
    
    // 创建socket,创建协议为ipv4的报式套接字,0为默认协议,即UDP
    sd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sd < 0) {
        perror("socker()");
        exit(1);
    }

    //加入多播组
    ip_mreqn mreq;
    inet_pton(AF_INET, MGROUP, &mreq.imr_multiaddr);
    inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);
    mreq.imr_ifindex = if_nametoindex("ens192");

    if(setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt()");
        exit(1);
    }

    // 填写本机的地址信息
    laddr.sin_family = AF_INET;
    // ip地址和网络端口号是要通过网络发送过去的,所以需要考虑字节序的问题,也就是htons
    laddr.sin_port = htons(RECVPORT);
    // 因为本机的ip地址有可能会变化,为了避免ip地址每一次变化,都要进来修改,所以给它匹配一个万能地址0.0.0.0
    // 对"0.0.0.0"的定义是any address.就是说在当前绑定阶段,本机的ip地址是多少,这四个0就会自动换成当前的ip地址.
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr.s_addr);
    
    //绑定接收(本机)的ip地址和端口号
    if (bind(sd, (const sockaddr*)&laddr, sizeof(laddr)) < 0) {
        perror("sendto()");
        exit(1);
    }

    // 接收
    // !!!!这里一定要初始化对端地址的大小!!!
    socklen_t addr_len = sizeof(raddr);
    while (1) {
        if (recvfrom(sd, (void*)&mssg, sizeof(mssg), 0, (sockaddr*)&raddr, &addr_len) < 0) {
            perror("recvfrom()");
            exit(1);
        }
        inet_ntop(AF_INET, &raddr.sin_addr, ipstr, IPSTRSIZE);
        std::cout << "---------recive message from " << std::string(ipstr) << ":" << ntohs(raddr.sin_port) << "---------" << std::endl;
        // 单字节传输不涉及到大端小端的存储情况
        std::cout << "name" << ":" << mssg.name << std::endl;
        std::cout << "math" << ":" << ntohs(mssg.math) << std::endl;
        std::cout << "chinese" << ":" << ntohs(mssg.chinese) << std::endl;
    }
    
    //关闭
    close(sd);
    exit(1);
}

运行结果

vratdrh7771.rsv.ven.veritas.com [109]: ./rcver
---------recive message from 10.85.171.130:48514---------
name:mike
math:100
chinese:100
---------recive message from 10.85.171.130:39871---------
name:hahah
math:100
chinese:100

多播中有一个特殊的 ip 地址(224.0.0.1),它表示,所有支持多播的地址默认都存在这个组当中,并且无法离开。如果 snder 方向这个 ip 地址发送信息,就相当于向 255.255.255.255 上发消息。

4.UDP协议分析

4.1.丢包原因

UDP丢包并不是因为TTL,TTL是当前包的要跳转的路由的个数,linux环境下一般默认为64,Windows一般为128,一般情况下完全足够。丢包其实是由于阻塞造成的。路由有等待队列,并不是我的数据包从本路由到下一个路由是无条件发送的,而是有等待队列,这个等待队列会有丢包的算法实现。比如当前队列已经排列百分之N的容量时,就会随机的丢包等操作。
解决:闭环流控(停等式流控)

4.2.停等式流量控制

问题1 发送端发送后,接收端返回ACK,发送端会在一段时间后才能收到消息
在这里插入图片描述
问题2:发送端发送消息后丢包了,在一定时间(RTT)后没有收到接收端ACK,则重新发送消息
在这里插入图片描述
问题3:发送端发送消息后,接收端收到了数据,并且发送了ACK,但是ACK数据丢了,在一定时间后没有收到接收端ACK,则重新发送消息。接收端如何判断收到的包已经收到过了(data包加编号),则直接放弃该包,直接回复ACK。
在这里插入图片描述
问题4:如图 d2 丢包,ACK1 延迟回复给了发送端,则发送端认为d2发送成功,继续发d3,实际接受端是没有收到过d2的。所以ACK也要加编号,否则就可能出现这类问题。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

主流电商平台淘宝/1688/京东电商数据实时采集监测|电商API接口接入

电商大数据平台基于网络主流电商平台淘宝/1688/京东电商数据进行搭建&#xff0c;全面监测了包含淘宝、京东、苏宁、美团、大众点评等共计100余个主流电商交易平台&#xff0c;并凭借多年的电子商务数据分析挖掘经验积累形成的电商数据清洗体系和挖掘模型&#xff0c;能高效完成…

ARIMA

一.数据平稳性与差分法 1.平稳性&#xff1a; 2.差分法&#xff1a; 错开时间点&#xff0c;使得数据可以平稳 原数据➡️一阶差分➡️二阶差分&#xff1a; 二、arima 1.自回归模型 2.移动平均模型 关注的是误差项的累积 3.arma p d(几阶差分&#xff09; q自己指定 4.总…

分手我见得多了,怎么软件也玩分手?

网管小贾 / sysadm.cc 今年年初&#xff0c;我们就注意到了一件忒奇怪的事儿。 我们公司的同事小孙&#xff0c;以前人长得高高瘦瘦&#xff0c;做人做事也是谨小慎微、内敛腼腆&#xff0c;怎么突然间变得容光焕发、大大咧咧&#xff0c;脸上肚子上也多了几斤肉&#xff0c;整…

基于ssm的酒店民宿管理系统的设计与实现

系统主要功能介绍&#xff1a; 1、登录&#xff1a;输入账号密码进行登录&#xff0c;登录后才能进行相应的操作 2、客房管理&#xff1a;客房管理主要是酒店预订&#xff0c;可以选择不同的房间&#xff0c;比如大床房&#xff0c;家庭房等&#xff0c;入住办理&#xff0c;…

【力扣刷题日记】1076.项目员工II

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 1076.项目员工II 表&#xff1a;Project 列名类型project_idintemployee_idint (project_id, employee_id)…

AIGC实战——Transformer模型

AIGC实战——Transformer模型 0. 前言1. T52. GPT-3 和 GPT-43. ChatGPT小结系列链接 0. 前言 我们在 GPT (Generative Pre-trained Transformer) 一节所构建的 GPT 模型是一个解码器 Transformer&#xff0c;它逐字符地生成文本字符串&#xff0c;并使用因果掩码只关注输入字…

代码随想录算法训练营第五十天|123.买卖股票的最佳时机III、188.买卖股票的最佳时机IV

123.买卖股票的最佳时机III 刷题https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/文章讲解https://programmercarl.com/0123.%E4%B9%B0%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BAIII.html视频讲解https://www…

Arduino中的map函数

一、案例 val analogRead(dyPin); //读取模拟口的模拟量数值 dyValuemap(val,0,1023,0,500);//这个函数是将电位器调节的模拟量的值按比例转换成对应的电压量 问题&#xff0c;为什么不是0~499呢&#xff1f; 其实也行↓ 当map(val, 0, 1023, 0, 500)被调用时&#xff0…

YiYi-Web项目介绍

YiYi-Web项目介绍 1. 简介2. 使用2.1 后端开发环境2.2 前端开发环境 3. 测试环境&#xff1a;4. 更新日志5. 打包情况6.项目截图 本项目前端是html、css、js、jQuery基础技术。 后端都是最新的SpringBoot技术&#xff0c;不分离版本&#xff0c; 是最基础的项目开发教程&#x…

yolov5训练并生成rknn模型部署在RK3588开发板上,实现NPU加速推理

简介 RK3588是瑞芯微&#xff08;Rockchip&#xff09;公司推出的一款高性能、低功耗的集成电路芯片。它采用了先进的28纳米工艺技术&#xff0c;并配备了八核心的ARM Cortex-A76和Cortex-A55处理器&#xff0c;以及ARM Mali-G76 GPU。该芯片支持多种接口和功能&#xff0c;适…

atoi函数详解

atoi函数使用方法 在c官网中是这样介绍atoi函数的 通俗的讲就是把字符串中的字符数字转换为整形数字&#xff0c;遇到空格就跳过&#xff0c;如果在字符串开始遇到不是有效的整数比如说abc就直接返回0&#xff0c;如果遇到像这种情况123abc345这个就只返回123&#xff0c;这个…

申请Github Education获取免费Copilot权限(2024.3.18实测成功)

起因&#xff1a;旧帐户Copilot权限被封 我已经离开Github Copilot就无法独自耐着性子写代码了&#xff08;懒惰AI成瘾性&#xff09;&#xff0c;这两天Github Copilot不知道为什么在大规模封号&#xff0c;我不幸也被封号了&#xff08;禁用掉了Github Copilot权限&#xff…

大数据技术原理与应用 01.大数据概述

不可以垂头丧气&#xff0c;会显矮 —— 24.3.24 参考学习&#xff1a;厦门大学 林子雨老师 大数据技术原理与应用 一、大数据时代 大数据概念、影响、应用、关键技术 大数据与云计算、物联网的关系 ①三次信息化浪潮时代 ②第三次信息化浪潮的技术支撑 1>存储设备容量不断…

微服务(基础篇-003-Nacos)

目录 Nacos注册中心&#xff08;1&#xff09; 认识和安装Nacos&#xff08;1.1&#xff09; Nacos快速入门&#xff08;1.2&#xff09; 服务注册到Nacos(1.2.1) Nacos服务分级存储模型&#xff08;1.3&#xff09; 配置集群&#xff08;1.3.1&#xff09; 根据集群修改…

[ Linux ] git工具的基本使用(仓库的构建,提交)

1.安装git yum install -y git 2.打开Gitee&#xff0c;创建你的远程仓库&#xff0c;根据提示初始化本地仓库&#xff08;这里以我的仓库为例&#xff09; 新建好仓库之后跟着网页的提示初始化便可以了 3.add、commit、push三板斧 git add . //add仓库新增&#xff08;变…

阿里云倚天云服务器怎么样?如何收费?

阿里云倚天云服务器CPU采用倚天710处理器&#xff0c;租用倚天服务器c8y、g8y和r8y可以享受优惠价格&#xff0c;阿里云服务器网aliyunfuwuqi.com整理倚天云服务器详细介绍、倚天710处理器性能测评、CIPU架构优势、倚天服务器使用场景及生态支持&#xff1a; 阿里云倚天云服务…

AI预测福彩3D第17弹【2024年3月24日预测--第1套算法重新开始计算第14次测试】

今天周末&#xff0c;家里事情比较多&#xff0c;回来的比较晚&#xff0c;3D预测的结果只能在今天晚上7点半左右发布了。废话不多说了&#xff0c;直接上结果吧~ 最终&#xff0c;经过研判分析&#xff0c;2024年3月24日福彩3D的七码预测结果如下&#xff1a; 百位&#xff1a…

大宇、德国ODI、希亦超声波清洗机值得买吗?精品轻松分辨

长时间佩戴眼镜不清洗的话上面的细菌堪比马桶这么脏&#xff01;从佩戴眼镜开始就没有人告诉过我手动清洗眼镜会非常容易刮花镜片&#xff0c;是我自己佩戴眼镜这么长时间观察到的&#xff0c;后面了解到超声波清洗机可以很好的保护到眼镜&#xff0c;于是开始做功课挑选超声波…

对话李喆:Martech在中国需要转化成以客户需求为驱动的模式

关于SaaS模式在中国的发展&#xff0c;网上出现多种声音。Marteker近期采访了一些行业专家&#xff0c;围绕SaaS模式以及Martech在中国的发展提出独特观点。赛诺贝斯副总裁李喆认为&#xff0c;SaaS可以分为场景化的SaaS、一体化的SaaS和功能化的SaaS&#xff0c;三者都有一定规…

MultiArch与Ubuntu/Debian 的交叉编译

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;基于ARM 的Linux系统的交叉编译 下一篇&#xff1a;MultiArch与Ubuntu/Debian 的交叉编译 警告&#xff1a; 本教程可能包含过时的信息。 什么是“MultiArch” OpenCV 可能…