UDP网络聊天室

前言

基于UDP的网络聊天室

一、项目需求

  1. 如果有用户登录,其他用户可以收到这个人的登录信息
  2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
  3. 如果有人下线,其他用户可以收到这个人的下线信息
  4. 服务器可以发送系统信息

二、步骤

1.创建UDP服务器

1.1 头文件

 #include <stdio.h>#include <string.h>#include <stdlib.h>#include <math.h>#include <sys/types.h>#include <unistd.h>#include <sys/stat.h>#include <fcntl.h>#include <pthread.h>#include <semaphore.h>#include <wait.h>#include <signal.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/ipc.h>#include <sys/sem.h>#include <semaphore.h>#include <sys/msg.h>#include <sys/shm.h>#include <sys/un.h>#include <netinet/in.h>#include <arpa/inet.h>#include <errno.h>#include <sys/select.h>#include <poll.h>#include <sqlite3.h>

1.2 定义宏 

#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录消息长度

1.3 定义链表节点  数据传递消息类型

/*定义链表节点*/
typedef struct Node{char name[login_msg_len];struct sockaddr_in addr;struct Node *next;
}Node;/*定义消息类型*/
typedef struct Msgtype{char type;char username[login_msg_len];char content[BUFSIZE];
}Msgtype;

1.4 声明函数

Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR);/*创建单向链表*/
Node *add_node(char *name,struct sockaddr_in addr);/*向链表中添加节点*/
int print_msg(Msgtype *msg);/*打印消息类型*/
void broadcast(int sfd,Node *head,Msgtype *msg);/*广播消息*/
int is_exist(Node *head,Msgtype *msg);/*查找用户名是否存在*/
int is_empty(Node *head);/*链表判空*/
void insert_node(Node *head,char *name,struct sockaddr_in addr);/*头插法插入节点*/
void delete_node(Node *head,char *name);/*按用户名删除节点*/
void broadcast_system(int sfd,Node *head,char *input_buf);/*向所有客户端发送系统消息*/

1.5  创建单向链表

Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR){Node *node=(Node*)malloc(sizeof(Node));if(node==NULL){perror("malloc");exit(1);}char *msg="system";for(int i=0;i<sizeof(msg);i++){node->name[i]=msg[i];}node->addr.sin_family=AF_INET;node->addr.sin_port=htons(SER_PORT);node->addr.sin_addr.s_addr=inet_addr(SER_IP_STR);node->next=NULL;return node;
}

1.6  向链表中加入节点

Node *add_node(char *name,struct sockaddr_in addr){Node *new_node=(Node*)malloc(sizeof(Node));if(new_node==NULL){perror("malloc");exit(1);}strcpy(new_node->name,name);new_node->addr=addr;new_node->next=NULL;return new_node;
}

1.7 对链表判空

int is_empty(Node *head){if(head==NULL){return 1;}return head->next==NULL?1:0;
}

1.8 插入节点

oid insert_node(Node *head,char *name,struct sockaddr_in addr){if(head==NULL){perror("head is null");exit(1);}   Node *new_node=add_node(name,addr);new_node->next=head->next;head->next=new_node;printf("%s进入聊天室\n",name);
}

1.9 判断数据模式

int print_msg(Msgtype *msg){switch(msg->type){case 'L':return 1;case 'C':return 2;case 'Q':return 3;default:return 0;}
}

2.0 查找用户是否存在

int is_exist(Node *head,Msgtype *msg){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){return 0;}Node *p=head->next;while(p!=NULL){if(strcmp(p->name,msg->username)==0){return 1;}p=p->next;}return 0;
}

2.1 广播消息 

void broadcast(int sfd,Node *head,Msgtype *msg){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){printf("聊天室为空\n");return;}Node *p=head->next;while(p!=NULL){/*发送消息*/if(strcmp(p->name,msg->username)!=0){sendto(sfd,msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));}p=p->next;}
}

2.2 按用户名删除用户节点

void delete_node(Node *head,char *name){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){printf("聊天室为空\n");return;}printf("%s退出聊天室\n",name);Node *p=head->next;Node *pre=head;while(p!=NULL){if(strcmp(p->name,name)==0){pre->next=p->next;free(p);return;}pre=p;p=p->next;}

2.3 广播系统信息

void broadcast_system(int sfd,Node *head,char *content){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){printf("聊天室为空\n");return;}Msgtype msg;msg.type='C';strcpy(msg.username,"system");strcpy(msg.content,content);Node *p=head->next;while(p!=NULL){/*发送消息*/sendto(sfd,&msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));p=p->next;}printf("system发送消息:%s\n",content);
}

2.4 创建套接字,绑定IP,端口信息

/*获取IP地址和端口*/if(3>argc){printf("输入格式不对,请按照以下格式输入:./server ip port\n");exit(1);}const char *SER_IP_STR=argv[1];in_port_t SER_PORT=atoi(argv[2]);/*创建UDP套接字*/int sfd=-1;if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){perror("socket");exit(1);} /*设置UDP服务器的地址*/struct sockaddr_in addr;memset(&addr,0,sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(SER_PORT);if(inet_aton(SER_IP_STR,&addr.sin_addr)==0){perror("inet_aton");exit(1);}/*绑定端口*/if(bind(sfd,(struct sockaddr*)&addr,sizeof(addr))<0){perror("bind");exit(1);}printf("bind success\n");

2.5 创建链表

/*创建链表*/Node *head=create_node(SER_PORT,SER_IP_STR);

2.6 使用poll实现并发执行

/*监控服务器响应*/struct pollfd pfd[2];pfd[0].fd = sfd;        // 监控套接字pfd[0].events = POLLIN; // 监控读事件pfd[1].fd = 0;          // 监控标准输入pfd[1].events = POLLIN; // 监控读事件
/*等待IO事件*/int ret=poll(pfd,2,-1);if(ret<0){perror("poll");exit(1);}else if (ret==0){printf("poll timeout\n");continue;}
if(pfd[0].revents==POLLIN){
}
if(pfd[1].revents==POLLIN){
}

2.7  创建接收客户端网络信息,数据信息的结构体

/*创建接收客户端网络信息的结构体*/struct sockaddr_in client_addr;socklen_t client_addr_len=sizeof(client_addr);/*创建客户端数据信息的结构体*/Msgtype client_msg;memset(&client_msg,0,sizeof(client_msg));

 2.8 接收数据处理

int recv_len=recvfrom(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,&client_addr_len);if(recv_len<0){perror("recvfrom");exit(1);}/*判断消息类型*//*登录类型*/if(print_msg(&client_msg)==1){/*判断用户是否存在*/if(is_exist(head,&client_msg)){/*向客户端发送用户名已存在消息*/client_msg.type='E';sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_addr));continue;}else{/*向链表中插入节点*/insert_node(head,client_msg.username,client_addr);/*向客户端发送登录成功消息*/client_msg.type='L';strcpy(client_msg.content,"进入聊天室");/*向所有客户端发送登录消息*/broadcast(sfd,head,&client_msg);}}else if (print_msg(&client_msg)==2){/*客户端数据传输类型*//*向所有客户端发送消息*/printf("%s发送消息\n",client_msg.username);broadcast(sfd,head,&client_msg);}else if (print_msg(&client_msg)==3){/*客户端退出登录类型*//*删除节点*/delete_node(head,client_msg.username);/*向客户端发送退出登录消息*/client_msg.type='Q';strcpy(client_msg.content,"退出聊天室");broadcast(sfd,head,&client_msg);}else{/*客户端消息类型错误*/char *msg="消息类型错误";for(int i=0;i<sizeof(msg);i++){client_msg.content[i]=msg[i];}sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_msg));continue; }

 2.9 发送数据处理

/*服务器终端输入*/char input_buf[BUFSIZE];fgets(input_buf,BUFSIZE,stdin);input_buf[strlen(input_buf)-1]='\0';/*判断客户端是否登录*/if(is_empty(head)){printf("聊天室为空,请等待其他用户登录\n");continue;}/*向所有客户端发送消息*/broadcast_system(sfd,head,input_buf);

 2.创建UDP客户端

1.1 定义宏

#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录信息长度

1.2 定义数据信息结构体

typedef struct Msgtype{char type;char username[login_msg_len];char content[BUFSIZE];
}Msgtype;

1.3 创建套接字,绑定服务器

 /*获取IP地址和端口*/if(3>argc){printf("输入格式不对,请按照以下格式输入:./server ip port\n");}const char *SER_IP_STR=argv[1];int SER_PORT=atoi(argv[2]);/*创建UDP套接字*/int sfd=-1;if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){perror("socket");exit(1);}/*设置服务器地址*/struct sockaddr_in server_addr;memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(SER_PORT);if(inet_aton(SER_IP_STR,&server_addr.sin_addr)==0){perror("inet_aton");exit(1);}socklen_t addr_len=sizeof(server_addr);

 1.4 创建客户端数据手收发结构体,用户登录信息结构体

/*创建客户端数据收发结构体*/Msgtype recv_msg;memset(&recv_msg,0,sizeof(recv_msg));Msgtype send_msg;memset(&send_msg,0,sizeof(send_msg));/*创建用户登录信息*/Msgtype login_msg;

1.5  用户登录

 /*登录服务器*/memset(&login_msg,0,sizeof(login_msg));login_msg.type='L';printf("请输入用户名:");fgets(login_msg.username, login_msg_len, stdin);login_msg.username[strcspn(login_msg.username, "\n")] = '\0'; // 去掉换行符/*发送登录信息*/if (sendto(sfd, &login_msg, sizeof(login_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){perror("sendto");exit(1);}

1.6 使用poll实现并发 

/*监控服务器响应*/struct pollfd pfd[2];pfd[0].fd = sfd;        // 监控套接字pfd[0].events = POLLIN; // 监控读事件pfd[1].fd = 0;          // 监控标准输入pfd[1].events = POLLIN; // 监控读事件;
/*等待IO事件*/int ret=poll(pfd,2,-1);if(ret<0){perror("poll");exit(1);}else if (ret==0)   {printf("poll timeout\n");continue;}
if(pfd[0].revents==POLLIN){
}
if(pfd[1].revents==POLLIN){
}

 1.7 根据条件进行并发执行终端输入,数据接收

if(pfd[0].revents==POLLIN){/*接收服务器消息*/int len=recvfrom(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr *)&server_addr, &addr_len);if(len<0){perror("recvfrom");exit(1);}if(recv_msg.type=='L'){printf("-----------%s加入聊天室----------\n",recv_msg.username);}else if(recv_msg.type=='C'){printf("%s:%s\n",recv_msg.username,recv_msg.content);}else if (recv_msg.type=='Q') {printf("------------%s%s---------------\n",recv_msg.username,recv_msg.content);}else{printf("%s用户名已存在或服务器故障,请重新登录\n",login_msg.username);break;}}if(pfd[1].revents==POLLIN){/*接收用户输入*/char input[BUFSIZE];fgets(input,BUFSIZE, stdin);input[strcspn(input, "\n")] = '\0'; // 去掉换行符if(strcmp(input,"quit")==0){send_msg.type='Q';sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));break;//退出循环}else{/*发送消息*/memset(&send_msg,0,sizeof(send_msg));send_msg.type='C';strncpy(send_msg.username,login_msg.username,login_msg_len);strncpy(send_msg.content,input,BUFSIZE);sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));}}

 3.服务器最终程序

#include <myhead.h>
#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录消息长度/*定义链表节点*/
typedef struct Node{char name[login_msg_len];struct sockaddr_in addr;struct Node *next;
}Node;/*定义消息类型*/
typedef struct Msgtype{char type;char username[login_msg_len];char content[BUFSIZE];
}Msgtype;/*声明函数*/
Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR);/*创建单向链表*/
Node *add_node(char *name,struct sockaddr_in addr);/*向链表中添加节点*/
int print_msg(Msgtype *msg);/*打印消息类型*/
void broadcast(int sfd,Node *head,Msgtype *msg);/*广播消息*/
int is_exist(Node *head,Msgtype *msg);/*查找用户名是否存在*/
int is_empty(Node *head);/*链表判空*/
void insert_node(Node *head,char *name,struct sockaddr_in addr);/*头插法插入节点*/
void delete_node(Node *head,char *name);/*按用户名删除节点*/
void broadcast_system(int sfd,Node *head,char *input_buf);/*向所有客户端发送系统消息*/int main(int argc, char const *argv[])
{   /*获取IP地址和端口*/if(3>argc){printf("输入格式不对,请按照以下格式输入:./server ip port\n");exit(1);}const char *SER_IP_STR=argv[1];in_port_t SER_PORT=atoi(argv[2]);/*创建UDP套接字*/int sfd=-1;if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){perror("socket");exit(1);} /*设置UDP服务器的地址*/struct sockaddr_in addr;memset(&addr,0,sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(SER_PORT);if(inet_aton(SER_IP_STR,&addr.sin_addr)==0){perror("inet_aton");exit(1);}/*绑定端口*/if(bind(sfd,(struct sockaddr*)&addr,sizeof(addr))<0){perror("bind");exit(1);}printf("bind success\n");/*创建链表*/Node *head=create_node(SER_PORT,SER_IP_STR);/*监控服务器响应*/struct pollfd pfd[2];pfd[0].fd = sfd;        // 监控套接字pfd[0].events = POLLIN; // 监控读事件pfd[1].fd = 0;          // 监控标准输入pfd[1].events = POLLIN; // 监控读事件/*创建接收客户端网络信息的结构体*/struct sockaddr_in client_addr;socklen_t client_addr_len=sizeof(client_addr);/*创建客户端数据信息的结构体*/Msgtype client_msg;memset(&client_msg,0,sizeof(client_msg));/*循环接收客户端信息*/while(1){/*等待IO事件*/int ret=poll(pfd,2,-1);if(ret<0){perror("poll");exit(1);}else if (ret==0){printf("poll timeout\n");continue;}if(pfd[0].revents==POLLIN){/*接收客户端登录信息*/int recv_len=recvfrom(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,&client_addr_len);if(recv_len<0){perror("recvfrom");exit(1);}/*判断消息类型*//*登录类型*/if(print_msg(&client_msg)==1){/*判断用户是否存在*/if(is_exist(head,&client_msg)){/*向客户端发送用户名已存在消息*/client_msg.type='E';sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_addr));continue;}else{/*向链表中插入节点*/insert_node(head,client_msg.username,client_addr);/*向客户端发送登录成功消息*/client_msg.type='L';strcpy(client_msg.content,"进入聊天室");/*向所有客户端发送登录消息*/broadcast(sfd,head,&client_msg);}}else if (print_msg(&client_msg)==2){/*客户端数据传输类型*//*向所有客户端发送消息*/printf("%s发送消息\n",client_msg.username);broadcast(sfd,head,&client_msg);}else if (print_msg(&client_msg)==3){/*客户端退出登录类型*//*删除节点*/delete_node(head,client_msg.username);/*向客户端发送退出登录消息*/client_msg.type='Q';strcpy(client_msg.content,"退出聊天室");broadcast(sfd,head,&client_msg);}else{/*客户端消息类型错误*/char *msg="消息类型错误";for(int i=0;i<sizeof(msg);i++){client_msg.content[i]=msg[i];}sendto(sfd,&client_msg,sizeof(client_msg),0,(struct sockaddr*)&client_addr,sizeof(client_msg));continue; }}if(pfd[1].revents==POLLIN){/*服务器终端输入*/char input_buf[BUFSIZE];fgets(input_buf,BUFSIZE,stdin);input_buf[strlen(input_buf)-1]='\0';/*判断客户端是否登录*/if(is_empty(head)){printf("聊天室为空,请等待其他用户登录\n");continue;}/*向所有客户端发送消息*/broadcast_system(sfd,head,input_buf);}}/*关闭套接字*/close(sfd);return 0;
}
/*创建单向链表*/
Node *create_node(in_port_t SER_PORT,const char * SER_IP_STR){Node *node=(Node*)malloc(sizeof(Node));if(node==NULL){perror("malloc");exit(1);}char *msg="system";for(int i=0;i<sizeof(msg);i++){node->name[i]=msg[i];}node->addr.sin_family=AF_INET;node->addr.sin_port=htons(SER_PORT);node->addr.sin_addr.s_addr=inet_addr(SER_IP_STR);node->next=NULL;return node;
}
/*向链表中添加节点*/
Node *add_node(char *name,struct sockaddr_in addr){Node *new_node=(Node*)malloc(sizeof(Node));if(new_node==NULL){perror("malloc");exit(1);}strcpy(new_node->name,name);new_node->addr=addr;new_node->next=NULL;return new_node;
}
/*链表判空*/
int is_empty(Node *head){if(head==NULL){return 1;}return head->next==NULL?1:0;
}
/*头插法插入节点*/
void insert_node(Node *head,char *name,struct sockaddr_in addr){if(head==NULL){perror("head is null");exit(1);}   Node *new_node=add_node(name,addr);new_node->next=head->next;head->next=new_node;printf("%s进入聊天室\n",name);
}
/*判断数据模式*/
int print_msg(Msgtype *msg){switch(msg->type){case 'L':return 1;case 'C':return 2;case 'Q':return 3;default:return 0;}
}
/*查找用户名是否存在*/
int is_exist(Node *head,Msgtype *msg){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){return 0;}Node *p=head->next;while(p!=NULL){if(strcmp(p->name,msg->username)==0){return 1;}p=p->next;}return 0;
}
/*广播消息*/
void broadcast(int sfd,Node *head,Msgtype *msg){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){printf("聊天室为空\n");return;}Node *p=head->next;while(p!=NULL){/*发送消息*/if(strcmp(p->name,msg->username)!=0){sendto(sfd,msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));}p=p->next;}
}
/*按用户名删除节点*/
void delete_node(Node *head,char *name){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){printf("聊天室为空\n");return;}printf("%s退出聊天室\n",name);Node *p=head->next;Node *pre=head;while(p!=NULL){if(strcmp(p->name,name)==0){pre->next=p->next;free(p);return;}pre=p;p=p->next;}}
/*广播system消息*/
void broadcast_system(int sfd,Node *head,char *content){if(head==NULL){perror("head is null");exit(1);}if(is_empty(head)){printf("聊天室为空\n");return;}Msgtype msg;msg.type='C';strcpy(msg.username,"system");strcpy(msg.content,content);Node *p=head->next;while(p!=NULL){/*发送消息*/sendto(sfd,&msg,sizeof(Msgtype),0,(struct sockaddr*)&p->addr,sizeof(p->addr));p=p->next;}printf("system发送消息:%s\n",content);
}

 4.客户端最终程序

#include <myhead.h>
#define BUFSIZE 1024//定义缓冲区大小
#define login_msg_len 20//定义登录信息长度/*定义消息类型*/
typedef struct Msgtype{char type;char username[login_msg_len];char content[BUFSIZE];
}Msgtype;int main(int argc, char const *argv[])
{   /*获取IP地址和端口*/if(3>argc){printf("输入格式不对,请按照以下格式输入:./server ip port\n");}const char *SER_IP_STR=argv[1];int SER_PORT=atoi(argv[2]);/*创建UDP套接字*/int sfd=-1;if((sfd=socket(AF_INET,SOCK_DGRAM,0))<0){perror("socket");exit(1);}/*设置服务器地址*/struct sockaddr_in server_addr;memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(SER_PORT);if(inet_aton(SER_IP_STR,&server_addr.sin_addr)==0){perror("inet_aton");exit(1);}socklen_t addr_len=sizeof(server_addr);/*创建客户端数据收发结构体*/Msgtype recv_msg;memset(&recv_msg,0,sizeof(recv_msg));Msgtype send_msg;memset(&send_msg,0,sizeof(send_msg));/*创建用户登录信息*/Msgtype login_msg;while (1){/*登录服务器*/memset(&login_msg,0,sizeof(login_msg));login_msg.type='L';printf("请输入用户名:");fgets(login_msg.username, login_msg_len, stdin);login_msg.username[strcspn(login_msg.username, "\n")] = '\0'; // 去掉换行符/*发送登录信息*/if (sendto(sfd, &login_msg, sizeof(login_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0){perror("sendto");exit(1);}/*监控服务器响应*/struct pollfd pfd[2];pfd[0].fd = sfd;        // 监控套接字pfd[0].events = POLLIN; // 监控读事件pfd[1].fd = 0;          // 监控标准输入pfd[1].events = POLLIN; // 监控读事件;while(1){/*等待IO事件*/int ret=poll(pfd,2,-1);if(ret<0){perror("poll");exit(1);}else if (ret==0)   {printf("poll timeout\n");continue;}if(pfd[0].revents==POLLIN){/*接收服务器消息*/int len=recvfrom(sfd, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr *)&server_addr, &addr_len);if(len<0){perror("recvfrom");exit(1);}if(recv_msg.type=='L'){printf("-----------%s加入聊天室----------\n",recv_msg.username);}else if(recv_msg.type=='C'){printf("%s:%s\n",recv_msg.username,recv_msg.content);}else if (recv_msg.type=='Q') {printf("------------%s%s---------------\n",recv_msg.username,recv_msg.content);}else{printf("%s用户名已存在或服务器故障,请重新登录\n",login_msg.username);break;}}if(pfd[1].revents==POLLIN){/*接收用户输入*/char input[BUFSIZE];fgets(input,BUFSIZE, stdin);input[strcspn(input, "\n")] = '\0'; // 去掉换行符if(strcmp(input,"quit")==0){send_msg.type='Q';sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));break;//退出循环}else{/*发送消息*/memset(&send_msg,0,sizeof(send_msg));send_msg.type='C';strncpy(send_msg.username,login_msg.username,login_msg_len);strncpy(send_msg.content,input,BUFSIZE);sendto(sfd, &send_msg, sizeof(send_msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));}}}}close(sfd);return 0;
}

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

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

相关文章

Softing工业将亮相2024年阿赫玛展会——提供过程自动化的连接解决方案

您可于2024年6月10日至14日前往美因河畔法兰克福11.0号馆&#xff0c;Softing将在C25展位展出&#xff0c;欢迎莅临&#xff01; 作为工业应用中数据交换领域公认的专家&#xff0c;Softing工业致力于帮助各行各业的客户部署网络自动化和优化生产流程。 使用Softing产品&…

mybatis plus 配置多数据源(数据源进行切换)

多数据源(数据源进行切换) AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源&#xff0c;这样我们可以在执行查询之前&#xff0c;设置使用的数据源。实现可动态路由的数据源&#xff0c;在每次数据库查询操作前执行。它的抽象方法 determineCurrentLookupKey()…

如何在OpenHarmony上使用SeetaFace2人脸识别库?

简介 相信大部分同学们都已了解或接触过OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;了&#xff0c;但你一定没在OpenHarmony上实现过人脸识别功能&#xff0c;跟着本文带你快速在OpenHarmony标准设备上基于SeetaFace2和OpenCV实现人脸识别。 项目效…

【Vue】Vue2路由

目录 路由作用Vue Router路由Vue Router路由的组成VueRouter常用的函数Vue Router的使用安装Vue Router创建router引入router使用 备注 Vue多级路由&#xff08;嵌套路由&#xff09;编写组件配置嵌套路由 Vue中的动态路由代码示例父组件Home.vue子组件路由配置 路由的 query 参…

黑龙江等保测评深入理解

“没有网络安全&#xff0c;就没有国家安全”&#xff0c;等级保护测评是指按照网络安全系统制定的一系列的防护过程&#xff0c;对已经有的和即将上线的商业服务的基础设施&#xff08;系统&#xff0c;数据库&#xff0c;中间件等&#xff09;所做的一系列的检查&#xff0c;…

HeyGen AI是什么?怎样使用HeyGen AI?

在数字时代&#xff0c;视频内容为王。无论是在社交媒体还是网站上&#xff0c;视频都以其独特的方式吸引着人们的眼球。然而&#xff0c;制作出专业水准的视频往往需要大量的时间和技术知识。HeyGen AI正是为了解决这一难题而诞生的。 HeyGen AI简介 HeyGen AI是一个创新的视…

618值得买的好物清单,这些数码好物你千万不能错过!

​随着618购物节的距离越来越近&#xff0c;你是不是已经开始疯狂浏览购物app&#xff0c;准备大肆采购一番了&#xff1f;但是在购物之前&#xff0c;还是得先做一做功课&#xff0c;避免陷入购物陷阱&#xff0c;而作为一名经验丰富的数码爱好者&#xff0c;想通过这次机会给…

Thinkphp内核开发盲盒商城源码v2.0 对接易支付/阿里云短信/七牛云存储

源码简介 这套系统是我从以前客户手里拿到的,100完整可用,今天测试防红链接失效了,需要修改防红API即可!前端页面展示我就不放了,懂的都懂 优点是Thinkphp开发的&#xff0c;二开容易。 源码图片 资源获取&#xff1a;Thinkphp内核开发盲盒商城源码v2.0 对接易支付/阿里云短…

kafka监控配置和告警配置——筑梦之路

kafka_exporter项目地址&#xff1a;https://github.com/danielqsj/kafka_exporter docker-compose部署kafka_exporter # docker-compose部署多个kafka_exporter&#xff0c;每个exporter对接一个kafka# cat docker-compose.ymlversion: 3.1 services:kafka-exporter-opslogs…

KubeEdge学习

KubeEdge学习主要包括对KubeEdge的理解、安装、配置、部署应用以及了解其在实践中的应用案例等方面。以下是关于KubeEdge学习的详细步骤和要点&#xff1a; 理解KubeEdge&#xff1a; KubeEdge是一个开源的系统&#xff0c;它基于Kubernetes构建&#xff0c;旨在将本机容器化应…

vue3+Ts 关于生成环境与开发环境请求路径问题

这里我是创建了axios 实例&#xff0c;通过axios 实例去请求后端&#xff0c; // 创建axios实例 export const service:AxiosInstance axios.create({//请求地址baseURL: (window as any).Config.BACKEND_URL, //import.meta.env.VITE_APP_BASE_API, // 超时timeout: 10000 *…

3DMax文件打开跳出请求操作需要提升

解决方法如下 打开autoremove&#xff0c;点击扩展功能&#xff0c;点击管理员已经阻止运行此应用 提示修复成功后&#xff0c;重启电脑再尝试打开max文件。

保研笔试复习——nju

文章目录 一、单选计算机网络计算机组成原理数字逻辑电路数据结构操作系统微机系统 多选题计算机网络计算机系统结构操作系统 免责声明&#xff1a;题目源自于网络&#xff0c;侵删。 就在今天2024-5-18&#xff0c;考的题下面的只有一道AVL的原题&#xff0c;其他都不是原题&a…

平板如何实现无纸化会议

为了实现高效的无纸化会议&#xff0c;连通宝可以是在内部网络部署&#xff0c;那么&#xff0c;平板如何实现无纸化会议&#xff1f; 1. 服务器配置&#xff1a; 部署专用无纸化会议系统服务器&#xff08;如rhub无纸化会议服务器&#xff09;至组织的内部网络中。确保该服务…

Ipad air6买什么电容笔?5款超值精品平替电容笔推荐!

电容笔作为ipad的最佳拍档&#xff0c;为学生党和打工人带来了极大的便利&#xff0c;二者搭配效率真的大大提升&#xff0c;但是&#xff0c;如何选购一支适合自己的电容笔呢&#xff1f;作为一个对数码设备非常感兴趣并且有一定了解的人&#xff0c;我根据自己多年的使用经验…

SQLite数据库免改造透明加密解决方案:给数据加把锁

在数字化时代&#xff0c;信息安全和隐私保护显得尤为重要。TDE透明加密技术&#xff0c;是一种在用户无感知的情况下对数据进行加密和解密的技术。它能够在数据生成、存储、传输和使用过程中自动进行加密处理&#xff0c;无需用户手动操作。透明加密技术的核心在于其透明性&am…

webgl three 模型、几何体、材质关系 及克隆、拷贝

使用相同几何体、材质的模型相互等价 console.log(material1);console.log(mesh.material)console.log(mesh1.material) 当一个改变时另外两个也都改变 几何体也一样 重复模型、几何体、材质可以多次使用 更改坐标放在不同位置 网格模型Mesh、几何体、材质复制使用克隆.clon…

Wireshark 4.2.5:发现 QUIC 和 VXLAN 协议的新功能

Wireshark 是一种先进且广泛使用的网络协议分析仪&#xff0c;最近发布了新版本 4.2.5&#xff0c;它提供了许多新功能和改进。 Wireshark 4.2.5 发行说明 什么是 Wireshark&#xff1f; Wireshark 是世界上最流行的网络协议分析器。它用于故障排除、分析、开发和教育。 Wiresh…