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.创建socket
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议
		perror("socket");
		return -1;
	}
	//2.绑定IP、端口号
	addr.sin_family = AF_INET;    				  //IPv4
	addr.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.监听socket
	if(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.创建socket
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议
		perror("socket");
		return -1;
	}
	//2.链接服务器
	addr.sin_family = AF_INET;    		  		//IPv4
	addr.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.创建socket
	if((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;    				  //IPv4
	addr.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.监听socket
	if(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.创建socket
	if((fd=socket(AF_INET,SOCK_STREAM,0))<0){//IPv4,TCP协议
		perror("socket");
		return -1;
	}
	//2.绑定IP、端口号
	addr.sin_family = AF_INET;    				  //IPv4
	addr.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.监听socket
	if(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.创建socket
	if((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//IPv4,UDP协议
		perror("socket");
		return -1;
	}
	//2.绑定IP、端口号
	addr.sin_family = AF_INET;    				  //IPv4
	addr.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.创建socket
	if((fd=socket(AF_INET,SOCK_DGRAM,0))<0){//IPv4,UDP协议
		perror("socket");
		return -1;
	}
	//2.设置要发送到的服务器信息
	addr.sin_family = AF_INET;    		  		//IPv4
	addr.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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java @RequestPart注解:同时实现文件上传与JSON对象传参

RequestPart注解&#xff1a;用于处理multipart/form-data请求的一部分&#xff0c;通常用于文件上传或者处理表单中的字段。 java后端举例&#xff1a; PostMapping("/fileTest")public AjaxResult fileTest(RequestPart("file") MultipartFile file,Req…

【时间之外】IT人求职和创业应知【10】

认知决定你的赚钱能力。以下是今天可能影响你求职和创业的热点新闻: 今日关键字:SFISF【互换便利】 1. 央行推出股票回购增持再贷款,科技股全线爆发 新闻概要: 2024年10月18日,央行等三部门联合发布《关于设立股票回购增持再贷款有关事宜的通知》,同时表示年底有进一步…

基于GeoScene Pro的开源数据治理与二维制图规范化处理智能工具箱

内容导读 本文描述的是一个基于GeoScene Pro4.0/ArcGIS3.1 Pro平台的开源数据治理与二维制图规范化处理智能工具箱(免费试用&#xff0c;文末有获取方式)&#xff0c;旨在解决GIS应用中数据转换、检查、治理和制图数据规范化处理方面的问题。 工具箱结合了Geoscene/ArcGIS Pr…

Asp.net Core SignalR 跨域设置(Furion)

前端VUE2.0/3.0 后端NET8.0/NET6.0 框架Furion 前端安装SignalR通信库&#xff0c;下面任意一条安装指令都可以&#xff0c;根据项目自行选择 npm install microsoft/signalr yarn add microsoft/signalr前端使用 <script> import { HubConnectionBuilder } from micr…

[Halcon矩阵] 通过手眼标定矩阵计算相机旋转角度

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

SpringBoot教程(三十二) | SpringBoot集成Skywalking链路跟踪

SpringBoot教程&#xff08;三十二&#xff09; | SpringBoot集成Skywalking链路跟踪 一、Skywalking是什么&#xff1f;二、Skywalking与JDK版本的对应关系三、Skywalking下载四、Skywalking 数据存储五、Skywalking 的启动六、部署探针前提&#xff1a; Agents 8.9.0 放入 项…

C#从零开始学习(用unity探索C#)(unity Lab1)

初次使用Unity 本章所有的代码都放在 https://github.com/hikinazimi/head-first-Csharp Unity的下载与安装 从 unity官网下载Unity Hub Unity的使用 安装后,注册账号,下载unity版本,然后创建3d项目 设置窗口界面布局 3D对象的创建 点击对象,然后点击Move Guzmo,就可以拖动…

云服务解决方案,针对小程序、网页、HTML5等轻量化视频解决方案

无论是社交媒体上的短视频分享&#xff0c;还是企业官网中的产品展示&#xff0c;亦或是教育平台上的互动课程&#xff0c;高质量、易制作的视频内容正以前所未有的速度改变着我们的生活方式和工作模式。然而&#xff0c;面对多样化的发布平台和日益增长的个性化需求&#xff0…

Python从0到100(六十五):Python OpenCV-图像运颜色转换及几何变换

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

.net 根据html的input type=“week“控件的值获取星期一和星期日的日期

初始化 "week" 控件值&#xff1a; //MVC部分 public ActionResult WeeklyList() {int weekNo new GregorianCalendar().GetWeekOfYear(System.DateTime.Now, System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday);string DefaultWeek DateTime.No…

ssm医院交互系统+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 摘要 I Abstract II 1绪论 1 1.1研究背景与意义 1 1.1.1研究背景 1 1.1.2研究意义 1 1.2国内外研究…

python+大数据+基于spark的短视频推荐系统【内含源码+文档+部署教程】

博主介绍&#xff1a;✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立工作室。专注于计算机相关专业毕业设计项目实战6年之久&#xff0c;选择我们就是选择放心、选择安心毕业✌ &#x1f345;由于篇幅限制&#xff0c;想要获取完整文章或者源码&#xff0c;或者代做&am…

Mybatis框架 day1018

ok了家人们书接上文&#xff0c;我们继续学习mybatis框架 五.数据输出 5.7 属性和字段的映射 5.7.1 别名映射 将数据库表的字段别名设置成和实体类属性一致。 <!-- 给每一个字段设置一个别名&#xff0c;让别名和Java实体类中 属性名一致 --> <select id"fi…

赋能特大城市水务数据安全高速运算,深圳计算科学研究院YashanDB数据库系统斩获“鼎新杯”二等奖

第三届“鼎新杯”数字化转型应用优秀案例评选结果日前正式公布&#xff0c;深圳计算科学研究院联合深圳市环境水务集团有限公司申报的《深圳环境水务国产数据库YashanDB&#xff0c;赋能特大城市水务数据安全高速运转》案例&#xff0c;经过5个多月的评审&#xff0c;从4000申报…

设计模式04-创建型模式1(简单工厂/工厂模式/抽象工厂/Java)

3.1 简单工厂模式 3.1.1 创建型模式 创建型设计模式将对象的创建过程和对象的使用过程分离&#xff0c;用户使用对象时无需关注对象的创建细节&#xff0c;外界对于这些对象只需要知道它们共同的接口&#xff0c;而不用清楚其实现细节&#xff0c;使得整个系统的设计更加符合…

Redis JSON介绍

Redis JSON介绍 Redis JSON先说说JSON是什么再说说JSON Path先推荐两个网站JSONPath JAVA clents Redis JSON 安装内存json命令语法命令url命令解释JSON.ARRAPPENDJSON.ARRINDEXJSON.ARRINSERTJSON.ARRLENJSON.ARRPOPJSON.ARRTRIMJSON.CLEARJSON.DEBUG MEMORYJSON.DEBUGJSON.DE…

单例模式(自动加载)

目录 介绍 使用 在脚本中写一个函数 让一个「自定义场景」作为单例「自动加载」 介绍 单例模式是编程中的一种设计思想&#xff0c;是为了解决某些编程语言中没有全局变量概念而产生的这对于实现某种模块非常好用 比如玩家信息&#xff0c;有时候&#xff0c;游戏中的很多…

数组中超过一半的元素

有一个数组&#xff0c;找出其中数量超过的元素是谁。比如数组 [3, 2, 3] &#xff0c;输出 3。 这个问题要解起来不难&#xff0c;暴力计数&#xff0c;转为 map&#xff0c;排序都能解决。但是他们的空间复杂度都不低&#xff0c;即便排序能做到 O(1) 的空间复杂度&#xff…

计算机系统简介

一、计算机的软硬件概念 1.硬件&#xff1a;计算机的实体&#xff0c;如主机、外设、硬盘、显卡等。 2.软件&#xff1a;由具有各类特殊功能的信息&#xff08;程序&#xff09;组成。 系统软件&#xff1a;用来管理整个计算机系统&#xff0c;如语言处理程序、操作系统、服…

电影评论网站:Spring Boot技术栈应用

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理电影评论网站的相关信息成为必然。开发合适…