13.1 Linux_网络编程_TCP/UDP

字节序

1、概述

什么是字节序:

字节序就是字节的存储顺序,分为大端字节序和小端字节序。

  • 大端字节序:低地址存高位(网络)
  • 小端字节序:低地址存低位(主机)

检验主机字节序模式:

#include <stdio.h>int main(){unsigned int a = 0x11223344;unsigned char b = *((unsigned char *)&a);if(b == 0x44){printf("小端字节序\n");}else{printf("大端字节序\n");}return 0;
}

2、字节序转换

2.1 端口号字节序转换函数

因为主机使用的是小端字节序,网络使用的是大端字节序,因此在数据传输时,需要先将数据转换为大端字节序传给网络,之后再将数据转换为小端字节序传给另一个主机。

/* h:本机 n:网络 l:32位 s:16位 *///32位数据(4字节)
uint32_t htonl(uint32_t hostlong);//本机->网络
uint32_t ntohl(uint32_t netlong);//网络->本机
//16位数据(2字节)
uint16_t htons(uint16_t hostshort);//本机->网络
uint16_t ntohs(uint16_t netshort);//网络->本机

返回值:将小端字节序转换为大端字节序后的端口号

参数:端口号值 

2.2 IP地址字节序转换函数

该函数主要是实现点分十进制表示的IP地址转换,而不需要一个字节一个字节的去转换。

2.2.1 IPv4
//点分十进制字符串->网络字节序
in_addr_t inet_addr(const char *cp);
int inet_aton(const char *cp, struct in_addr *inp);
//网络字节序->点分十进制字符串
char *inet_ntoa(struct in_addr in);

inet_addr返回值:ip地址

cp:点分十进制字符串,例如传入"192.168.1.1"这个字符串

inp:ip地址

inet_ntoa返回值:点分十进制字符串

in:ip地址

2.2.1 IPv6
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

套接字socket

概述

socket多种含义:

  • 应用编程接口API:socket API,简称socket。 
  • 函数名:socket API中有一个名为socket的函数
  • 端点:比如TCP连接是有两个端点,这两个端点是一对一通信的关系。这个端点也叫socket
  • 文件描述符:socket函数的返回值是一个socket描述符,简称socket

socket的作用:

socket处在应用层与内核之间。在应用层中实现的是与应用相关的代码,在内核中实现的是网络通信相关的代码。在OSI结构中,应用层就是OSI的应用层、表示层、会话层,内核就是运输层、网络层、数据链路层、物理层。

什么是三元组:

三元组指的是IP地址、端口号、协议。该数据通过bind函数进行绑定。

  • IP地址:标识计算机,找到与网络中的哪一个计算机进行通信。
  • 端口号:标识进程,找到与计算机中哪一个进程进行通信。
  • 协议:指定数据以什么样的方式进行传递。主要指TCP、UDP

套接字的类型:

  • 流式套接字 (SOCK_STREAM) :提供可靠的、面向连接的通信流;它使用TCP,从而保证数据传输的可靠性和顺序性
  • 数据报套接字 (SOCK_DGRAM) :定义了一种不可靠、面向无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP
  • 原始套接字(SOCK_RAW) :允许直接访问底层协议,如IP或ICMP,它功能强大但使用较为不便,主要用于协议开发。

socket文件的读写含义:

socket在Linux中也是一种文件,对socket文件进行读就是读取网络传输过来的数据,对socket文件进行写就是向网络中传输相应的数据。

相关函数 

socket相关API:

创建套接字、绑定通信结构体、监听套接字、接收套接字、发起连接

通用 

1、创建套接字
int socket(int domain, int type, int protocol);

返回值:成功返回socket文件描述符,失败返回-1

domain:指定bind中传入的地址族结构体的类型,与sa_family_t的取值要一致。

domain值含义
AF_UNIXUNIX 域套接字地址族,用于在同一台主机上的进程间通信
AF_INETIPv4 地址族,用于 IPv4 通信
AF_INET6IPv6 地址族,用于 IPv6 通信

type:套接字的类型,就是"概述"中的说的三种类型

type值使用的协议
SOCK_STREAMTCP
SOCK_DGRAMUDP
SOCK_RAW

protocol:协议。在TCP、UDP时,该值写0。type = SOCK_RAW时需要根据需求选择该值。

2、绑定通信结构体
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

返回值:成功返回0,失败返回-1 

sockfd:socket文件描述符

addr:通用地址族结构体,存放了通信地址的结构、端口号、IP地址这三个信息

addrlen:传入的addr结构体的宽度,用sizeof求出

struct sockaddr结构体:

//通用地址族结构体
struct sockaddr {sa_family_t sa_family;    //通信地址的结构char        sa_data[14];
}

 sa_family:代表通信地址的结构,取值及含义如下

sa_family值含义
AF_UNIXUNIX 域套接字地址族,用于在同一台主机上的进程间通信
AF_INETIPv4 地址族,用于 IPv4 通信
AF_INET6IPv6 地址族,用于 IPv6 通信

struct sockaddr_in结构体:

该结构体用于捆绑IPv4的地址。 

struct sockaddr_in {sa_family_t sin_family;   //通信地址的结构in_port_t sin_port;       //端口号struct in_addr sin_addr;  //IP地址(这是个结构体)
};struct in_addr {in_addr_t s_addr; //IP地址
};

struct sockaddr_in6结构体:

该结构体用于捆绑IPv6的地址。 

struct sockaddr_in6 {sa_family_t sin6_family;   /* Address family (AF_INET6) */in_port_t sin6_port;       /* Port number */uint32_t sin6_flowinfo;    /* IPv6 flow information */struct in6_addr sin6_addr; /* IPv6 address */uint32_t sin6_scope_id;    /* IPv6 scope-id */
};
struct in6_addr {uint8_t s6_addr[16]; /* IPv6 address in network byte order */
};

TCP 

1、监听套接字
int listen(int sockfd, int backlog);

返回值:成功返回0,失败返回-1  

sockfd:socket文件描述符

backlog:允许接入的客户端的总数量

该函数应用于服务器, 调用完该函数后,会创建一个指定长度的队列,用于存储接收到的客户端。

2、接收套接字
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

返回值:成功新的socket文件描述符,用于与客户端进行连接,失败返回-1

              后面数据交互用的是该文件描述符,而不是socket函数返回的文件描述符。

sockfd:socket文件描述符

addr:通用地址族结构体,该参数是传出的参数,是接入服务器的客户端的信息。

addrlen:addr结构体的宽度,该参数是传出的参数

该函数应用于客户端,是从listen创建的队列中出队一个客户端,并生成一个新的socket与这个客户端进行连接。

注意:一个accept只能接入一个客户端,如果想要接入多个客户端,那么应该把accept写入到while循环中

3、发起连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

返回值:成功返回0,失败返回-1   

sockfd:socket文件描述符

addr:通用地址族结构体,该参数是要连接的服务器的信息

addrlen:addr结构体的宽度,sizeof求出

4、获取socket信息
//获取自己的socket信息
int getsockname(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);
//获取对端的socket信息
int getpeername(int socket, struct sockaddr *restrict address,socklen_t *restrict address_len);

返回值:成功返回0,失败返回-1 

socket:写入accept返回的socket文件描述符

其余参数与accept的参数一致

UDP

1、接收/发送数据
//发送
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
//接收
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

sockfd:socket文件描述符

buf:缓冲区

len:数据长度

flag:标志位,一般设置为0。具体含义如下

flag值含义
0

send相当于write

recv相当于read(读后数据从缓冲区删除)

MSG_PEEK

窥视传入的数据。

数据被复制到缓冲区,但不会从输入队列中删除

MSG_OOB处理带外数据
//发送
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
//接收
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

第一行参数:sockfd、buf、len、flags与send、recv函数的参数含义一致

dest_addr:发送给哪一个设备,包含设备的IP、端口号

src_addr:接收到的是哪一个设备的信息,包含设备的IP、端口号

addrlen:长度,用sizeof 求出

TCP

测试

1、使用ifconfig获取环回IP地址

2、使用 nc <IP地址> <端口号>使得终端作为客户端连接服务器(这里服务器端口号为8888)

单线程

TCP客户端与服务端实现步骤:

server.c代码: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc ,char** argv){int fd;struct sockaddr_in addr;//判断参数有效性if(argc != 3){printf("param err\n");printf("%s<ip><port>\n",argv[0]);return -1;}printf("ip = %s\n",argv[1]);printf("port = %s\n",argv[2]);//1.创建socketif((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议perror("socket");return -1;}//2.绑定IP、端口号addr.sin_family = AF_INET;    				  //IPv4addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){perror("bind");return -1;}//3.监听socketif(listen(fd,5) == -1){ 	//允许最多接入5个客户端perror("listen");return -1;}//4.接受客户端链接int newFd;struct sockaddr_in newAddr;socklen_t newAddrlen;if((newFd = accept(fd,(struct sockaddr*)&newAddr,&newAddrlen)) < 0){perror("accept");return -1;}//5.数据交互printf("client port = %d\n",ntohs(newAddr.sin_port));printf("client ip = %s\n",inet_ntoa(newAddr.sin_addr));while(1){write(newFd,"server",strlen("server\n"));sleep(1);}close(newFd);close(fd);return 0;
}

 client.c代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc,char** argv){int fd;struct sockaddr_in addr;//判断参数有效性if(argc != 3){printf("param err\n");printf("%s<ip><port>\n",argv[0]);return -1;}printf("ip = %s\n",argv[1]);printf("port = %s\n",argv[2]);//1.创建socketif((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议perror("socket");return -1;}//2.链接服务器addr.sin_family = AF_INET;    		  		//IPv4addr.sin_port = htons(atoi(argv[2])); 		//服务器端口号,要转化为大端子节序addr.sin_addr.s_addr = inet_addr(argv[1]);  //服务器IP地址:在本网络上的本主机,即:自己if(connect(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){perror("connect");return -1;}//3.数据交互char buf[100] = {0};while(1){if(read(fd,buf,sizeof(buf)-1) > 0){printf("read:%s\n",buf);write(fd,"client:i read it\n",strlen("client:i read it\n"));}}close(fd);return 0;
}

代码执行结果:

并发

1、地址快速重用

当服务器与客户端正在处于连接状态,若服务器先于客户端关闭,之前服务器使用的端口号并不会立刻关闭。为了解决这个问题需要将以下代码添加到"建立套接字"代码之后。

int flag=1,len=sizeof(int);
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,len) == -1){perror("setsockopt");return -1;
}

2、多进程并发

多进程并发设计步骤:

  • 在 "单线程" 的代码基础上,将accept函数和数据交互部分写入一个新的while循环,这代表服务器可以不断接收客户端。
  • 在accept后创建子进程,子进程需要关闭socket返回的文件描述符,父进程需要关闭accept返回的文件描述符。之后子进程用来与接入的客户端进行通信,父进程继续等待新的客户端接入。
  • 在accept前编写SIGCHLD信号处理相关函数,以便多进程时使用信号机制来回收子进程。

客户端退出情况分析:

  • 正常交互:服务器收到客户端发来的退出指令退出交互的while,之后使用exit退出子进程
  • 异常退出:客户端突然终止,服务器向客户端发送数据后会产生SIGPIPE信号,从而由信号终止子进程 

server.c改进代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>void Set_SIGCHLD(void);
void SIGCHLD_Handler(int sig);
int main(int argc ,char** argv){int fd;struct sockaddr_in addr;//判断参数有效性if(argc != 3){printf("param err\n");printf("%s<ip><port>\n",argv[0]);return -1;}printf("ip = %s\n",argv[1]);printf("port = %s\n",argv[2]);//1.创建socketif((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议perror("socket");return -1;}//地址快速重用int flag=1,len=sizeof(int);if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,len) == -1){perror("setsockopt");return -1;}//2.绑定IP、端口号addr.sin_family = AF_INET;    				  //IPv4addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){perror("bind");return -1;}//3.监听socketif(listen(fd,5) == -1){ 	//允许最多接入5个客户端perror("listen");return -1;}//多进程并发pid_t pid;int newFd;struct sockaddr_in newAddr;socklen_t newAddrlen;Set_SIGCHLD();//以信号方式回收子进程while(1){//4.接受客户端链接if((newFd = accept(fd,(struct sockaddr*)&newAddr,&newAddrlen)) < 0){perror("accept");return -1;}//父进程处理接收客户端链接的问题//子进程处理与客户端交互的问题if((pid=fork()) == -1){perror("fork");return -1;}else if(pid == 0){close(fd);//对于子进程,socket返回的fd没有用//5.数据交互printf("client port = %d\n",ntohs(newAddr.sin_port));printf("client ip = %s\n",inet_ntoa(newAddr.sin_addr));while(1){write(newFd,"server",strlen("server\n"));sleep(1);}exit(0);}else{close(newFd);//对于父进程,accept返回的newFd没有用}}close(fd);return 0;
}
void Set_SIGCHLD(void){struct sigaction act;act.sa_handler = SIGCHLD_Handler;sigemptyset(&act.sa_mask);act.sa_flags = SA_RESTART;//让因为信号而终止的系统调用继续运行if(sigaction(SIGCHLD,&act,NULL) != 0){perror("sigaction");}
}
void SIGCHLD_Handler(int sig){int wstatus;waitpid(-1,&wstatus,WNOHANG);if(WIFEXITED(wstatus)){      //判断子进程是否正常退出printf("子进程的返回值为%d\n",WEXITSTATUS(wstatus));}else{printf("子进程是否被信号结束%d\n",WIFSIGNALED(wstatus));printf("结束子进程的信号类型%d\n",WTERMSIG(wstatus));}
}

代码运行结果如下:

3、多线程并发

多进程并发设计步骤: 

  • 在 "单线程" 的代码基础上,将accept函数和数据交互部分写入一个新的while循环,这代表服务器可以不断接收客户端。
  • 在accept后创建新的线程并进行线程分离,让新的线程去与客户端进行数据交互,原线程依旧等待新的客户端接入

存在问题:

未解决ctrl c终止客户端后,整个服务器因管道破裂信号而整个进程退出的情况

server.c改进代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>void* clientThread(void* arg);
int main(int argc ,char** argv){int fd;struct sockaddr_in addr;//判断参数有效性if(argc != 3){printf("param err\n");printf("%s<ip><port>\n",argv[0]);return -1;}printf("ip = %s\n",argv[1]);printf("port = %s\n",argv[2]);//1.创建socketif((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议perror("socket");return -1;}//2.绑定IP、端口号addr.sin_family = AF_INET;    				  //IPv4addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){perror("bind");return -1;}//3.监听socketif(listen(fd,5) == -1){ 	//允许最多接入5个客户端perror("listen");return -1;}//多线程并发int newFd;struct sockaddr_in newAddr;socklen_t newAddrlen;pthread_t tid;while(1){//4.接受客户端链接if((newFd = accept(fd,(struct sockaddr*)&newAddr,&newAddrlen)) < 0){perror("accept");return -1;}printf("client port = %d\n",ntohs(newAddr.sin_port));printf("client ip = %s\n",inet_ntoa(newAddr.sin_addr));//创建线程if(pthread_create(&tid,NULL,(void*)clientThread,(void*)newFd) != 0){perror("pthread_create");return -1;}pthread_detach(tid);}close(fd);return 0;
}
void* clientThread(void* arg){int newFd = (int)arg;//5.数据交互while(1){write(newFd,"server",strlen("server\n"));sleep(1);}printf("client exit\n");close(newFd);return NULL;
}

UDP

测试

1、使用ifconfig获取环回IP地址

2、使用 nc -u <IP地址> <端口号>使得终端作为客户端连接服务器(这里服务器端口号为8888)

单线程

UDP客户端与服务端实现步骤:

server.c代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc,char** argv){int fd;struct sockaddr_in addr;//判断参数有效性if(argc != 3){printf("param err\n");printf("%s<ip><port>\n",argv[0]);return -1;}printf("ip = %s\n",argv[1]);printf("port = %s\n",argv[2]);//1.创建socketif((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//IPv4,UDP协议perror("socket");return -1;}//2.绑定IP、端口号addr.sin_family = AF_INET;    				  //IPv4addr.sin_port = htons(atoi(argv[2])); 		  //端口号,要转化为大端子节序addr.sin_addr.s_addr = inet_addr(argv[1]);    //IP地址:0表示在本网络上的本主机,即:自己if(bind(fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)) == -1){perror("bind");return -1;}//3.数据交互char buf[100] = {0};struct sockaddr_in src_addr;socklen_t src_addrlen;while(1){memset(buf,0,sizeof(buf));if(recvfrom(fd,buf,sizeof(buf)-1,0,(struct sockaddr*)&src_addr,&src_addrlen) > 0){printf("client port = %d\n",ntohs(src_addr.sin_port));printf("client ip = %s\n",inet_ntoa(src_addr.sin_addr));printf("read:%s\n",buf);}	}close(fd);return 0;
}

client.c代码: 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main(int argc,char** argv){int fd;struct sockaddr_in addr;//判断参数有效性if(argc != 3){printf("param err\n");printf("%s<ip><port>\n",argv[0]);return -1;}printf("ip = %s\n",argv[1]);printf("port = %s\n",argv[2]);//1.创建socketif((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//IPv4,UDP协议perror("socket");return -1;}//2.设置要发送到的服务器信息addr.sin_family = AF_INET;    		  		//IPv4addr.sin_port = htons(atoi(argv[2])); 		//服务器端口号,要转化为大端子节序addr.sin_addr.s_addr = inet_addr(argv[1]);  //服务器IP地址:在本网络上的本主机,即:自己//3.数据交互while(1){sendto(fd,"cilent",strlen("cilent"),0,(struct sockaddr*)&addr,sizeof(addr));sleep(1);}close(fd);return 0;
}

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

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

相关文章

【Linux】-权限

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;深入代码世界&#xff0c;了解掌握 Linux 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 ​ 一、权限的概念 在Linux 中&…

开源图像超分ECBSR项目源码分析

相关介绍 项目GitHub地址&#xff1a;https://github.com/xindongzhang/ECBSR项目相关论文&#xff1a;https://www4.comp.polyu.edu.hk/~cslzhang/paper/MM21_ECBSR.pdf&#xff08;也可以点这里下载&#xff09;论文解读&#xff1a;Edge-oriented Convolution Block for Re…

秃姐学AI系列之:语义分割 + 数据集 | 转置卷积 + 代码

语义分割 语义分割将图片中的每个像素分类到对应的类别 通常来说现在的会议软件的背景虚化这个功能用的就是语义分割技术 无人车进行路面识别也是语义分割技术 语义分割 vs 实例分割 语义分割将图像划分为若干组成区域&#xff0c;这类问题的方法通常利用图像中像素之间的相关…

基于Multisim三极管B放大系数放大倍数测量电路设计(含仿真和报告)

【全套资料.zip】三极管B放大系数放大倍数测量电路电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.用三个数码管显示B的大小&#xff0c;分别显示个位、十位和百位。 2.显示范围…

【论文精炼分享】GPU Memory Exploitation for Fun and Profit 24‘USENIX

今天分享的论文《GPU Memory Exploitation for Fun and Profit》来自2024年USENIX Security。在本文中&#xff0c;作者团队对 CUDA 程序中的缓冲区溢出问题进行了全面的研究&#xff1a; &#xff08;1&#xff09;对用于访问各种 GPU 内存空间的机制进行了逆向工程&#xff…

纯css 轮播图片,鼠标移入暂停 移除继续

核心 滚动&#xff1a; animation: 动画名称 20s linear infinite normal;停止&#xff1a; animation: 动画名称 20s linear infinite paused; 完整例子&#xff1a; html: <div class"carousel-wrapper"><div class"carousel"><div cl…

Docker学习笔记(2)- Docker的安装

1. Docker的基本组成 镜像&#xff08;image&#xff09;&#xff1a;Docker镜像就像是一个模板&#xff0c;可以通过这个模板来创建容器服务。通过一个镜像可以创建多个容器。最终服务运行或者项目运行就是在容器中。容器&#xff08;container&#xff09;&#xff1a;Docker…

【JavaEE初阶】网络编程TCP协议实现回显服务器以及如何处理多个客户端的响应

前言 &#x1f31f;&#x1f31f;本期讲解关于TCP/UDP协议的原理理解~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不多说…

ParallelsDesktop20最新版本虚拟机 一键切换系统 游戏娱乐两不误

让工作生活更高效&#xff1a;Parallels Desktop 20最新版本虚拟机的神奇之处 大家好&#xff01;&#x1f44b; 今天我要跟大家安利一款让我工作效率飞升的神器——Parallels Desktop 20最新版本虚拟机。作为一个日常需要在不同操作系统间来回穿梭的人&#xff0c;这款软件简直…

算法的学习笔记—(牛客JZ50)

&#x1f600;前言 在处理字符串时&#xff0c;寻找第一个只出现一次的字符是一项常见的任务。本文将探讨几种有效的解法&#xff0c;包括使用 HashMap 和位集&#xff08;BitSet&#xff09;。 &#x1f3e0;个人主页&#xff1a;尘觉主页 文章目录 &#x1f970;第一个只出现…

生命科学的前沿挑战与未来机遇

生命科学的前沿挑战与未来机遇 一、引言 21世纪被誉为生命科学的世纪&#xff0c;生命科学的迅猛发展为人类的健康、环境和社会经济带来了巨大的变革。从基因编辑技术的突破&#xff0c;到合成生物学的兴起&#xff0c;再到生物医药的快速进步&#xff0c;生命科学的前沿挑战…

Xmind一款极简思维导图和头脑风暴软件,支持PC和移动端,Xmind 2024.10.01101版本如何升级到Pro版?简单操作,最新可用!

文章目录 Xmind下载安装Xmind免费升级到Pro Xmind 是一款全功能的思维导图和头脑风暴软件&#xff0c;不限制节点和文件数&#xff0c;创新无限&#xff0c;界面纯净简洁无广告&#xff0c;支持PC和移动端&#xff0c;思维导图和大纲视图自由切换&#xff0c;可本地化文档存储&…

虚拟机数据恢复—通过拼接数据库页碎片的方式恢复数据库的数据恢复案例

虚拟机数据恢复环境&#xff1a; 某品牌服务器通过同品牌某型号的RAID卡&#xff0c;将4块STAT硬盘为一组RAID10阵列。上层部署XenServer虚拟化平台&#xff0c;虚拟机安装Windows Server系统&#xff0c;每台虚拟机有两个虚拟机磁盘&#xff08;系统盘 数据盘&#xff09;&am…

jmeter 从多个固定字符串中随机取一个值的方法

1、先新增用户参数&#xff0c;将固定值设置为不同的变量 2、使用下面的函数&#xff0c;调用这写变量 ${__RandomFromMultipleVars(noticeType1|noticeType2|noticeType3|noticeType4|noticeType5)} 3、每次请求就是随机取的值了

优化多表联表查询的常见方法归纳

目录 一、使用mybatis的嵌套查询 二、添加表冗余字段&#xff0c;减少联表查询需求 三、分表预处理&#xff0c;前端再匹配 一、使用mybatis的嵌套查询 【场景说明】 前端需要展示一张列表&#xff0c;其中的字段来源于多张表&#xff0c;如何进行查询优化&#xff1f; 【…

飞凌嵌入式FET527N-C核心板已适配OpenHarmony4.1

近期&#xff0c;飞凌嵌入式为FET527N-C核心板适配了OpenHarmony4.1系统——进一步提升了核心板的兼容性、稳定性和安全性。 OpenHarmony4.1在应用开发方面展现了全新的开放能力&#xff0c;以更加清晰的逻辑和场景化视角提供给开发者丰富的API接口&#xff0c;应用开发能力得…

让你的 IDEA 使用更流畅 | IDEA内存修改

随着idea使用越来越频繁&#xff0c;笔者最近发现使用过程中有时候会出现卡顿现象&#xff0c;例如&#xff0c;启动软件变慢&#xff0c;打开项目的速度变慢等&#xff1a; 因此如果各位朋友觉得最近也遇到了同样的困惑&#xff0c;不妨跟着笔者一起来设置IDEA的内存大小吧~ …

基于ECS和NAS搭建个人网盘

前言 在数字化时代&#xff0c;数据已成为我们生活中不可或缺的一部分。个人文件、照片、视频等数据的积累&#xff0c;使得我们需要一个安全、可靠且便捷的存储解决方案。传统的物理存储设备&#xff08;如硬盘、U盘&#xff09;虽然方便&#xff0c;但存在易丢失、损坏和数据…

登录前端笔记(二):vuex管理用户数据;跨域;axios封装;环境;请求响应拦截;权限;用户资料Vuex共享

一、Vuex登录流程之用户模块&#xff1a; 简言之&#xff1a;点击登录调用actions且得到token&#xff0c;把得到的token提交给mutations从而修改state里的数据。 原视频 &#xff08;1&#xff09;Vuex用户模块流程 组件页面里点击登录后&#xff0c;调用stores里的actions&…

09_实现reactive之代理 Set 和 Map

目录 创建代理建立响应式联系避免污染原始数据处理 forEachfor...ofvalues 与 keys 方法 Set 和 Map 都有特定的属性和方法来操作自身&#xff0c;因此需要单独处理。 创建代理 我们来看一段案例代码&#xff0c;体验一下和它们的独特之处&#xff0c;如下&#xff1a; const…