UDP编程详解

2023-05-16

1.1 编程准备—字节序、地址转换

1.1.1 字节序概述

字节序概念:是指多字节数据的存储顺序

分类:
​ 大端格式:将低位字节数据存储在低地址
​ 小端格式:将高位字节数据存储在低地址

注意:
​ LSB:低地址
​ MSB:高地址

如何判断当前系统的字节序:

#include <stdio.h>

union un
{
	int a;
	char b;
}

int main()
{
	union un myun;
	myun.a = 0x12345678;
	printf("a = %#x\n",myun.a);
	printf("b = %#x\n",myun.b);
	
	if(myun.b == 0x78){
		printf("小端存储\n");
	}
	else{
		printf("大端存储\n");
	}
	
	return 0;
}

结果展示:

1.1.2 字节序转换函数
  1. 网络协议制定了通讯字节序——大端
  2. 只有在多字节数据处理时才需要考虑字节序
  3. 运行在同一台计算上的进程相互通信时,一般不用考虑字节序
  4. 异构计算机之间通讯,需要转换自己的字节序为网络字节序

在需要字节序转换的时候一般调用特定字节序转换函数

host --> network
1 -- htonl
#include<arpa/inet.h>
uint32_t htonl(uint32_t hostint32);
功能:
	将32位主机字节序数据转换成网络字节序数据
参数:
	hostint32:	待转换的32位主机字节序数据
返回值:
	成功:返回网络字节序的值

2 -- htons
#include<arpa/inet.h>
uint16_t htons(uint16_t hostint16);
功能:
	将16位主机字节序数据转换成网络字节序数据
参数:
	hostint16:	待转换的16位主机字节序数据
返回值:
	成功:返回网络字节序的值

network --> host
3 -- ntohl
#include<arpa/inet.h>
uint32_t ntohl(uint32_t netint32);
功能:
	将32位网络字节序数据转换成主机字节序数据
参数:
	netint32:	待转换的32位网络字节序数据
返回值:
	成功:返回主机字节序数据

4 -- ntohs
#include<arpa/inet.h>
uint16_t ntohs(uint16_t netint16);
功能:
	将16位网络字节序数据转换成主机字节序数据
参数:
	netint16:	待转换的16位网络字节序数据
返回值:
	成功:返回主机字节序数据

案例:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
	int a = 0x12345678;
	short a = 0x1234;
	
	printf("%#x\n",htonl(a));

	printf("%#x\n",htons(b));

	return 0;
}
1.1.3 地址转换函数

人为识别的ip地址是点分十进制的字符串形式,但是计算机或网络中识别的ip地址是整型数据,所以需要转化

1.1.3.1 inet_pton函数
#include <arpa/inet.h>
int inet_pton(int family,const char *strptr,void *addrptr);
功能:
	将点分十进制数串转换成32位无符号整数
参数:
	family	协议族
			AF_INET			ipv4网络协议
			AF_INET6		iPV6网络协议
	strptr	点分十进制数串
	addrptr	32位无符号整数的地址
返回值:
	成功返回1、失败返回其他

案例:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
	char ip_str[] = "198.168.3.103";
	unsigned int ip_int = 0;
	unsigned char *ip_p = NULL;
	
	//将点分十进制ip地址转化为32位无符号整形数据
	inet_pton(AF_INET,ip_str,&ip_int);
	printf("ip_int = %d\n",ip_int;

	ip_p = (char *)&ip_int;
	printf("in_uint = %d,%d,%d,%d\n",*ip_p,*(ip_p+1),*(ip_p+2),*(ip_p+3));

	return 0;
}
1.1.3.2 inet_ntop()函数
#include <arpa/inet.h>
const char * inet_ntop(int family,const void *addrptr,char *strptr, size_t len);
功能:
	将32位无符号整数转换成点分十进制数串的ip地址
参数:
	family	协议族
			AF_INET			ipv4网络协议
			AF_INET6		iPV6网络协议
	addrptr	32位无符号整数的地址
	strptr	点分十进制数串
	len		strptr缓存区长度
			len的宏定义
				#define INET_ADDRSTRLEN  16  //for ipv4
				#define INET6_ADDRSTRLEN 46	 //for ipv6
返回值:
	成功返回1、失败返回其他

案例:

#include <stdio.h>
#include <arpa/inet.h>

int main()
{
	unsigned char ip_int[] = {198,168,3,103};
	char ip_str[16] = ""; //"198.168.3.103"刚好16个字节
	inet_ntop(AF_INET,&ip_int,ip_str,16);
	printf("ip_s = %s\n",ip_str);
	return 0;
}
1.1.3.3 inet_addr()和inet_ntoa()

这两个函数只能用在ipv4地址的转换

#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

in_addr_t inet_addr(const char *cp);
功能:
	将点分十进制ip地址转化为整型数据
参数:
	cp:点分十进制的ip地址
返回值:
	成功:整型数据
	
char *inet_ntoa(struct in_addr in);
功能:将整型数据转化位点分十进制的ip地址
参数:
	in:	保存ip地址的结构体
返回值:
	成功:点分十进制的ip地址

1.2 UDP介绍、编程流程

1.2.1 UDP概述

UDP协议
​ 面向无连接的用户数据报协议,在传输数据前不需要先建立连接;目的主机的运输层收到UDP报文后,不需要给出任何确认。

UDP特点

  1. 相对TCP速度稍快些;
  2. 简单的请求/应答应用程序可以使用UDP;
  3. 对于海量数据传输不应该使用UDP;
  4. 广播和多播应用必须使用UDP。

UDP应用
​ DNS(域名解析)、NFS(网络文件系统)、RTP(流媒体)等
​ 一般语音和视频童话都是使用UDP通信的。

1.2.2 网络编程接口socket

Socket作用:提供不同主机上的进程之间的通信;

Socket特点:

  1. socket也称”套接字“;
  2. 是一种文件描述符,代表了一个通信管道的一个端点;
  3. 类似对文件的操作一样,可以使用read、write、close等函数对socket套接字进行网络数据的收取和发送等操作;
  4. 得到socket套接字(描述符)的方法调用socket()。

socket分类:
SOCK_STREAM, 流式套接字,用于TCP;
SOCK_DGRAM, 数据报套接字,用于UDP;
SOCK_RAW, 原始套接字,对于其他层次的协议操作时需要使用这个类型

1.2.3 UDP编程C/S架构

UDP网络编程流程:

服务器:
​ 创建套接字socket();
​ 将服务器的ip地址、端口号与套接字进行绑定;
​ 接收数据recvfrom()
​ 发送数据sendto()

客户端:
​ 创建套接字socket()
​ 发送数据sendto()
​ 接收数据recvfrom()
​ 关闭套接字close()

1.2.4 UDP编程—创建套接字
1.2.4.1 创建套接字
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol);
功能:
	创建一个套接字,返回一个文件描述符;
参数:
	domain:通信域,协议族;
			AF_UNIX		本地通信
			AF_INET		ipv4网络协议
			AF_INET6	ipv6网络协议
			AF_PACKET	底层接口
	type:	套接字的类型
			SOCKET_STREAM	流式套接字(TCP)
			SOCKET_DGRAM	数据报套接字(UDP)
			SOCKET_RAW		原始套接字(用于链路层)
	特点:
			创建套接字时,系统不会分配端口
			创建的套接字默认属性是主动的,即主动发起服务的请求,当作为服务器时,往往需要修改为被动的。
1.2.4.2 创建UDP套接字demo
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types>
int main(int argc, char const *argv[]){
	//使用socket函数创建套接字
	//创建一个用于UDP网络编程的套接字
	int sockfd = 0;
	sockfd = socket(AF_INET,SOCKE_DGRAM,0);
	if(sockfd < 0){
		perror("socket");
		exit(-1);
	}
	printf("sockfd = %d\n",sockfd);//任何一个进程在开启或创建的时候会分配三个文件描述符(0,1,2)
}
1.2.5 UDP编程—发送、绑定、接收数据
1.2.5.1 ipv4套接字地址结构

在网络编程中经常使用的结构体

#include <netinet/in.h>

struct in_addr
{
	in_addr_t s_addr; // 4字节
}

struct sockaddr_in
{
	sa_family_t sinfamily; //2字节;
	in_port_t 	sin_port;  //2字节;
	struct in_addr	sin_addr; //4字节;
	char sin_zero[8]		//8字节
}



为了使不同格式地址能被传入套接字函数,地址须要强制转换成通用套接字地址结构,原因是因为不同场合所使用的结构体不一样,但是调用的函数却是同一个,所以定义一个通用结构体,当在指定场合使用时,在根据要求传入指定的结构体即可。

通用结构体sockaddr

头文件#include <netinet/in.h>
struct sockaddr
{
	sa_family_t sa_family;	//2字节
	char sa_data[14]		//14字节
}
//注意,以上3个结构在Linux系统中已经定义
1.2.5.2 两种地址结构使用场合

在定义源地址和目的地址结构的时候,选用struct sockaddr_in;
例:

struct sockaddr_in my_addr;

当调用编程接口函数,且该函数需要从传入地址结构时需要用struct sockaddr进行强制转换
例:

bind(sockfd,struct sockaddr*)&my_addr,sizeof(my_addr));
1.2.5.3 发送数据—sendto函数
ssize_t sendto(int sockfd,const void *buf,size_t nbytes,int flags,const struct sockaddr *to, socklen_t addrlen);
功能:
	向to结构体指针中指定的ip,发送UDP数据
参数:
	sockfd:		套接字
	buf:		发送数据缓存区
	nbytes:		发送数据缓存区的大小
	flags:		一般为0
	to:			指向目的主机地址结构体的指针
	addrlen:	to指向内容的长度
注意:
	通过to和addrlen确定目的地址
	可以发送0长度的UDP数据包
返回值:
	成功:发送数据的字符数
	失败:-1
1.2.5.4 向“网络调试助手”发送消息

设置网络调试助手(在windows下运行的软件)中的属性
注意:ip地址不能随意设置,必须是当前windows的ip地址

ubuntu下客户段的代码编写:

#include <stdio.h>			//printf
#include <stdlib.h>			//exit
#include <sys/types.h>
#include <sys/socket.h>		//socket
#include <netinet/in.h>		//sockaddr_in
#include <arpa/inet.h>		//htons	inet_addr
#include <unisted.h>		//close
#include <string.h>

int main()
{
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) == -1)
	{
		perrpr("fail to socket");
		exit(1);
	}
	printf("sockfd = %d",sockfd);
	
	//第二步:填充服务器网络信息结构体	sockaddr_in(一般不需要,系统会自动分配)
	struct sockaddr_in	severaddr;
	serveraddr.sin_family = AF_INET;	//协议族	AF_INET:ipv4网络协议
	serveraddr.sin_addr.s_addr = inet_addr("192.168.3.78");	//ip地址
	serveraddr.sin_port = htons(8080);
	socklen_t addrlen = sizeof(serveraddr)
	
	//第三步:发送数据
	char buf[128] = "";
	while(1)
	{
		fgets(buf,128,stdin);
		buf[strlen(buf) - 1] = '\0'; 	//把buf字符串中的\n转化为\0
		if(sendto(sockfd,buf,N,0,(struct sockaddr *)&serveraddr,addrlen));
		{
			perror("fail to sendto");
			exit(1);
		}
	}
	
	//第四步:关闭套接字文件描述符
	close(socketfd);
	
	return 0;
}
1.2.5.5 绑定bind函数

UDP网络程序想要收取数据需要什么条件?
确定的ip地址
确定的port

怎么完成上面的条件呢?
​ 接收端:使用bind函数,来完成地址结构与socket套接字的绑定,这样ip、port就固定了;
​ 发送端:在sendto函数中指定接收端的ip、port,就可以发送数据了。

由于服务器是被动的,客户端是主动的,所以一般先运行服务器,后运行客户端,所以服务器需要固定自己的信息(ip地址和端口号),这样客户端才可以找到服务器并与之通信,但是客户端一般不需要bind绑定,因为系统会自动给客户端分配ip地址和端口号。

int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
功能:
	将本地协议地址与sockfd绑定
参数:
	sockfd:		文件描述符,socket的返回值
	addr:		网络信息结构体
					通用结构体(一般不用)
						struct sockaddr
					网络信息结构体	sockaddr_in
						#include<netinet/in.h>
						struct sockaddr_in
	addrlen:	addr的长度
返回值:
	成功:0
	失败:-1
	

示例:

#include <stdio.h>			//printf
#include <stdlib.h>			//exit
#include <sys/types.h>
#include <sys/socket.h>		//socket
#include <netinet/in.h>		//sockaddr_in
#include <arpa/inet.h>		//htons	inet_addr
#include <unisted.h>		//close
#include <string.h>

int main(int argc, char **argv)
{
	if(argc < 3){
		fprintf(stderr,"Useage: %s ip port\n",argv[0]);
		exit(1);
	}
	
	//第一步:创建套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_DGRAM,0))== -1)
	{
		perror("fail to socket");
		exit(1);
	}
	//第二步:将服务器的网络信息结构绑定前进行填充
	struct sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(agrv[1]);
	serveraddr.sinport = htons(atoi(argv[2]));
	
	//第三步:将网络信息结构体与套接字绑定
	if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))==-1){
		perror("fail to bind");
		exit(1);
	}
	
	return 0;
}
1.2.5.6 接收数据—recvfrom函数
ssize_t recvfrom(int sockfd,void *buf,size_t nbytes,int flags, struct sockaddr *from,socklen_t *addrlen);
功能:
	接收UDP数据,并将源地址信息保存在from指向的结构中
参数:
	sockfd:		套接字
	buf:		接收数据缓冲区
	nbytes:		接收数据缓冲区的大小
	flags:		套接字标志(常为0)
					0				阻塞
					MSG_DONTWAIT	非阻塞
	from:		源地址结构体指针,用来保存数据的来源
	addrlen:	from所指内容的长度
注意:
	通过from和addrlen参数存放数据来源信息
	from和addrlen可以为NULL,表示不保存数据来源
返回值:
	成功:接收到的字符数
	失败:-1
1.2.5.7 接收“网络调试助手”的数据

此时网络调试助手作为客户端,ubuntu的程序作为服务器

设置客户端(网络调试助手)

设置服务器(ubuntu程序)

#include <stdio.h>			//printf
#include <stdlib.h>			//exit
#include <sys/types.h>
#include <sys/socket.h>		//socket
#include <netinet/in.h>		//sockaddr_in
#include <arpa/inet.h>		//htons	inet_addr
#include <unisted.h>		//close
#include <string.h>

int main(int argc, char **argv)
{
	if(argc < 3){
		fprintf(stderr,"Useage: %s ip port\n",argv[0]);
		exit(1);
	}
	
	//第一步:创建套接字
	int sockfd;
	if((sockfd = socket(AF_INET,SOCK_DGRAM,0))== -1)
	{
		perror("fail to socket");
		exit(1);
	}
	//第二步:将服务器的网络信息结构绑定前进行填充
	struct sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(agrv[1]);			//192.168.3.103
	serveraddr.sinport = htons(atoi(argv[2]));					//9999
	
	//第三步:将网络信息结构体与套接字绑定
	if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))==-1){
		perror("fail to bind");
		exit(1);
	}
	
	//接收数据
	char buf[128] = "";
	struct sockaddr_in clientaddr;
	socklen_t addrlen = sizeof(struct sockaddr_in);
	while(1){
		if(recvfrom(sockfd,128,0,(struct sockaddr *)&clientaddr,&addrlen) == -1){
			perror("fail to recvfrom");
			exit(1);
		}
	}
	
	//打印数据
	//打印客户端的ip地址和端口号
	printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
	//打印接收到数据
	printf("from client:%s\n",buf);
	
	
	return 0;
}
1.2.6 UDP编程—client、server

其中在网络编程开发中client和server双方既可以有发送数据还可以接收数据;一般认为提供服务的一方为server,而接受服务的另一方为client。

1.2.6.1 C/S架构回顾
1.2.6.2 UDP客户端注意点

1.本地IP、本地端口(我是谁)
2.目的IP、目的端口(发给谁)
3.在客户端的代码中,我们只设置了目的IP、目的端口

客户端的本地ip、本地port是我们调用sendto的时候linux系统底层自动给客户端分配的;分配端口的方式为随机分配,即每次运行系统给的port不一样

//UDP客户端的实现
#include <stdio.h>			//printf
#include <stdlib.h>			//exit
#include <sys/types.h>
#include <sys/socket.h>		//socket
#include <netinet/in.h>		//sockaddr_in
#include <arpa/inet.h>		//htons	inet_addr
#include <unisted.h>		//close
#include <string.h>

int main(int argc,char **argv)
{
	if(argc < 3){
		fprintf(stderr,"Usage: %s <ip> <port>\n",agrv[0]);
		exit(1);
	}
	
	int sockfd;		//文件描述符
	struct sockaddr_in serveraddr;		//服务器网络信息结构体
	socklen_t addrlen = sizeof(serveraddr);
	
	//第一步:创建套接字
	if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) < 0){
		perror("fail to socket");
		exit(1);
	}
	
	//客户端自己指定自己的ip地址和端口号,一般不需要,系统会自动分配
	struct sockaddr_in clientaddr;
	clientaddr.sin_family = AF_INET;
	clientaddr.sin_addr.s_addr = inet_addr(argv[3]); 	//客户端的ip地址
	clientaddr.sin_port = htons(atoi(argv[4]));			//客户端的端口号
	
	//第二步:填充服务器网络信息结构体
	//inet_addr:将点分十进制字符串ip地址转换为整型数据
	//htons:将主机字节序转化为网络字节序
	//atoi:将数字型字符串转化为整型数据
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	
	//第三步:进行通信
	char buf[32] = "";
	while(1){
		fgets(buf,sizeof(buf),stdin);
		buf[strlen(buf) - 1] = '\0';
		
		if(sendto(sockfd,buf,sizeof(buf),(struct sockaddr *)&serveraddr,sizeof(serveraddr)))
		{
			perror("fail to sendto");
			exit(1);
		}
		
		char text[32] = "";
		if(recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&serveraddr,&addrlen) == -1){
			perror("fail to recvfrom");
			exit(1);
		}
		printf("from server:%s\n",text);
	}
	
	//第四步:关闭文件描述符
	close(sockfd);
	
	return 0;
}
1.2.6.3 UDP服务器注意点

1.服务器之所以要bind是因为它的本地port需要是固定的,而不是随机的
2.服务器也可以主动地给客户端发送数据
3.客户端也可以用bind,这样客户端的本地端口就是固定的了,但一般不这样做。

//UDP服务器的实现
#include <stdio.h>			//printf
#include <stdlib.h>			//exit
#include <sys/types.h>
#include <sys/socket.h>		//socket
#include <netinet/in.h>		//sockaddr_in
#include <arpa/inet.h>		//htons	inet_addr
#include <unisted.h>		//close
#include <string.h>

int main(int argc,char **argv)
{
	if(argc < 3){
		fprintf(stderr,"Usage: %s <ip> <port>\n",agrv[0]);
		exit(1);
	}
	
	int sockfd;		//文件描述符
	struct sockaddr_in serveraddr;		//服务器网络信息结构体
	socklen_t addrlen = sizeof(serveraddr);
	
	//第一步:创建套接字
	if((sockfd = socket(AF_INET,SOCK_DGRAM,0)) < 0){
		perror("fail to socket");
		exit(1);
	}
	
	//第二步:填充服务器网络信息结构体
	//inet_addr:将点分十进制字符串ip地址转换为整型数据
	//htons:将主机字节序转化为网络字节序
	//atoi:将数字型字符串转化为整型数据
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));
	
	//第三步:将套接字与服务器网络信息结构体绑定
	if(bind(sockfd,(struct sockaddr *)&serveraddr, addrlen) < 0){
		perror("fail to bind");
		exit(1);
	}
	
	while(1){
		//第四步:进行通信
		char text[32] = "";
		struct sockaddr_in clientaddr;
		if(recvfrom(sockfd,text,sizeof(text),0,(struct sockaddr *)&clientaddr,&addrlen) == -1){
			perror("fail to recvfrom");
			exit(1);
		}
		printf("[%s - %d]: %s\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),text);
		
		strcat(text,"*_*");
		
		if(sendto(sockfd,text,sizeof(text),0,(struct sockaddr *)&clientaddr,addrlen)){
			perror("fail to sendto");
			exit(1);
		}
	}
	
	//第四步:关闭文件描述符
	close(sockfd);
	
	return 0;
}

执行结果:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UDP编程详解 的相关文章

随机推荐

  • 约瑟夫环详解

    package newjosephu public class myfinaljosephu 你可能会说crazy 我只想说ez xff01 public static void main String args circlelinkedl
  • 01背包问题(滚动数组实现的逻辑)

    package tttest public class mybetterbag public static void main String args int weight 61 1 3 4 int bagsize 61 4 int val
  • java递归实现辗转相除法

    public static int getres int a int b if b 61 0 return getres b a b return a
  • java 线索二叉树的构建

    public class test public static void main String args Node root 61 new Node 1 Node node2 61 new Node 2 Node node3 61 new
  • VINS-Mono

    非极大值抑制 NMS算法 xff0c 思想是搜索局部最大值 xff0c 抑制非极大值 输入 xff1a 目标边界框及其对应的置信度的分列表 xff0c 设定阈值 xff0c 阈值用来删除重叠较大的边界框 IoU xff1a 两个边界框的交集
  • vtk世界坐标系与屏幕坐标系的转换

    世界坐标系 gt 屏幕坐标系 double worldCoord 3 61 100 100 20 vtkSmartPointer lt vtkCoordinate gt pCoorPress 61 vtkSmartPointer lt vt
  • VTK读取DCM保存成png

    VTK读取DCM保存成png include lt vtkSmartPointer h gt include lt vtkImageViewer2 h gt include lt vtkDICOMImageReader h gt inclu
  • 姿态矩阵与姿态向量的相互转化

    罗德里格斯公式 转轴 n 是矩阵 R 特征值 1 对应的特征向量 求解此方程 再归一化 就得到了旋转轴 CV EXPORTS W void Rodrigues InputArray src OutputArray dst OutputArr
  • 电脑连接树莓派Zero W

    电脑连接树莓派ZeroW 陈拓 2018 05 16 2018 06 09 0 概述 本位介绍两种电脑连接树莓派PiZero W的方法 xff1a l 电脑通过USB以太网连接树莓派ZeroW l 电脑通过WiFi连接树莓派ZeroW 1
  • 读取 ttf 字体文件并提取汉字轮廓

    读取 ttf 字体文件并提取汉字轮廓 ttf 免费下载 ttf 下载链接 编译安装 freetype freetype下载链接 实现效果 demo下载地址
  • CRC-16/MODBUS 校验位计算

    实验指令 xff1a 前23位表示 信息头 43 信息内容 24 xff0c 25位是待计算的校验位 26位是结束码 7E 01 00 00 01 10 02 00 27 00 0C 00 02 00 00 00 00 00 50 00 3
  • QSS之QPushButton 详细介绍

    官方参考资料 span class hljs tag QPushButton span span class hljs rules span class hljs comment 前景色 span span class hljs rule
  • bochs 详细介绍

    BOCHS 简介及配置 Bochs 简介 Bochs xff08 读音同 Box xff09 是用C 43 43 开发的以 LGPL 许可证发放的开放源代码的x86 xff0c x86 64模拟器 xff0c 模拟整个PC 它被设计成可以运
  • QT信号槽传递复杂参数

    原理 1 先将想要传递的多个数据封装成一个结构体 2 然后再用QVariant打包 3 通过信号槽机制传送 4 传到目标类后再解开QVariant的到自己想要的数据 实验目标 按下按钮后 xff0c 将界面上3个控件的值传递到Product
  • Opencv 轮廓提取

    边缘检测的一般步骤 滤波 消除噪声增强 使边界轮廓更加明显检测 选出边缘点 方法一 Canny算法 特点 xff1a 轮廓细腻 include lt opencv2 opencv hpp gt include lt opencv2 high
  • opencv矩形识别

    include lt opencv2 highgui highgui hpp gt include lt opencv2 imgproc imgproc hpp gt include lt opencv2 core core hpp gt
  • 【怎么在KEIL中自定义STM32的栈顶地址】

    STM32的启动文件中定义的栈和堆的大小 xff0c 由于栈是向下生长的 所以我们一般希望将栈顶设置为RAM的最高有效地址 这样就可以最大限度保护栈不会溢出 但是STM32的启动文件居然不是这样的思路 那STM32的启动文件的分配思路是怎么
  • VaR和CVaR举例说明_笔记转载

    风险价值VaR成为金融市场风险度量的主流指标 VaR自1993年发展至今已作为金融市场风险度量的主流指标被各银行 投资公司 证券公司及金融监管机构广泛采用 VaR不仅具有概念简单 xff0c 易于沟通和理解的优点 xff0c 而且为不同金融
  • 树莓派GPIO控制

    树莓派GPIO 控制 陈拓 2018 06 09 2018 06 10 0 概述 本文介绍树莓派 Zero W的GPIO控制 xff0c 并用LED看效果 也适宜于树莓派3B 43 0 1 树莓派GPIO编号方式 功能物理引脚 从左到右 x
  • UDP编程详解

    1 1 编程准备 字节序 地址转换 1 1 1 字节序概述 字节序概念 xff1a 是指多字节数据的存储顺序 分类 xff1a 大端格式 xff1a 将低位字节数据存储在低地址 小端格式 xff1a 将高位字节数据存储在低地址 注意 xff