网络协议深度解析:SSL、 TLS、HTTP和 DNS(C/C++代码实现)

在数字化时代,网络协议构成了互联网通信的基石。SSL、TLS、HTTP和DNS是其中最关键的几种,它们确保了我们的数据安全传输、网页的正确显示以及域名的正常解析。

要理解这些协议,首先需要了解网络分层模型。SSL和TLS位于传输层之上,用于加密数据;HTTP是应用层协议,负责网页内容的传输;而DNS则在应用层与传输层之间,它将人类可读的域名转换为机器可读的IP地址。

详细分析各个协议

DNS(域名系统)的主要作用

域名系统(DNS)是互联网的一项关键服务,它负责将用户可读的域名(如www.example.com)转换为网络设备可识别的IP地址(如192.0.2.1)。这个过程被称为域名解析。没有DNS,我们就需要记住复杂且难以记忆的数值序列来访问网站,这就大大降低了互联网的可用性。

DNS查询过程

当用户尝试访问一个网站时,以下是一个典型的DNS查询过程:

  1. 首先,用户的设备(比如电脑或手机)上的应用程序(通常是网页浏览器)会检查本地缓存,看看是否已经有对应域名的IP地址记录。如果有,它就直接使用该IP地址。

  2. 如果本地缓存中没有记录,设备会向配置的本地DNS服务器(通常由互联网服务提供商提供)发起解析请求。

  3. 本地DNS服务器收到请求后,会先检查自己的缓存。如果找到相应的记录,它会直接返回结果给请求者。

  4. 如果本地DNS服务器没有相关信息,它就会进行递归查询。在递归查询中,本地DNS服务器会代表请求者向其他DNS服务器发起查询请求,直到得到最终结果为止。

  5. 迭代查询则不同,它是一步接一步的过程,每一步只提供下一级查询的线索。在这种情况下,本地DNS服务器会告诉请求者下一个应该查询的DNS服务器的地址,请求者接着向这个新的地址发送请求,如此循环直至达到最终的权威名称服务器。

  6. 权威名称服务器负责管理特定域的DNS记录,它对所请求的域名拥有权威的信息。当查询到达这里,权威服务器会返回所请求的域名对应的IP地址记录。

  7. 一旦本地DNS服务器从权威名称服务器获取到所需的IP地址,它会将这个信息缓存起来以供后续使用,并将结果返回给原始请求者。

  8. 最后,原始请求者的设备收到解析出的IP地址,并使用该地址连接到目标网站。

从上面信息了解到,DNS通过一种分布式数据库的方式工作,它将域名解析任务分散到全球众多的DNS服务器上。这些服务器相互协作,通过递归或迭代查询,最终将用户友好的域名转换为网络上的设备能够理解的IP地址。

HTTP(超文本传输协议)的主要作用

超文本传输协议(HTTP)是用于传输网页文档的核心技术,它基于请求/响应模型工作。HTTP允许客户端(通常是Web浏览器)向服务器发送请求,并接收包含网页内容的响应。这个协议是无状态的,意味着每次通信后不会留下记录,除非使用某些技术如Cookie或会话标识来维持状态。

HTTP在应用层上运行,通常通过TCP/IP协议在网络中传输数据,但也可以在其他协议上运行,只要它们能提供类似可靠的流传输服务。

HTTP请求/响应模型

HTTP通信遵循一个非常直接的模型:一方(客户端)发送请求,另一方(服务器)返回响应。

  • 请求:由以下几部分组成:

    • 请求行:包含HTTP方法(如GET、POST)、请求的URL和HTTP协议版本。
    • 请求头:包含关于请求的附加信息,例如用户代理、接受的内容类型、语言等。
    • 空行:用以分隔请求头和请求体。
    • 请求体:对于一些请求方法(如POST、PUT),可能包含额外的数据。
  • 响应:同样由以下几部分组成:

    • 状态行:包含HTTP协议版本、状态码和状态消息。
    • 响应头:包含关于响应的附加信息,例如内容类型、内容长度、服务器信息等。
    • 空行:用以分隔响应头和响应体。
    • 响应体:实际返回给客户端的数据,比如HTML文档、图片或其他资源。

HTTP方法和状态码

  • 方法

    • GET:请求指定资源的表示形式。使用查询字符串可包含额外信息。
    • POST:提交要处理的数据,比如表单内容。
    • PUT:上传指定的资源替换目标资源。
    • DELETE:删除指定的资源。
    • HEAD:请求资源的头部信息,不返回实体主体。
    • OPTIONS:描述目标资源的通信选项。
    • CONNECT:建立网络隧道,转换为代理服务器。
    • 还有其他不常用的方法,如TRACE、PATCH等。
  • 状态码

    • 1xx(信息):请求已接收,继续处理。
    • 2xx(成功):请求正常处理完毕。例如,200 OK 表示请求成功,204 No Content 表示请求成功但没有内容返回。
    • 3xx(重定向):需要进一步操作以完成请求。例如,301 Moved Permanently 表示资源永久移动到新位置,304 Not Modified 表示资源未修改,可以使用缓存的副本。
    • 4xx(客户端错误):请求有语法错误或请求无法实现。例如,400 Bad Request 表示服务器无法理解请求的语法,404 Not Found 表示找不到请求的资源。
    • 5xx(服务器错误):服务器在处理请求的过程中发生了错误。例如,500 Internal Server Error 表示服务器内部错误,503 Service Unavailable 表示服务器目前无法处理请求。

总结来说,HTTP是Web通信的基础,它定义了客户端和服务器之间交换信息的格式和规则。了解HTTP的方法和状态码对于开发和维护网站和网络应用程序至关重要。

SSL/TLS(安全套接字层/传输层安全)

SSL和TLS用于加密客户端与服务器之间的通信,保障数据传输安全

SSL(Secure Sockets Layer)和TLS(Transport Layer Security)是网络安全中非常重要的技术。它们通过综合运用对称加密、非对称加密、消息认证码等密码学技术,为互联网上的数据传输提供安全保障。具体来说,SSL和TLS的主要作用包括:

  • 加密数据:防止敏感信息如登录凭证、交易细节等在传输过程中被未授权的第三方窃取;
  • 数据完整性:确保数据在传输过程中没有被篡改;
  • 身份验证:验证通信双方的身份,保证用户正在与真正的服务器进行交流。

SSL/TLS的握手过程涉及多个步骤,这个过程确定了加密参数,并在客户端与服务器之间建立了加密连接。具体如下:

  1. 客户端发起请求:客户端发送一个"client hello"消息到服务器,包含版本信息、加密套件列表、压缩算法和其他必要信息。此消息以明文形式发送。
  2. 服务器响应:服务器回应一个"server hello"消息,选择客户端提供的加密套件,并发送自己的数字证书及相关信息。
  3. 客户端验证:客户端接收服务器的证书,并对证书的有效性进行验证。如果需要,客户端也可能发送自己的证书给服务器进行双向验证。
  4. 密钥协商:双方根据协商的加密套件进行密钥交换。如果是使用RSA算法,服务器会用自己的私钥加密一个预备主秘钥,客户端用服务器的公钥解密得到主秘钥。
  5. 确立加密参数:使用从前面步骤得到的主秘钥,生成加密和MAC(消息认证码)密钥,这些密钥将用于后续的所有通信。
  6. 加密通信:双方确认无误后,开始使用协商好的加密参数进行加密通信。

了解HTTPS(HTTP over SSL/TLS)的结合方式

HTTPS,全称为HTTP Secure,也就是安全版的HTTP,它将HTTP协议与SSL/TLS协议结合起来,在互联网上提供加密的数据传输。具体结合方式如下:

  1. 应用层与传输层的安全封装:在标准的HTTP通信过程中,所有的数据传输都是明文形式的,这意味着数据可以被任何监听网络流量的人读取。而HTTPS通过在HTTP之上添加SSL/TLS协议,为数据提供了加密处理。这样即便是数据被截获,没有相应的密钥也无法解密数据内容。
  2. 保持HTTP的应用逻辑不变:在HTTPS的工作模式中,原有的HTTP请求和响应的处理逻辑并没有改变。只是在数据传输到TCP协议之前,先由SSL/TLS进行加密,确保了传输过程的安全性。这种分层的方法既保障了旧的HTTP应用无需修改即可升级到HTTPS,也符合了TCP/IP协议族的分层哲学。
  3. 使用安全的握手建立连接:当客户端与服务器进行HTTPS通信时,会首先建立一个SSL/TLS的握手,以确保双方的身份验证,并且确立加密参数和密钥。这个过程是整个加密通信的基础,并确保了后续所有传输的数据都能得到加密保护。
  4. 对外部资源的处理:现代网页通常包含多个外部资源,如图片、CSS和JavaScript文件等。在使用HTTPS时,即使是这些外部资源,浏览器也会分别建立安全的连接来获取它们,从而确保整个页面加载过程的数据安全。

所以,HTTPS的核心在于它在HTTP上增加了一层SSL/TLS协议,以实现数据的加密传输。这种机制不仅提高了数据传输的安全性,而且在很大程度上继承了原有HTTP协议的应用模型,使得从HTTP迁移到HTTPS的过程对于开发者和用户来说相对平滑。

网络协议深度解析:SSL、 TLS、HTTP和 DNS(C/C++代码实现)

#define PKT_PARSE_OK 0
#define PKT_PARSE_ERR -1#define PKT_IPPROTO_ICMP                1       //ICMP
#define PKT_IPPROTO_TCP			  	    6		//TCP
#define PKT_IPPROTO_UDP					17		//UDP#pragma pack(1)typedef struct  {uint8_t		dest[6];uint8_t		src[6];uint16_t	type;
}PKT_ETH_HEADER_S;///* IPv4 header */
typedef struct 
{uint8_t	ver_ihl;			/*Version (4 bits) + Internet header length (4 bits)*/uint8_t	tos;				/*Type of service*/uint16_t  tlen;				/*Total length*/ uint16_t  identification;	/*Identification*/uint16_t  flags_fo;			/*Flags (3 bits) + Fragment offset (13 bits)*/uint8_t	ttl;				/*Time to live*/uint8_t	proto;				/*Protocol*/uint16_t  crc;				/*Header checksum*/uint32_t	saddr;			/*Source address*/uint32_t	daddr;			/*Destination address*///uint32_t	op_pad;			/*Option + Padding*/
} PKT_IP_HEADER_S;
//
///* UDP header*/
typedef struct 
{uint16_t sport;				/*Source port*/uint16_t dport;				/*Destination port*/uint16_t len;					/*Datagram length*/uint16_t crc;					/*Checksum*/
} PKT_UDP_HEADER_S;
//
///* TCP header*/
typedef struct 
{uint16_t 	sport;				/*Source port*/uint16_t 	dport;				/*Destination port*/uint32_t	seq;				/*Sn number*/uint32_t	ack;				/*Ack number*/uint16_t	flags;uint16_t	win;				/*Window size*/uint16_t	sum;				/*Checksum*/uint16_t	urp;				/*Urgent pointer*/
} PKT_TCP_HEADER_S;typedef struct 
{uint32_t          linkid;uint32_t          seq; //sequint32_t		  ack;//ackuint32_t		  exp;//expire next sequint16_t	      flags;uint16_t	      win;//Window sizeuint16_t	      crc;//Checksumuint16_t	      urp;//Urgent pointer
}PKT_TCP_S;typedef struct 
{uint32_t      linkid;uint16_t      crc;	//Checksumuint32_t		len;    //udp len with headuint8_t*      data;
}PKT_UDP_S;typedef union  
{PKT_TCP_S tcp;PKT_UDP_S udp;
}PKT_TRANS_LAYER_U;typedef struct 
{uint32_t sip;       //源ipuint32_t dip;       //目的ipuint16_t sport;     //源portuint16_t dport;     //目的portuint8_t  proto;       //传输层协议PKT_IPPROTO_TCP,PKT_IPPROTO_UDPuint8_t *peth_pkt;      // 报文指针uint8_t *pnet_pkt;      //网络层指针,网络层起始uint8_t *ptrans_pkt;    //传输层指针,传输层起始uint8_t *papp_pkt;      //数据层指针,数据层起始uint8_t *pmpls_pkt;uint8_t *l2;uint8_t *l3;uint8_t *l4;uint16_t pkt_len;      //整个报文长度uint16_t ethh_len;      //2-2.5层头长度uint16_t net_len;      //ip数据长度,网络层+传输层+应用层uint16_t trans_len;    //传输层数据长度,传输层+应用层uint16_t app_len;      //应用层数据长度uint16_t mpls_len;      //应用层数据长度uint16_t eth_pack_num;   //2.5层封装层数uint8_t  vlan_flag;uint8_t  ipfrag_flag;  //ip分片标记,1分片uint8_t  mpls_flag;  uint8_t  ipv4_flag;uint8_t  ipv6_flag;uint8_t  icmp_flag;uint16_t app_proto; //应用层协议类型PKT_TRANS_LAYER_U trans_info; //tcp,udp信息 
}PKT_INFO_S;
#pragma pack()#define IP_SIP(p)		ntohl(((PKT_IP_HEADER_S *)(p))->saddr)			/*源IP*/
#define IP_DIP(p)		ntohl(((PKT_IP_HEADER_S *)(p))->daddr)			/*目的IP*/
#define IP_HLEN(p)		((((PKT_IP_HEADER_S *)(p))->ver_ihl & 0x0F) << 2)	/*IP头长度*/
#define IP_PLEN(p)		ntohs(((PKT_IP_HEADER_S *)(p))->tlen)				/*IP包长度*/
#define IP_IDEN(p)      ntohs(((PKT_IP_HEADER_S *)(p))->identification)   /*IP包标识*/
#define IP_OFF(p)		((ntohs(((PKT_IP_HEADER_S *)(p))->flags_fo)&0x1FFF)<<3) 
#define IP_MF(p)		((((PKT_IP_HEADER_S *)(p))->flags_fo&0x20)>>5)		
#define UDP_SPORT(p)    ntohs(((PKT_UDP_HEADER_S *)(p))->sport)			/*Upd包源端口*/
#define UDP_DPORT(p)	ntohs(((PKT_UDP_HEADER_S *)(p))->dport)			/*Udp包目的端口*/
#define UDP_PLEN(p)		ntohs(((PKT_UDP_HEADER_S *)(p))->len)				/*Udp包的长度*/
#define UDP_HLEN        (uint16_t)8
#define TCP_SN(p)		ntohl(((PKT_TCP_HEADER_S*)(p))->seq)
#define TCP_ACK(p)		ntohl(((PKT_TCP_HEADER_S*)(p))->ack)
#define TCP_HLEN(p)		((((PKT_TCP_HEADER_S*)(p))->flags&0x00F0)>>2)
#define TCP_SPORT(p)	ntohs(((PKT_TCP_HEADER_S *)(p))->sport)			/*Tcp包源端口*/
#define TCP_DPORT(p)    ntohs(((PKT_TCP_HEADER_S *)(p))->dport)			/*Tcp包目的端口*/
#define TCP_WIN(p)      ntohs(((PKT_TCP_HEADER_S *)(p))->win)
#define TCP_SYN(p)		(((PKT_TCP_HEADER_S*)(p))->flags&0x0200)
#define TCP_FIN(p)		(((PKT_TCP_HEADER_S*)(p))->flags&0x0100)
#define TCP_RST(p)		(((PKT_TCP_HEADER_S*)(p))->flags&0x0400)
#define TCP_ACKF(p)      (((PKT_TCP_HEADER_S*)(p))->flags&0x1000)struct http_header {char *name, *value;
};struct http_message {char type;char *method, *url;char *protocol;char *status, *line;#define HTTP_HEADER_MAX 16struct http_header headers[HTTP_HEADER_MAX];char *body;char connection_close;char connection_keepalive;char gzip_supported;
};typedef enum {DNS_UDP = 0,DNS_TCP,
}DNS_TRANS_TYPE;typedef struct {DNS_TRANS_TYPE trans_type;int requestID;//请求标识int requestFlag;//请求标志char*  dns_queries_data;unsigned short dns_queries_len;char  DNSname[DNS_DATA_LENGTH+1];//DNS名称unsigned short requestType;//查询类型	unsigned short  requestClass;//查询类
}DNS_REQUEST_INFO;    ...void CallBackPacket(char *data, int len)
{
...if (pkt_info.proto == PKT_IPPROTO_TCP && pkt_info.dport == 443){//printf("-------------Https Packet Start------------- \n");char servername[128] = {0};int namelen = 0;err = ssl_clienthello_parser_servername(pkt_info.papp_pkt, pkt_info.app_len, servername, &namelen);if (err != 0){//printf("    Not Find ServerName.\n");}else{if (namelen >= 128)return;servername[namelen] = 0;printf("[HTTPS] Find ServerName: %s, Sip:%d.%d.%d.%d Dip:%d.%d.%d.%d Sport:%d Dport:%d \n", servername,pkt_info.sip >> 24 & 0xff, pkt_info.sip >> 16 & 0xff, pkt_info.sip >> 8 & 0xff, pkt_info.sip & 0xff,pkt_info.dip>> 24 & 0xff, pkt_info.dip >> 16 & 0xff, pkt_info.dip >> 8 & 0xff, pkt_info.dip & 0xff,pkt_info.sport, pkt_info.dport);}//printf("-------------Https Packet E n d------------- \n");}else if (pkt_info.proto == PKT_IPPROTO_UDP && pkt_info.dport == 53){DNS_REQUEST_INFO dns_req = {0};err = dns_parse(pkt_info.papp_pkt, pkt_info.app_len, &dns_req);if (err == 0){printf("[DNS] DNS ServerName: %s, Sip:%d.%d.%d.%d Dip:%d.%d.%d.%d Sport:%d Dport:%d \n", dns_req.DNSname,pkt_info.sip >> 24 & 0xff, pkt_info.sip >> 16 & 0xff, pkt_info.sip >> 8 & 0xff, pkt_info.sip & 0xff,pkt_info.dip>> 24 & 0xff, pkt_info.dip >> 16 & 0xff, pkt_info.dip >> 8 & 0xff, pkt_info.dip & 0xff,pkt_info.sport, pkt_info.dport);}}else if (pkt_info.proto == PKT_IPPROTO_TCP && pkt_info.dport == 80){struct http_message http_msg = {0};err = http_parser(pkt_info.papp_pkt, &http_msg, HTTP_REQUEST);if (err == 0){int i = 0;char host[256] = {0};for (; i < 16; i++){if (http_msg.headers[i].name){if (strncmp(http_msg.headers[i].name, "Host", 4) == 0){strcpy(host, http_msg.headers[i].value);int len = strlen(host);if (host[len-1] == '\r')host[len-1] = 0;break;}}}printf("[HTTP] Method:%s Host:%s URL: %s, Sip:%d.%d.%d.%d Dip:%d.%d.%d.%d Sport:%d Dport:%d \n", http_msg.method,host,http_msg.url,pkt_info.sip >> 24 & 0xff, pkt_info.sip >> 16 & 0xff, pkt_info.sip >> 8 & 0xff, pkt_info.sip & 0xff,pkt_info.dip>> 24 & 0xff, pkt_info.dip >> 16 & 0xff, pkt_info.dip >> 8 & 0xff, pkt_info.dip & 0xff,pkt_info.sport, pkt_info.dport);            }}
...return;
}
...
int main(int argc, char *argv[])
{
...fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));if(fd<0){printf("socket create err %d. \n", fd);goto failed_2;}//PACKET_VERSION和 SO_BINDTODEVICE可以省略
#if 1const int tpacket_version = TPACKET_V1;/* set tpacket hdr version. */ret = setsockopt(fd, SOL_PACKET, PACKET_VERSION, &tpacket_version, sizeof (int));if(ret<0){printf("setsockopt packet version err %d. \n", ret);goto failed_2;}/* bind to device. */ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intfName, strlen(intfName));if(ret<0){printf("setsockopt bind device error %d . \n", ret);goto failed_2;}
#endifstruct tpacket_req req;
#define PER_PACKET_SIZE 2048const int BUFFER_SIZE = 1024*1024*16; //16MB的缓冲区req.tp_block_size = 4096;req.tp_block_nr = BUFFER_SIZE/req.tp_block_size;req.tp_frame_size = PER_PACKET_SIZE;req.tp_frame_nr = BUFFER_SIZE/req.tp_frame_size;ret = setsockopt(fd, SOL_PACKET, PACKET_RX_RING, (void *)&req, sizeof(req));if(ret<0){printf("setsockopt rx ring error %d . \n", ret);goto failed_2;}buff = (char *)mmap(0, BUFFER_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);if(buff == MAP_FAILED){printf("mmap err. \n");goto failed_2;}int nIndex=0, i=0;while(1){//这里在poll前先检查是否已经有报文被捕获了struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE);//如果frame的状态已经为TP_STATUS_USER了,说明已经在poll前已经有一个数据包被捕获了,如果poll后不再有数据包被捕获,那么这个报文不会被处理,这就是所谓的竞争情况。if(pHead->tp_status == TP_STATUS_USER)goto process_packet;//poll检测报文捕获struct pollfd pfd;pfd.fd = fd;//pfd.events = POLLIN|POLLRDNORM|POLLERR;pfd.events = POLLIN;pfd.revents = 0;ret = poll(&pfd, 1, -1);if(ret<0){perror("poll");goto failed_1;}process_packet://尽力的去处理环形缓冲区中的数据frame,直到没有数据frame了for(i=0; i<req.tp_frame_nr; i++){struct tpacket_hdr* pHead = (struct tpacket_hdr*)(buff+ nIndex*PER_PACKET_SIZE);//XXX: 由于frame都在一个环形缓冲区中,因此如果下一个frame中没有数据了,后面的frame也就没有frame了if(pHead->tp_status == TP_STATUS_KERNEL)break;//处理数据frameCallBackPacket((char*)pHead+pHead->tp_mac, pHead->tp_len);//char* mac = (char*)pHead+pHead->tp_mac;//printf("GetPacket,Len:%d,Mac:%2x-%2x-%2x-%2x-%2x-%2x\n", pHead->tp_len,//    mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);//重新设置frame的状态为TP_STATUS_KERNELpHead->tp_len = 0;pHead->tp_status = TP_STATUS_KERNEL;//更新环形缓冲区的索引,指向下一个framenIndex++;nIndex%=req.tp_frame_nr;}}
...
}

Makefile

CC = gcc
CFLAGS = -Wall -g
LIBS = -lpthreadall: mainmain: main.o dns_parser.o http_parser.o packet_handle.o pkt_parse.o ssl_parser.o$(CC) $(CFLAGS) -o main main.o dns_parser.o http_parser.o packet_handle.o pkt_parse.o ssl_parser.o $(LIBS)main.o: main.c$(CC) $(CFLAGS) -c main.cdns_parser.o: dns_parser.c$(CC) $(CFLAGS) -c dns_parser.chttp_parser.o: http_parser.c$(CC) $(CFLAGS) -c http_parser.cpacket_handle.o: packet_handle.c$(CC) $(CFLAGS) -c packet_handle.cpkt_parse.o: pkt_parse.c$(CC) $(CFLAGS) -c pkt_parse.cssl_parser.o: ssl_parser.c$(CC) $(CFLAGS) -c ssl_parser.cclean:rm -f *.o main

运行结果:
If you need the complete source code, please add the WeChat number (c17865354792)

使用套接字接收网络数据包的示例。它创建了原始套接字,并将其绑定到指定的网络接口上。然后,它使用mmap()函数将一个环形缓冲区映射到内存中,以便存储捕获的数据包。接下来,它使用poll()函数检测是否有新的数据包到达,如果有,则处理这些数据包。在处理数据包时,它会调用CallBackPacket()函数处理数据包

总结

SSL(安全套接层)和TLS(传输层安全)是用于在互联网通信中提供安全和数据完整性的协议。它们位于传输层之上,确保数据在传输过程中被加密,以防止未经授权的访问和篡改。

HTTP(超文本传输协议)是一种应用层协议,用于在互联网上传输网页和其他资源。它定义了客户端和服务器之间的通信规则,确保网页内容能够正确地传输和显示。

DNS(域名系统)是一种将人类可读的域名转换为机器可读的IP地址的系统。它位于应用层和传输层之间,当用户在浏览器中输入一个域名时,DNS会将其解析为对应的IP地址,以便计算机能够找到并连接到目标服务器。

We also undertake the development of program requirements here. If necessary, please follow the WeChat official account 【程序猿编码】and contact me

参考:RFC 1035、RFC 2246、RFC 7235

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

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

相关文章

【Java--数据结构】链表经典OJ题详解(上)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 谈谈头插、头删、尾插、头插的时间复杂度 反转一个单链表 链表的中间结点 返回倒数第k个结点 合并两个链表 谈谈头插、头删、尾插、头插的时间复杂度 头插和头删的时…

使用react-vant上传图片遇到的问题

使用react-vant Uploader上传图片后出现的问题。 先试用upload上传图片。 <Form.Itemrules{[{ required: true, message: 请上传头像 }]}label上传头像namefiles><Uploader accept* maxCount"1" onChange{imgFile} /></Form.Item> 图片上传成功后…

Linux网络-DNS域名解析服务

目录 一.DNS相关介绍 1.DNS是什么 2.DNS系统的分布式数据结构 根域 顶级域 二级域 子域 主机 3.服务器类型 主域名服务器 从域名服务器 缓存域名服务器 转发域名服务器 二.DNS域名解析 1.DNS域名解析方式及功能 2.DNS域名解析查询方式 2.1.递归查询&#xff0…

OpenWRT设置自动获取IP,作为二级路由器

前言 上一期咱们讲了在OpenWRT设置PPPoE拨号的教程&#xff0c;在光猫桥接的模式下&#xff0c;OpenWRT如果不设置PPPoE拨号&#xff0c;就无法正常上网。 OpenWRT设置PPPoE拨号教程 但现在很多新装的宽带&#xff0c;宽带师傅为了方便都会把光猫设置为路由模式。如果你再外…

微软在汉诺威工业博览会上推出新制造业Copilot人工智能功能,强化Dynamics 365工具集

在近日于德国汉诺威举行的盛大工业博览会上&#xff0c;微软向全球展示了其最新推出的制造业人工智能功能&#xff0c;这些功能以Dynamics 365工具集为核心&#xff0c;旨在通过先进的AI技术为制造业带来前所未有的变革。 此次推出的新功能中&#xff0c;最为亮眼的是支持AI的…

数据可视化(四):Pandas技术的高级操作案例,豆瓣电影数据也能轻松分析!

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

Blueprints - 鼠标光标判断相关节点

一些以前的学习笔记归档&#xff1b; 俯视角场景中要用鼠标光标判断是否点中物体&#xff0c;或依靠光标引发各种事件&#xff1b; 这些逻辑一般编写在Controller中&#xff0c;Controller类本身就带有相关判断节点&#xff1a; 其中Get Hit Result Under Cursor by Channel是…

如何批量跟踪京东物流信息

随着电商行业的快速发展&#xff0c;快递业务日益繁忙&#xff0c;无论是商家还是消费者&#xff0c;都需要一种高效、便捷的快递查询工具。快递批量查询高手软件应运而生&#xff0c;以其强大的功能和便捷的操作体验&#xff0c;赢得了广大电商、微商精英们的青睐。 快递批量…

机器人视觉教学实训平台

一&#xff1a;功能概述 1.1、功能简介 机器人视觉教学实训平台基于睿尔曼机器人与海康机器视觉产品&#xff0c;面向机器人视觉系统应用而开发设计&#xff0c;产品涵盖机器人系统、工业视觉系统、自动化控制系统、计算机编程系统&#xff0c;可以在一台设备上进行多种与机器…

冷热不均?试试智慧供热二网平衡解决方案吧!

一、系统背景&#xff1a; 在城市供热系统中&#xff0c;目前普遍存在热力平衡调节困难、过量供热及供热不足并存、系统灵活性不足、管理粗放、智能化水平不高、无法根据实际天气变化及具体需求灵活调节等问题。供水管和回水管之间的温差过大&#xff0c;导致热能在循环过程中…

Confluence 快捷键大揭秘:提高效率的小窍门

使用 Confluence 快捷键的好处有&#xff1a; 1.提高工作效率&#xff1b; 2.更流畅地进行编辑、导航和管理操作&#xff1b; 3.减少误操作&#xff1b; 4.展现专业水平。 更多精彩内容&#xff1a; 成为 Jira 大师&#xff1a;效率达人的必备秘诀 Jira Cloud 项目管理专栏 PMO…

Linux:进程创建 进程终止

Linux&#xff1a;进程创建 & 进程终止 进程创建fork写时拷贝 进程终止退出码strerrorerrno 异常信号exit 进程创建 fork fork函数可以用于在程序内部创建子进程&#xff0c;其包含在头文件<unistd.h>中&#xff0c;直接调用fork()就可以创建子进程了。 示例代码&…

个人博客系统的设计与实现

https://download.csdn.net/download/liuhaikang/89222885http://点击下载源码和论文 本 科 毕 业 设 计&#xff08;论文&#xff09; 题 目&#xff1a;个人博客系统的设计与实现 专题题目&#xff1a; 本 科 毕 业 设 计&#xff08;论文&#xff09;任 务 书 题 …

算法-动态规划专题

文章目录 前言 : 动态规划简述1 . 斐波那契模型1.1 泰波那契数列1.2 最小花费爬楼梯1.3 解码方法 前言 : 动态规划简述 动态规划在当前我们的理解下,其实就是一种变相的递归,我们查看一些资料也可以知道,动态规划其实属于递归的一个分支,通过把递归问题开辟的栈帧通过一定的手…

“亚马逊依赖”之下,傲基科技的品牌势能如何提升?

受益于出口政策红利、低人工成本、完善的供应链以及成熟的生产工艺优势&#xff0c;近年来我国家具出口行业迅速发展。 数据显示&#xff0c;我国家具出口规模1995年仅为11.06亿美元&#xff0c;至2023年增至641.96亿美元。随着出口规模持续扩大&#xff0c;相关企业积极走入公…

【OpenGL实践08】现代渲染管线在GLUT和Pygame和Qt.QOpenGLWidget上各自的实现代码

Qt.QOpenGLWidget进行现代渲染管线实验效果 一、说明 据说QOpenGLWidget是用来取代QGLWidget的继承者&#xff0c;我们试图将GLUT上的旧代码改成QOpenGLWidget&#xff0c;本以为差别不大&#xff0c;轻易搞定&#xff0c;经实践发现要付出极大努力才能完成。经多次实验发现G…

编译原理实验课

本人没咋学编译原理&#xff0c;能力有限&#xff0c;写的不好轻点喷&#xff0c;大佬路过的话&#xff0c;那你就路过就好 东大编译原理实验课原题&#xff0c;22年 1. 基本题&#xff1a;简单的扫描器设计 【问题描述】 熟悉并实现一个简单的扫描器&#xff0c;设计扫描器…

nosql数据库 redis

一、介绍 1、redis与mysql的区别&#xff1a; Redis是一种基于键值对的内存数据库&#xff0c;数据存储在内存中&#xff0c;因此读写速度非常快。它支持多种数据结构&#xff0c;如字符串、哈希、列表等。 MySQL是一种关系型数据库&#xff0c;数据以表格的形式组织存储在磁…

探索 Python 的动态类型系统:变量引用、不可变性及高效内存管理与垃圾回收机制的深入分析

文章目录 1. 动态类型及其内存管理解析1.1 变量与对象的引用关系1.2 对象的不可变性和内存地址的变化 2. 垃圾回收与内存优化策略2.1 动态内存分配的基础2.2 Python 的垃圾回收 Python作为一种流行的高级编程语言&#xff0c;以其代码的易读性和简洁性著称。尤其是它的动态类型…

泛私域新引擎:小程序AI智能名片S2B2C商城的分销式导购策略与案例剖析

在数字化浪潮的推动下&#xff0c;小程序AI智能名片S2B2C商城以其独特的分销式导购能力&#xff0c;逐渐在泛私域的特殊场景中占据了一席之地。这种模式不仅打破了传统线上线下的界限&#xff0c;更通过以人为核心的营销方式&#xff0c;实现了利益驱动的深度分销。 分销式导购…