8. 网络编程
网络的基本概念
TCP/IP协议概述
OSI和TCP/IP模型
socket(套接字)
创建socket
字节序
字节序转换函数
通用地址结构
因特网地址结构
IPV4地址族和字符地址间的转换(点分十进制->网络字节序)
填写IPV4地址族结构案例
掌握TCP协议网络基础编程
相关函数
特殊bind地址
案例1:time
-
先启动一个服务端,客户链接上来之后,服务端返回一个系统时间
-
time_tcp_server.c
#include <stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>int sockfd;void sig_handler(int signo)
{if(signo==SIGINT){printf("server close\n");/*步骤6:* 关闭socket* */close(sockfd);exit(1);}
}/*输出连接上来的客户端相关信息* */
void out_addr(struct sockaddr_in *clientaddr)
{// 将端口从网络字节序转换成主机字节序int port = ntohs(clientaddr->sin_port);char ip[16];memset(ip,0,sizeof(ip));// 网络字节序-> 点分十进制inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));printf("client:%s(%d) connected \n",ip,port);
}
void do_service(int fd)
{// 获得系统时间long t = time(0);char *s = ctime(&t);size_t size = strlen(s)*sizeof(char);// 将服务器端获得的系统时间写到客户端if(write(fd,s,size)!=size){perror("write error");}
}
int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}// 绑定一个信号,ctrl+c 终止服务端if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint error");exit(1);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/sockfd = socket(AF_INET,SOCK_STREAM,0);/** 步骤2:调用bind函数将socket和地址(ip和port)进行绑定 * */// 专用地址,绑定的时候在强转为通用地址struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1]));// 字符串-> 整型,主机字节序->网络字节序serveraddr.sin_addr.s_addr = INADDR_ANY;//响应所有请求if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}/**步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求将接受到的客户端连接请求放置到对应的队列中第二个参数:10:队列的长度* */if(listen(sockfd,10)<0){perror("listen error");exit(1);}/**步骤4:调用accept函数,从队列中获得一个客户端的请求连接* 并返回一个新的socket描述符*注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端的连接* */struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);while(1){// 第二个参数可以设置为NULL,如果不想知道客户端的信息的话int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);if(fd<0){perror("accept error");continue;}/**步骤5:调用IO函数和客户端进行双向的通信*/out_addr(&clientaddr);do_service(fd);/*步骤5:* 关闭 socket* */


- time_tcp_client.c```c
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:创建socket* */struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[2]));// 字符串-> 整型,主机字节序->网络字节序inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("connect error");exit(1);}// 步骤3:调用IO函数,和服务器进行双向通讯char buffer[1024];memset(buffer,0,sizeof(buffer));size_t size;if((size=read(sockfd,buffer,sizeof(buffer)))<0){perror("read error");}if(write(STDOUT_FILENO,buffer,size)!=size){perror("write error");}// 步骤4:关闭socketclose(sockfd);return 0;
}
自定义协议
- msg.h
#ifndef __MSG_H__
#define __MSG_H__
#include<sys/types.h>
typedef struct{// 协议头部char head[10];// 验证码char checknum;//协议体部char buff[512] ;//数据
}MSG;
/** 发送一个基于自定义协议的message,发送的数据存放在buff中* */
extern int write_msg(int sockfd,char *buff,size_t len);
/*读取一个基于自定义协议的message。读取的数据存放在buff中* */
extern int read_msg(int sockfd,char *buff,size_t len);#endif
- msg.c
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<sys/types.h>
#include"msg.h"// 计算校验码
static unsigned char msg_check(Msg *message)
{unsigned char s = 0;for(int i=0;i<sizeof(message->head);i++){s+=message->head[i];}for(int i=0;i<sizeof(message->buff);i++){s+=message->buff[i];}return s;
}/** 发送一个基于自定义协议的message,发送的数据存放在buff中* */int write_msg(int sockfd,char *buff,size_t len)
{Msg message;memset(&message,0,sizeof(message));strcpy(message.head,"iotek2025");memcpy(message.buff,buff,len);message.checknum = msg_check(&message);if(write(sockfd,&message,sizeof(message))!=sizeof(message)){return -1;}
}/*读取一个基于自定义协议的message。读取的数据存放在buff中* */
int read_msg(int sockfd,char *buff,size_t len)
{Msg message;memset(&message,0,sizeof(message));size_t size;if((size=read(sockfd,&message,sizeof(message)))<0){return -1;}else if(size==0){return 0;}// j进行校验码的验证unsigned char s =msg_check(&message);if((s==(unsigned char)message.checknum) && (!strcmp("iotek2025",message.head))){memcpy(buff,message.buff,len);return sizeof(message);}return -1;
}
- 在服务端的do_service中增加
void do_service(int fd)
{
//----------------------------------------size_t len;char buff[20];if((len=read(fd,buff,20))<0) // 注意由于客户端没有给服务端写数据,所以会在此阻塞{perror("do_service server read error");}//----------------------------------------// 获得系统时间long t = time(0);char *s = ctime(&t);size_t size = strlen(s)*sizeof(char);// 将服务器端获得的系统时间写到客户端if(write(fd,s,size)!=size){perror("write error");}
}
服务器并发性处理
多进程模型
- echo_tcp_server
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>
#include"msg.h"
int sockfd;void sig_handler(int signo)
{if(signo==SIGINT){printf("server close\n");/*步骤6:* 关闭socket* */close(sockfd);exit(1);}if(signo==SIGCHLD){printf("child process dead.....\n");wait(0);}
}/*输出连接上来的客户端相关信息* */
void out_addr(struct sockaddr_in *clientaddr)
{// 将端口从网络字节序转换成主机字节序int port = ntohs(clientaddr->sin_port);char ip[16];memset(ip,0,sizeof(ip));// 网络字节序-> 点分十进制inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));printf("client:%s(%d) connected \n",ip,port);
}
void do_service(int fd)
{// 和客户端进行读写操作(双向通信)char buff[512];while(1){memset(buff,0,sizeof(buff));printf("start read and write....\n");size_t size;if((size=read_msg(fd,buff,sizeof(buff)))<0){perror("protocal error");break;}else if(size==0){break; // 写端突然挂了}else{printf("%s\n",buff);if(write_msg(fd,buff,sizeof(buff))<0){if(errno==EPIPE)// 读端突然关闭时,类似于管道通信{break;}perror("protocal error");}}}
}
int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}// 绑定一个信号,ctrl+c 终止服务端if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint error");exit(1);}if(signal(SIGCHLD,sig_handler)==SIG_ERR)// 等子进程终止发送的信号{perror("signal sigchild error");exit(1);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:调用bind函数将socket和地址(ip和port)进行绑定 * */// 专用地址,绑定的时候在强转为通用地址struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1]));// 字符串-> 整型,主机字节序->网络字节序serveraddr.sin_addr.s_addr = INADDR_ANY;//响应所有请求if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}/**步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求将接受到的客户端连接请求放置到对应的队列中第二个参数:10:队列的长度* */if(listen(sockfd,10)<0){perror("listen error");exit(1);}/**步骤4:调用accept函数,从队列中获得一个客户端的请求连接* 并返回一个新的socket描述符*注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端的连接* */struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);while(1){// 第二个参数可以设置为NULL,如果不想知道客户端的信息的话int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);if(fd<0){perror("accept error");continue;}/**步骤5:启动子进程调用IO函数 */pid_t pid = fork();if(pid<0){continue;}else if(pid==0){out_addr(&clientaddr);do_service(fd);/*步骤6:* 关闭 socket* */close(fd); /// 子进程会复制父进程的fd}else{close(fd); }}return 0;
}
- echo_tcp_client.c
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>
#include"msg.h"int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:创建socket* */struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[2]));// 字符串-> 整型,主机字节序->网络字节序inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("connect error");exit(1);}// 步骤3:调用IO函数,和服务器进行双向通讯char buff[512];size_t size;char * prompt = ">" ;while(1){memset(buff,0,sizeof(buff));write(STDOUT_FILENO,prompt,1);size = read(STDIN_FILENO,buff,sizeof(buff));if(size<0) {continue;}buff[size-1] = '\0';if(write_msg(sockfd,buff,sizeof(buff))<0){perror("write msg error");continue;}else{if(read_msg(sockfd,buff,sizeof(buff))<0){perror("read msg error");continue;}else{printf("%s\n",buff);}}}// 步骤4:关闭socketclose(sockfd);return 0;
}
- 下面这个没有出现
多线程模型
- ehco_tcp_server_th.c
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<pthread.h>
#include"msg.h"int sockfd;void sig_handler(int signo)
{if(signo==SIGINT){printf("server close\n");/*步骤6:* 关闭socket* */close(sockfd);exit(1);}
}void do_service(int fd)
{// 和客户端进行读写操作(双向通信)char buff[512];while(1){memset(buff,0,sizeof(buff));printf("start read and write....\n");size_t size;if((size=read_msg(fd,buff,sizeof(buff)))<0){perror("protocal error");break;}else if(size==0){break; // 写端突然挂了}else{printf("%s\n",buff);if(write_msg(fd,buff,sizeof(buff))<0){if(errno==EPIPE)// 读端突然关闭时,类似于管道通信{break;}perror("protocal error");}}}
}void out_fd(int fd)
{struct sockaddr_in addr;socklen_t len = sizeof(addr);// 从fd中获得连接的客户端的相关信息if(getpeername(fd,(struct sockaddr*)&addr,&len)<0){perror("getpeername error");return;}char ip[16];memset(ip,0,sizeof(ip));int port = ntohs(addr.sin_port);inet_ntop(AF_INET,&addr.sin_addr.s_addr,ip,sizeof(ip));printf("%16s(%5d) closed\n",ip,port);
}void * th_fn(void *arg)
{int fd = (int)arg;do_service(fd);out_fd(fd);// 输出客户端的信息close(fd);return (void*)0;
}int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}// 绑定一个信号,ctrl+c 终止服务端if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint error");exit(1);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:调用bind函数将socket和地址(ip和port)进行绑定 * */// 专用地址,绑定的时候在强转为通用地址struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1]));// 字符串-> 整型,主机字节序->网络字节序serveraddr.sin_addr.s_addr = INADDR_ANY;//响应所有请求if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}/**步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求将接受到的客户端连接请求放置到对应的队列中第二个参数:10:队列的长度* */if(listen(sockfd,10)<0){perror("listen error");exit(1);}/**步骤4:调用accept函数,从队列中获得一个客户端的请求连接* 并返回一个新的socket描述符*注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端的连接* */struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);// 设置线程的分离属性pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);while(1){// 第二个参数可以设置为NULL,如果不想知道客户端的信息的话// 主控线程负责调用accept去获得客户端的连接 int fd = accept(sockfd,NULL,NULL);if(fd<0){perror("accept error");continue;}/**步骤5:启动子线程调用IO函数 */pthread_t th;int err;// 以分离状态启动子线程if((err=pthread_create(&th,&attr,th_fn,(void*)fd))!=0){perror("pthread create error");}pthread_attr_destroy(&attr);}return 0;
}
I/O多路转换(select)
掌握UDP协议网络基础编程
发生数据
接收数据
- time_udp_server
#include <stdio.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<signal.h>
#include<time.h>
#include<netdb.h>int sockfd;void sig_handler(int signo)
{if(signo==SIGINT){printf("server close\n");close(sockfd);exit(1);}}// 输出客户端的信息
void out_addr(struct sockaddr_in *clientaddr)
{char ip[16];memset(ip,0,sizeof(ip));inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));int port = ntohs(clientaddr->sin_port);printf("client : %s(%d)\n",ip,port);
}// 和客户端进行通信
void do_service()
{struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);char buffer[1024];memset(buffer,0,sizeof(buffer));// 接受客户端的数据报文if(recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&clientaddr,&len)<0){perror("recvfrom error");}else{out_addr(&clientaddr);printf("client send into : %s\n",buffer);//向客户端发送数据报文long int t = time(0);char * ptr = ctime(&t);size_t size = strlen(ptr)*sizeof(char);if(sendto(sockfd,ptr,size,0,(struct sockaddr*)&clientaddr,len)<0){perror("sendto error");}}}
int main(int argc,char *argv[])
{if(argc<0){printf("usage : %s port \n",argv[0]);exit(1);}if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint error");exit(1);}/*步骤1:** */sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){perror("sockfd error");exit(1);}int res;int opt = 1;// 设置套接字选项:让刚才实现的端口立即启动if((res=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)))<0){perror("setsockopt error");exit(1);}/*步骤2:调用bind函数对socket和地址进行绑定* */struct sockaddr_in serveraddr;memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1])); // portserveraddr.sin_addr.s_addr = INADDR_ANY; //ipif(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}/*步骤3:和客户端进行双向的数据通信* */while(1){do_service();}return 0;
}
- time_udp_client
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netdb.h>
#include<sys/socket.h>
#include<memory.h>
#include<unistd.h>int main(int argc,char *argv[])
{if(argc<3){printf("usgae : %s ip port \n",argv[0]);exit(1);}/*步骤1:创建socket* */int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){perror("socket error");exit(1);}/*步骤2:调用recvfrom和sendto等函数* 和服务器进行双向通信* */struct sockaddr_in serveraddr;memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr); //ipchar buffer[1024] = "hello iotek";// 向服务器端发送数据报文if(sendto(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("sendto error");exit(1);}else{//接受服务器发送的报文memset(buffer,0,sizeof(buffer));if(recv(sockfd,buffer,sizeof(buffer),0)<0){perror("recv error");exit(1);}else{printf("%s",buffer);}close(sockfd);}return 0;
}
- 在客户端可以使用connect进行连接
这里的connect并没有和服务端进行三次握手,只是在内核中记录了服务端的地址信息和端口,所以可以直接使用send不用指出服务端的地址等信息了。而且调用connect可以保证只是接受来自服务端发送的数据,不接受其他的信息
- 多次绑定同一个端口
第二个端口起作用
域名
- gethostent获取所有
- gethostbuname获取某一个
-
在Linux中的,位于
/etc/hosts
-
gethost.c
#include <stdio.h>
#include<netdb.h>
#include<stdlib.h>
#include<memory.h>void out_addr(struct hostent *h)
{printf("hostbyname: %s\n",h->h_name);printf("addrtype: %s\n",h->h_addrtype==AF_INET ? "IPV4" : "IPV6");char ip[16];memset(ip,0,sizeof(ip));inet_ntop(h->h_addrtype,h->h_addr_list[0],ip,sizeof(ip));printf("ip addrress : %s\n",ip);int i = 0;while(h->h_aliases[i] != NULL){printf("alias : %s \n",h->h_aliases[i]);i++;}
}int main(int argc,char *argv[])
{if(argc<2){printf("usage %s host \n",argv[0]);exit(1);}struct hostent *h;h = gethostbyname(argv[1]);if(h!=NULL){out_addr(h);}else{printf("no %s exits\n",argv[1]);}return 0;endhostent();
}
注意gethostbyname应该避免在多线程下使用。
- 使用gethostent
#include <stdio.h>
#include<netdb.h>
#include<stdlib.h>
#include<memory.h>void out_addr(struct hostent *h)
{printf("hostbyname: %s\n",h->h_name);printf("addrtype: %s\n",h->h_addrtype==AF_INET ? "IPV4" : "IPV6");char ip[16];memset(ip,0,sizeof(ip));inet_ntop(h->h_addrtype,h->h_addr_list[0],ip,sizeof(ip));printf("ip addrress : %s\n",ip);int i = 0;while(h->h_aliases[i] != NULL){printf("alias : %s \n",h->h_aliases[i]);i++;}
}int main(int argc,char *argv[])
{if(argc<2){printf("usage %s host \n",argv[0]);exit(1);}struct hostent *h;while((h=gethostent()) != NULL){if(!strcmp(argv[1],h->h_name)){out_addr(h);exit(0);}else{int i = 0;while(h->h_aliases[i] != NULL){if(!strcmp(argv[1],h->h_aliases[i])){out_addr(h);exit(0);}i++;}}}endhostent();return 0;
}
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netdb.h>
#include<sys/socket.h>
#include<memory.h>
#include<unistd.h>int is_host(struct hostent *host,char *name)
{if(!strcmp(host->h_name,name))return 1;int i =0;while(host->h_aliases[i] != NULL){if(!strcmp(host->h_aliases[i],name))return 1;i++;}
}
unsigned int get_ip_by_name(char *name)
{unsigned int ip = 0;struct hostent *host;while((host = gethostent())!=NULL){if(is_host(host,name)){memcpy(&ip,host->h_addr_list[0],4);break;}}endhostent();return ip;
}int main(int argc,char *argv[])
{if(argc<3){printf("usgae : %s ip port \n",argv[0]);exit(1);}/*步骤1:创建socket* */int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){perror("socket error");exit(1);}/*步骤2:调用recvfrom和sendto等函数* 和服务器进行双向通信* */struct sockaddr_in serveraddr;memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));unsigned int ip = get_ip_by_name(argv[1]);if(ip!=0){serveraddr.sin_addr.s_addr = ip;}else{inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);}//inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr); //ipchar buffer[1024] = "hello iotek";// 向服务器端发送数据报文if(sendto(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("sendto error");exit(1);}else{//接受服务器发送的报文memset(buffer,0,sizeof(buffer));if(recv(sockfd,buffer,sizeof(buffer),0)<0){perror("recv error");exit(1);}else{printf("%s",buffer);}close(sockfd);}return 0;
}
网络高级编程
广播
套接字选项
TCP不支持广播,只有UDP才能支持广播
缓存区
- receiver.c
#include <stdio.h>
#include<netdb.h>
#include<sys/socket.h>
#include<string.h>
#include<signal.h>
#include<stdlib.h>int sockfd;void sig_handler(int signo)
{if(signo == SIGINT){printf("receiver will exited");close(sockfd);exit(1);}
}int main(int argc,char *argv[])
{if(argc<2){fprintf(stderr,"usage : %s port \n",argv[0]);exit(1);}if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint errro");exit(1);}sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){perror("socket error");exit(1);}struct sockaddr_in serveraddr;memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[1]));serveraddr.sin_addr.s_addr = INADDR_ANY;if(bind(sockfd,(struct sockadd*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}char buffer[1024];struct sockaddr_in clientaddr;socklen_t len = sizeof(clientaddr);while(1){memset(buffer,0,sizeof(buffer));memset(&clientaddr,0,sizeof(clientaddr));if(recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&clientaddr,&len)<0){perror("recvfrom error");exit(1);}else{char ip[16];inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ip,sizeof(ip));int port = ntohs(clientaddr.sin_port); printf("%s{%d}:%s\n",ip,port,buffer);}}return 0;
}
- broadcast.c
#include <stdio.h>
#include<stdlib.h>
#include<string.h>
#include<netdb.h>
#include<sys/socket.h>int main(int argc,char * argv[])
{if(argc<3){fprintf(stderr,"usage: %s ip port\n",argv[0]);exit(1);}int sockfd = socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){perror("sockdt error");exit(1);}int opt = 1;// 采用广播地址发送setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&opt,sizeof(opt));struct sockaddr_in serveraddr;memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET;serveraddr.sin_port = htons(atoi(argv[2]));inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);printf("I will broadcast....\n");char *info = "helo yangpipi...";size_t size = strlen(info)*sizeof(char);if(sendto(sockfd,info,size,0,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("sendto error");exit(1);}else{printf("boradcast success\n");}close(sockfd);return 0;
多路复用之fcntl
- server.c
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<pthread.h>
#include<fcntl.h>
#include"vector_fd.h"VectorFD *vfd;
int sockfd;void sig_handler(int signo)
{if(signo==SIGINT){printf("server close----\n");/*步骤6:* 关闭socket* */close(sockfd);// 销毁动态数组destroy_vector_fd(vfd);exit(1);}
}/**fd对应于某个连接的客户端,和某个连接的客户端进行双向通信(非阻塞方式)* */
void do_service(int fd)
{// 和客户端进行读写操作(双向通信)char buff[512];while(1){memset(buff,0,sizeof(buff));// 以非阻塞方式读,读不到数据就返回了,直接服务于下一个客户端,因此不需要判断size<0的情况size_t size=read(fd,buff,sizeof(buff));if(size==0){char info[] = "client closed";write(STDOUT_FILENO,info,sizeof(info));// 从动态数组中删除对应的fdremove_fd(vfd,fd);// 关闭对应的客户端的socketclose(fd);}else if(size > 0){write(STDOUT_FILENO,buff,sizeof(buff));if(write(fd,buff,size)<size){if(errno==EPIPE)// 客户端关闭连接{perror("write error");remove_fd(vfd,fd);close(fd);}}}}
}void * th_fn(void *arg)
{int i;while(1){// 遍历动态数中的socket描述符for(int i = 0;i<vfd->counter;i++){do_service(get_fd(vfd,i));}}return (void*)0;
}void out_addr(struct sockaddr_in *clientaddr)
{char ip[16];memset(ip,0,sizeof(ip));int port = ntohs(clientaddr->sin_port);inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));printf("%s(%d) connected!\n",ip,port);
}
int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}// 绑定一个信号,ctrl+c 终止服务端if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint error");exit(1);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:调用bind函数将socket和地址(ip和port)进行绑定 * */// 专用地址,绑定的时候在强转为通用地址struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1]));// 字符串-> 整型,主机字节序->网络字节序serveraddr.sin_addr.s_addr = INADDR_ANY;//响应所有请求if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}/**步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求将接受到的客户端连接请求放置到对应的队列中第二个参数:10:队列的长度* */if(listen(sockfd,10)<0){perror("listen error");exit(1);}/**步骤4:调用accept函数,从队列中获得一个客户端的请求连接* 并返回一个新的socket描述符*注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端的连接* */// 创建放置套接字描述符fd的动态数组vfd = create_vector_fd();// 设置线程的分离属性pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);int err;pthread_t th; if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");exit(1);}pthread_attr_destroy(&attr);/** 1) 主控线程获得客户端的连接,将新的socket描述符放置到动态数组中* 2) 启动的子线程负责遍历动态数组中socket描述符并和对应的客户端进行双向通信(采用非阻塞方式读写)** */struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);while(1){int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);if(fd<0){perror("accept error");continue;}out_addr(&clientaddr);// 将读写方式修改为非阻塞方式int val;fcntl(fd,F_GETFL,&val);val |= O_NONBLOCK;fcntl(fd,F_SETFL,val); // 将返回新的socket描述符加入到动态数组中add_fd(vfd,fd);}return 0;
- client.c
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:创建socket* */struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[2]));// 字符串-> 整型,主机字节序->网络字节序inet_pton(AF_INET,argv[1],&serveraddr.sin_addr.s_addr);if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("connect error");exit(1);}// 步骤3:调用IO函数,和服务器进行双向通讯char buff[512];size_t size;char * prompt = ">" ;while(1){memset(buff,0,sizeof(buff));write(STDOUT_FILENO,prompt,1);size = read(STDIN_FILENO,buff,sizeof(buff));if(size<0) {continue;}buff[size-1] = '\0';if(write(sockfd,buff,sizeof(buff))<0){perror("write msg error");continue;}else{if(read(sockfd,buff,sizeof(buff))<0){perror("read msg error");continue;}else{printf("%s\n",buff);}}}// 步骤4:关闭socketclose(sockfd);return 0;
}
多路复用之select
- echo_tcp_sever_select.c
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<pthread.h>
#include<fcntl.h>
#include"vector_fd.h"VectorFD *vfd;
int sockfd;void sig_handler(int signo)
{if(signo==SIGINT){printf("server close----\n");/*步骤6:* 关闭socket* */close(sockfd);// 销毁动态数组destroy_vector_fd(vfd);exit(1);}
}/**fd对应于某个连接的客户端,和某个连接的客户端进行双向通信(非阻塞方式)* */
void do_service(int fd)
{// 和客户端进行读写操作(双向通信)char buff[512];while(1){memset(buff,0,sizeof(buff));// 以非阻塞方式读,读不到数据就返回了,直接服务于下一个客户端,因此不需要判断size<0的情况size_t size=read(fd,buff,sizeof(buff));if(size==0){//char info[] = "client closed";//write(STDOUT_FILENO,info,sizeof(info));printf("client closed\n"); // 这里由于内核设置,所以可以使用带缓存的IO// 从动态数组中删除对应的fdremove_fd(vfd,fd);// 关闭对应的客户端的socketclose(fd);}else if(size > 0){//write(STDOUT_FILENO,buff,sizeof(buff));printf("%s\n",buff);if(write(fd,buff,size)<size){if(errno==EPIPE)// 客户端关闭连接{perror("write error");remove_fd(vfd,fd);close(fd);}}}}
}/*遍历出动态数组中所有的描述符并加入到描述符集set中* 同时此函数返回动态数组中最大的那个描述符* */
int add_set(fd_set *set)
{FD_ZERO(set); // 清空描述符int max_fd = vfd->fd[0];for(int i = 0;i < vfd->fd[0];i++){int fd = get_fd(vfd,i);if(fd>max_fd){max_fd = fd;}FD_SET(fd,set);//将fd加入到描述符集中}return max_fd;
}
void * th_fn(void *arg)
{struct timeval t;t.tv_sec = 2;t.tv_usec = 0;int n = 0;int maxfd;fd_set set; // 描述符集maxfd = add_set(&set);/*调用select函数会阻塞,委托内核去检查传入的描述符是否准备好,* 如有则返回准备好的描述符,* 超时则返回0* 第一个参数为描述符集中描述符的范围(最大描述符+1)* */while((n=select(maxfd+1,&set,NULL,NULL,&t))>=0){if(n>0){/*检测那些描述符准备好,并和这些准备好的描述符对应的客户端进行数据的双向通信* */for(int i=0;i<vfd->counter;i++){int fd = get_fd(vfd,i);if(FD_ISSET(fd,&set)){do_service(fd);}}}//超时// 重新设置时间和清空描述符集t.tv_sec = 2;t.tv_usec = 0;// 重新遍历动态数组中最新的描述符放置到描述符集中maxfd = add_set(&set);}
}void out_addr(struct sockaddr_in *clientaddr)
{char ip[16];memset(ip,0,sizeof(ip));int port = ntohs(clientaddr->sin_port);inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));printf("%s(%d) connected!\n",ip,port);
}
int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(0);}// 绑定一个信号,ctrl+c 终止服务端if(signal(SIGINT,sig_handler)==SIG_ERR){perror("signal sigint error");exit(1);}/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("sockek error");exit(1);}/** 步骤2:调用bind函数将socket和地址(ip和port)进行绑定 * */// 专用地址,绑定的时候在强转为通用地址struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1]));// 字符串-> 整型,主机字节序->网络字节序serveraddr.sin_addr.s_addr = INADDR_ANY;//响应所有请求if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){perror("bind error");exit(1);}/**步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求将接受到的客户端连接请求放置到对应的队列中第二个参数:10:队列的长度* */if(listen(sockfd,10)<0){perror("listen error");exit(1);}/**步骤4:调用accept函数,从队列中获得一个客户端的请求连接* 并返回一个新的socket描述符*注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端的连接* */// 创建放置套接字描述符fd的动态数组vfd = create_vector_fd();// 设置线程的分离属性pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);int err;pthread_t th; if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0){perror("pthread create error");exit(1);}pthread_attr_destroy(&attr);/** 1) 主控线程获得客户端的连接,将新的socket描述符放置到动态数组中* 2) * a) 启动的子线程调用select函数委托内核去检查传入到select中的描述符是否准备好* b) 利用FD_ISSET来找出准备好的那些描述符并和对应的客户端进行双向通信(非阻塞)** */struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);while(1){int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);if(fd<0){perror("accept error");continue;}out_addr(&clientaddr);// 将返回新的socket描述符加入到动态数组中add_fd(vfd,fd);}return 0;
}
守护进程
- 编程步骤
- 出错处理
#include<stdio.h>
#include<stdlib.h>
#include<netdb.h>
#include<sys/socket.h>
#include<unistd.h>
#include<string.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<pthread.h>
#include<fcntl.h>
#include<syslog.h>
#include<sys/stat.h>
#include"vector_fd.h"VectorFD *vfd;
int sockfd;/**fd对应于某个连接的客户端,和某个连接的客户端进行双向通信(非阻塞方式)* */
void do_service(int fd)
{// 和客户端进行读写操作(双向通信)char buff[512];while(1){memset(buff,0,sizeof(buff));// 以非阻塞方式读,读不到数据就返回了,直接服务于下一个客户端,因此不需要判断size<0的情况size_t size=read(fd,buff,sizeof(buff));if(size==0){char info[] = "client closed\n";syslog(LOG_DEBUG,"client closed");//printf("client closed\n"); // 这里由于内核设置,所以可以使用带缓存的IO// 从动态数组中删除对应的fdremove_fd(vfd,fd);// 关闭对应的客户端的socketclose(fd);}else if(size > 0){//write(STDOUT_FILENO,buff,sizeof(buff));// printf("%s\n",buff);syslog(LOG_DEBUG,"%s\n",buff);if(write(fd,buff,size)<size){if(errno==EPIPE)// 客户端关闭连接{syslog(LOG_DEBUG,"write:%s\n",strerror(errno));remove_fd(vfd,fd);close(fd);}}}}
}/*遍历出动态数组中所有的描述符并加入到描述符集set中* 同时此函数返回动态数组中最大的那个描述符* */
int add_set(fd_set *set)
{FD_ZERO(set); // 清空描述符int max_fd = vfd->fd[0];for(int i = 0;i < vfd->fd[0];i++){int fd = get_fd(vfd,i);if(fd>max_fd){max_fd = fd;}FD_SET(fd,set);//将fd加入到描述符集中}return max_fd;
}
void * th_fn(void *arg)
{struct timeval t;t.tv_sec = 2;t.tv_usec = 0;int n = 0;int maxfd;fd_set set; // 描述符集maxfd = add_set(&set);/*调用select函数会阻塞,委托内核去检查传入的描述符是否准备好,* 如有则返回准备好的描述符,* 超时则返回0* 第一个参数为描述符集中描述符的范围(最大描述符+1)* */while((n=select(maxfd+1,&set,NULL,NULL,&t))>=0){if(n>0){/*检测那些描述符准备好,并和这些准备好的描述符对应的客户端进行数据的双向通信* */for(int i=0;i<vfd->counter;i++){int fd = get_fd(vfd,i);if(FD_ISSET(fd,&set)){do_service(fd);}}}//超时// 重新设置时间和清空描述符集t.tv_sec = 2;t.tv_usec = 0;// 重新遍历动态数组中最新的描述符放置到描述符集中maxfd = add_set(&set);}
}void out_addr(struct sockaddr_in *clientaddr)
{char ip[16];memset(ip,0,sizeof(ip));int port = ntohs(clientaddr->sin_port);inet_ntop(AF_INET,&clientaddr->sin_addr.s_addr,ip,sizeof(ip));syslog(LOG_DEBUG,"%s(%d) connected!\n",ip,port);
}
int main(int argc,char *argv[])
{// 参数:服务器端绑定的端口if(argc<2){printf("usage :%s #port\n",argv[0]);exit(1);}// 守护进程编程的5个步骤// 步骤1:创建屏蔽字为0umask(0);// 步骤2:调用fork函数创建子进程,然后父进程退出pid_t pid = fork();if(pid>0) exit(0);//步骤3:调用setsid函数创建一个新的会话setsid();//步骤4:将当前工作目录更改为根目录chdir("/");//步骤5:关闭不需要的文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);//打开系统日志服务的一个连接openlog(argv[0],LOG_PID,LOG_SYSLOG);/* * 步骤1. 创建socket,套接字socket创建在内核,是一个结构体* AF_INET : IPV4* SOCK_STREAM:TCP协议*/sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){syslog(LOG_DEBUG,"socket:%s\n",strerror(errno));exit(1);}/** 步骤2:调用bind函数将socket和地址(ip和port)进行绑定 * */// 专用地址,绑定的时候在强转为通用地址struct sockaddr_in serveraddr; memset(&serveraddr,0,sizeof(serveraddr));serveraddr.sin_family = AF_INET; // IPV4serveraddr.sin_port = htons(atoi(argv[1]));// 字符串-> 整型,主机字节序->网络字节序serveraddr.sin_addr.s_addr = INADDR_ANY;//响应所有请求if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<0){// 将日志信息写入到系统日志文件中(/var/log/syslog)syslog(LOG_DEBUG,"bind:%s\n",strerror(errno));exit(1);}/**步骤3:调用listen函数启动监听(指定port监听)通知系统去接受来自客户端的连接请求将接受到的客户端连接请求放置到对应的队列中第二个参数:10:队列的长度* */if(listen(sockfd,10)<0){syslog(LOG_DEBUG,"listen:%s\n",strerror(errno));exit(1);}/**步骤4:调用accept函数,从队列中获得一个客户端的请求连接* 并返回一个新的socket描述符*注意:若没有客户端连接,调用此函数后会阻塞直到获得一个客户端的连接* */// 创建放置套接字描述符fd的动态数组vfd = create_vector_fd();// 设置线程的分离属性pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);int err;pthread_t th; if((err=pthread_create(&th,&attr,th_fn,(void*)0))!=0){syslog(LOG_DEBUG,"pthread:%s\n",strerror(errno));exit(1);}pthread_attr_destroy(&attr);/** 1) 主控线程获得客户端的连接,将新的socket描述符放置到动态数组中* 2) * a) 启动的子线程调用select函数委托内核去检查传入到select中的描述符是否准备好* b) 利用FD_ISSET来找出准备好的那些描述符并和对应的客户端进行双向通信(非阻塞)** */struct sockaddr_in clientaddr;socklen_t clientaddr_len = sizeof(clientaddr);while(1){int fd = accept(sockfd,(struct sockaddr*)&clientaddr,&clientaddr_len);if(fd<0){syslog(LOG_DEBUG,"accept:%s\n",strerror(errno));continue;}out_addr(&clientaddr);// 将返回新的socket描述符加入到动态数组中add_fd(vfd,fd);}return 0;
}
相关文章:

8. 网络编程
网络的基本概念 TCP/IP协议概述 OSI和TCP/IP模型 socket(套接字) 创建socket 字节序 字节序转换函数 通用地址结构 因特网地址结构 IPV4地址族和字符地址间的转换(点分十进制->网络字节序) 填写IPV4地址族结构案例 掌握TCP协议网络基础编程 相关函数 …...

C++并发编程指南04
文章目录 共享数据的问题3.1.1 条件竞争双链表的例子条件竞争示例恶性条件竞争的特点 3.1.2 避免恶性条件竞争1. 使用互斥量保护共享数据结构2. 无锁编程3. 软件事务内存(STM) 总结互斥量与共享数据保护3.2.1 互斥量使用互斥量保护共享数据示例代码&…...

常见的同态加密算法收集
随着对crypten与密码学的了解,我们将逐渐深入学习相关知识。今天,我们将跟随同态加密的发展历程对相关算法进行简单的收集整理 。 目录 同态加密概念 RSA算法 ElGamal算法 ELGamal签名算法 Paillier算法 BGN方案 Gentry 方案 BGV 方案 BFV 方案…...

深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用
title: 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用 date: 2025/1/26 updated: 2025/1/26 author: cmdragon excerpt: 在现代数据库管理系统中,索引技术是提高查询性能的重要手段。当数据量不断增长时,如何快速、有效地访问这些数据成为了数据库设计的核…...

记录 | Docker的windows版安装
目录 前言一、1.1 打开“启用或关闭Windows功能”1.2 安装“WSL”方式1:命令行下载方式2:离线包下载 二、Docker Desktop更新时间 前言 参考文章:Windows Subsystem for Linux——解决WSL更新速度慢的方案 参考视频:一个视频解决D…...

AI智慧社区--生成验证码
接口文档: 从接口文档中可以得知的信息: 代码的返回格式为json格式,可以将Controlller换为RestController前端发起的请求为Get请求,使用注解GetMapping通过返回的数据类型,定义一个返回类型Result package com.qcby.…...

2501,20个窗口常用操作
窗口是屏幕上的一个矩形区域.窗口分为3种:覆盖窗口,弹窗和子窗口.每个窗口都有由系统绘画的"非客户区"和应用绘画的"客户区". 在MFC中,CWnd类为各种窗口提供了基类. 1,通过窗柄取得CWnd指针 可调用Cwnd::FromHandle函数,通过窗柄取得Cwnd指针. void CD…...

【gopher的java学习笔记】一文讲懂controller,service,mapper,entity是什么
刚开始上手Java和Spring时,就被controller,service,mapper,entity这几个词搞懵了,搞不懂这些究竟代表什么,感觉使用golang开发的时候也没太接触过这些名词啊~ 经过两三个月的开发后,逐渐搞懂了这…...

消息队列篇--通信协议篇--STOMP(STOMP特点、格式及示例,WebSocket上使用STOMP,消息队列上使用STOMP等)
STOMP(Simple Text Oriented Messaging Protocol,简单面向文本的消息传递协议)是一种轻量级、基于文本的协议,旨在为消息代理(消息队列)和客户端之间的通信(websocket)提供一种简单的…...

基于SpringBoot的租房管理系统(含论文)
基于SpringBoot的租房管理系统是一个集订单管理、房源信息管理、屋主申诉处理、用户反馈等多项功能于一体的系统。该系统通过SpringBoot框架开发,拥有完善的管理员后台、屋主管理模块、用户功能模块等,适用于房地产租赁平台或中介公司进行日常管理与运营…...

提升企业内部协作的在线知识库架构与实施策略
内容概要 在当前快速变化的商业环境中,企业对于提升内部协作效率的需求愈显迫切。在线知识库作为信息存储与共享的平台,成为了推动企业数字化转型的重要工具。本文将深入探讨如何有效打造与实施在线知识库,强调架构设计、知识资产分类管理及…...

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令
文章目录 指令格式(重点)1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…...

Jackson中@JsonTypeId的妙用与实例解析
在日常的Java开发中,Jackson库是处理JSON数据的常用工具。其中,JsonTypeId注解是一个非常实用的功能,它可以帮助我们更好地控制多态类型信息在序列化过程中的表现。今天,我们就来深入探讨一下JsonTypeId的用法,并通过具…...

Ubuntu 顶部状态栏 配置,gnu扩展程序
顶部状态栏 默认没有配置、隐藏的地方 安装使用Hide Top Bar 或Just Perfection等进行配置 1 安装 sudo apt install gnome-shell-extension-manager2 打开 安装的“扩展管理器” 3. 对顶部状态栏进行配置 使用Hide Top Bar 智能隐藏,或者使用Just Perfection 直…...

Java---入门基础篇(上)
前言 本片文章主要讲了刚学Java的一些基础内容,例如注释,标识符,数据类型和变量,运算符,还有逻辑控制等,记录的很详细,带你从简单的知识点再到练习题.如果学习了c语言的小伙伴会发现,这篇文章的内容和c语言大致相同. 而在下一篇文章里,我会讲解方法和数组的使用,也是Java中基础…...

Linux C++
一、引言 冯诺依曼架构是现代计算机系统的基础,它的提出为计算机的发展奠定了理论基础。在学习 C 和 Linux 系统时,理解冯诺依曼架构有助于我们更好地理解程序是如何在计算机中运行的,包括程序的存储、执行和资源管理。这对于编写高效、可靠的…...
gradio 合集
知识点 1:基本 Chatbot 创建 import gradio as gr 定义历史记录 history [gr.ChatMessage(role“assistant”, content“How can I help you?”), gr.ChatMessage(role“user”, content“What is the weather today?”)] 使用历史记录创建 Chatbot 组件 ch…...

996引擎 - NPC-动态创建NPC
996引擎 - NPC-动态创建NPC 创建脚本服务端脚本客户端脚本添加自定义音效添加音效文件修改配置参考资料有个小问题,创建NPC时没有控制朝向的参数。所以。。。自己考虑怎么找补吧。 多重影分身 创建脚本 服务端脚本 Mir200\Envir\Market_Def\test\test001-3.lua -- NPC八门名…...

论文阅读(十三):复杂表型关联的贝叶斯、基于系统的多层次分析:从解释到决策
1.论文链接:Bayesian, Systems-based, Multilevel Analysis of Associations for Complex Phenotypes: from Interpretation to Decision 摘要: 遗传关联研究(GAS)报告的结果相对稀缺,促使许多研究方向。尽管关联概念…...

代码随想录算法训练营第三十九天-动态规划-198. 打家劫舍
动规五部曲 dp[i]表示在下标为i的房间偷或不偷与前面所偷之和所能获得的最大价值递推公式:dp[i] std::max(dp[i - 2] nums[i], dp[i - 1])初始化:要给dp[0]与dp[1]来给定初始值,因为递推公式有-1与-2。dp[0] nums[0], dp[1] std::max(num…...

CF1098F Ж-function
【题意】 给你一个字符串 s s s,每次询问给你 l , r l, r l,r,让你输出 s s s l , r sss_{l,r} sssl,r中 ∑ i 1 r − l 1 L C P ( s s i , s s 1 ) \sum_{i1}^{r-l1}LCP(ss_i,ss_1) ∑i1r−l1LCP(ssi,ss1)。 【思路】 和前一道题一样&#…...

Python 函数魔法书:基础、范例、避坑、测验与项目实战
Python 函数魔法书:基础、范例、避坑、测验与项目实战 内容简介 本系列文章是为 Python3 学习者精心设计的一套全面、实用的学习指南,旨在帮助读者从基础入门到项目实战,全面提升编程能力。文章结构由 5 个版块组成,内容层层递进…...

vim交换文件的作用
1.数据恢复:因为vim异常的退出,使用交换文件可以恢复之前的修改内容。 2.防止多人同时编辑:vim检测到交换文件的存在,会给出提示,以避免一个文件同时被多人编辑。 (vim交换文件的工作原理:vim交换文件的工作…...

[NOI1995] 石子合并
[NOI1995] 石子合并 题目描述 在一个圆形操场的四周摆放 N N N 堆石子,现要将石子有次序地合并成一堆,规定每次只能选相邻的 2 2 2 堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 试设计出一个算法,计算出将 …...

真正的智能与那只蝴蝶
“蝴蝶效应”可以展开为对智能本质与大算力关系的追问,其中“蝴蝶”作为隐喻可能指向多重维度——从混沌理论的“蝴蝶效应”到庄子“物我两忘”的蝴蝶之梦。这种并置本身暗示了智能与宇宙秩序、认知边界之间的深刻张力。以下从三个层面展开分析:一、混沌…...

C++小病毒-1.0勒索(更新次数:2)
内容供学习使用,不得转卖,代码复制后请1小时内删除,此代码会危害计算机安全,谨慎操作 在C20环境下,并在虚拟机里运行此代码!,病毒带来后果自负! 使用时请删除在main()里的注释,并修改位置至C:\\(看我代码注释)//可以改成WIN Main() #include <iostream> #i…...

Node.js 的底层原理
Node.js 的底层原理 1. 事件驱动和非阻塞 I/O Node.js 基于 Chrome V8 引擎,使用 JavaScript 作为开发语言。它采用事件驱动和非阻塞 I/O 模型,使其轻量且高效。通过 libuv 库实现跨平台的异步 I/O,包括文件操作、网络请求等。 2. 单线程事…...

基于Django的豆瓣影视剧推荐系统的设计与实现
【Django】基于Django的豆瓣影视剧推荐系统的设计与实现(完整系统源码开发笔记详细部署教程)✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 该系统采用了Python作为后端开发语言,采用Django作为后端架构,结…...

P10638 BZOJ4355 Play with sequence Solution
Description 给定 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1,a2,⋯,an),有 m m m 个操作,分以下三种: assign ( l , r , k ) \operatorname{assign}(l,r,k) assign(l,r,k):对每个 i ∈ [ l , r ] i \…...

MySQL误删数据怎么办?
文章目录 1. 从备份恢复数据2. 通过二进制日志恢复数据3. 使用数据恢复工具4. 利用事务回滚恢复数据5. 预防误删数据的策略总结 在使用MySQL进行数据管理时,误删数据是一个常见且具有高风险的操作。无论是因为操作失误、系统故障,还是不小心执行了删除命…...