服务器模型 setsockopt 网络超时检测 广播组播和unix域套接字 5.23

news/2024/11/9 0:47:10/

四.服务器模型

1.循环服务器

TCP服务器
TCP服务器端运行后等待客户端的连接请求。
TCP服务器接受一个客户端的连接后开始处理,完成了客户的所有请求后断开连接。 
TCP循环服务器一次只能处理一个客户端的请求。
只有在当前客户的所有请求都完成后,服务器才能处理下一个客户的连接/服务请求。
缺点:如果某个客户端一直占用服务器资源,那么其它的客户端都不能被处理。TCP服务器一般很少采用循环服务器模型。 流程如下:socket(...); bind(...); listen(...); while(1) { connfd = accept(...); while(1) { recv(...); process(...); send(...); } close(connfd); }

2.并发服务器

为了弥补TCP循环服务器的缺陷,人们又设计了并发服务器的模型。
并发服务器的设计思想是服务器接受客户端的连接请求后创建子进程来为客户端服务 
TCP并发服务器可以避免TCP循环服务器中客户端独占服务器的情况。
为了响应客户机的请求,服务器要创建子进程来处理。 如果有多个客户端的话,服务器端需要创建多个子进程。过多的子进程会影响服务器端的运行效率流程如下:
1. 多线程:void *accept_client(void *arg){//int connfd = *((int*)arg);int connfd = (int)arg;while(1)   //和客户端通信部分{ recv(...); process(...); send(...); }close(connfd);pthread_exit(NULL);}sockfd = socket(...); bind(...); listen(...); while(1) { int connfd = accept(...); //pthread_create(&thread, NULL, accept_client, (void*)&connfd);pthread_create(&thread, NULL, accept_client, (void*)connfd);pthread_detach(thread);}close(sockfd);2. 多进程:void handler(int sig){while (waitpid(-1, NULL, WNOHANG)>0);}sockfd = socket();bind();listen();signal(SIGCHLD, handler);while (1){connfd = accept();pid = fork();if (0 == pid)  //子进程{close(sockfd);while (1)  {//与客户端通信}close(connfd);exit(0);}close(connfd); }close(sockfd);/
多进程服务器/*===============================================*   文件名称:server.c*   创 建 者:memories *   创建日期:2023年05月18日*   描    述:================================================*/这里重点提一下SIGCHLD信号(17号信号),对于父进程而言,一般要对它的子进程进行等待,以防止子进程变为僵尸进程,而导致内存泄漏问题,但是对于父进程的等待方式存在两种:阻塞式等待和非阻塞式等待,可以通过wait和waitpid这两个系统调用来实现,对于父进程而言,阻塞式等待让父进程停下自己手头上的事情,专心的等待子进程退出,这严重影响了父进程的工作;而对于非阻塞式等待,父进程每隔一段时间都要轮循一下,看看子进程有没有退出,这也让我们的程序变的复杂,所以我们就想能不能让父进程对于子进程的等待变成子进程退出了就通知父进程进行回收,也就是让对子进程的等待变成异步等待,不干扰父进程的正常工作呢?这里就得了解一下父进程是如何知道子进程已经退出的,也就是我们的SIGCHLD这个信号的作用,当子进程退出时,会向父进程发送SIGCHLD信号,而父进程就是根据是否收到这个信号来判断子进程是否退出的while(waitpid(-1,NULL,WNOHANG)>0);一次结束可以回收多个尸体(假如有五个客户端同时停止,waitpid(-1,NULL,WNOHANG)一次只会回收一个尸体,因为SIGCHLD只会执行一次,在第二次回收第二剧尸体时发现SIGCHLD已经被调用,就不会在第二次调用。。在这里采用while循环重复调用,waitpid的返回值若>0则资源还没有回收完,=0子进程资源已经回收完)    #include <stdio.h>
#include "ip.h"void handler(int arg)
{while(waitpid(-1,NULL,WNOHANG)>0);
}int main(int argc, char *argv[])
{ //1.创建套接字int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("socket");return -1;}printf("socket-----------------\n");//2.绑定本机地址和端口//用IPv4结构体struct sockaddr_in srvaddr;memset(&srvaddr,0,sizeof(srvaddr));srvaddr.sin_family = AF_INET;//指定地址族为IPv4的地址srvaddr.sin_port = htons(54320);//端口号srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将点分式的字符串转化32位的网络字节序//设置重用本地地址和端口int on = 1;if(0 > setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))){perror("setsockopt");return -1;}if(0 > bind(sockfd,(struct sockaddr*)&srvaddr,sizeof(srvaddr))){perror("bind");return -1;}printf("bind-----------------\n");//3.设置监听套接字if(0>listen(sockfd,5))//必须写大于1的整数{perror("listen");return -1;}printf("listen-----------------\n");signal(SIGCHLD,handler);//信号灯集,异步通信while(1){//4.接受客户端的连接,并生成通信套接字int connfd = accept(sockfd,NULL,NULL);if(connfd < 0){perror("accept");return -1;}printf("accept-----------------\n");pid_t pid=fork();if(pid<0){perror("fork");return -1;}else if(pid == 0){close(sockfd);//设置超时struct timeval tv={1};if(0 > setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))){perror("setsockopt");return -1;}//5.与客户端通信int ret;char buf[1024];while(1){memset(buf,0,sizeof(buf));ret = read(connfd,buf,sizeof(buf));if(ret < 0){perror("read");continue;}else if(ret == 0){printf("write close\n");break;}printf("recv:%s\n",buf);if(0 > write(connfd,buf,ret)){perror("write");return -1;}}}elseclose(connfd);}return 0;
}//
多线程/*===============================================*   文件名称:server.c*   创 建 者:memories *   创建日期:2023年05月18日*   描    述:================================================*/
#include <stdio.h>
#include "ip.h"
int *process1(void *arg);
void handler(int arg);int main(int argc, char *argv[])
{ //1.创建套接字int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd<0){perror("socket");return -1;}printf("socket-----------------\n");//2.绑定本机地址和端口//用IPv4结构体struct sockaddr_in srvaddr;memset(&srvaddr,0,sizeof(srvaddr));srvaddr.sin_family = AF_INET;//指定地址族为IPv4的地址srvaddr.sin_port = htons(5420);//端口号srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将点分式的字符串转化32位的网络字节序int on = 1;if(0 > setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))){perror("setsockopt");return -1;}if(0 > bind(sockfd,(struct sockaddr*)&srvaddr,sizeof(srvaddr))){perror("bind");return -1;}printf("bind-----------------\n");//3.设置监听套接字if(0>listen(sockfd,5))//必须写大于1的整数{perror("listen");return -1;}printf("listen-----------------\n");/SAI cliaddr;socklen_t addrlen = sizeof(cliaddr);while(1){//4.接受客户端的连接,并生成通信套接字int connfd = accept(sockfd,(SA *)&cliaddr,&addrlen);if(connfd < 0){perror("accept");return -1;}printf("accept success:%s %hu\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));pthread_t tid,tid2;if(0 != pthread_create(&tid,NULL,(void *)process1,(void *)&connfd)){printf("pthread creat failed\n");return -1;}//线程分离if(0 != pthread_detach(tid))break;/*if(0 > pthread_create(&tid2,NULL,process2,(void *)&connfd)){printf("pthread2 creat failed\n");return -1;}*/}//6.关闭套接字close(sockfd);return 0;
} void handler(int arg)
{printf("timeout\n");
}int *process1(void *arg)
{int connfd = *((int *)arg);struct timeval tv={1};if(0 > setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))){perror("setsockopt");return NULL;}/*struct sigaction act;sigaction(SIGALRM,NULL,&act);act.sa_handler = handler;act.sa_flags &= ~SA_RESTART; sigaction(SIGALRM,&act,NULL);*///5.与客户端通信int ret;char buf[1024];while(1){//  alarm(1);memset(buf,0,sizeof(buf));ret = read(connfd,buf,sizeof(buf));if(ret < 0){perror("read");continue;}else if(ret == 0){printf("write close\n");break;}printf("recv:%s\n",buf);if(0 > write(connfd,buf,ret)){perror("write");return NULL;}}close(connfd);pthread_exit(NULL);}

五.进阶篇

应用层知道底层的信息:网络信息检索函数

1.setsockopt()

#include <sys/types.h>          /* See NOTES */#include <sys/socket.h>int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);作用:获取/设置套接字属性
参数:sockfd --- 套接字文件描述符level --- 选项所在层1)SOL_SOCKET:通用套接字选项2)IPPROTO_IP:IP选项3)IPPROTO_TCP:TCP选项optname --- 选项的名字optval --- 选项的值optlen --- 选项的长度返回值:成功:0失败:-1,并设置error/*设置重用本地地址和端口*/
int on = 1;
if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
{perror("setsockopt");return -1;
}
/*===============================================
*   文件名称:server.c
*   创 建 者:     
*   创建日期:2023年05月18日
*   描    述:
================================================*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{ /*1. 创建套接字*/int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket");return -1;}printf("socket..............\n");/*设置重用本地地址和端口*/int on = 1;if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))){perror("setsockopt");return -1;}/*2. 绑定本机地址和端口*/struct sockaddr_in srvaddr;memset(&srvaddr, 0, sizeof(srvaddr));srvaddr.sin_family 	= AF_INET;  //地址族srvaddr.sin_port 	= htons(6666); //端口号srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);  //IP地址if (0 > bind(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) {perror("bind");return -1;}printf("bind................\n");/*3. 设置监听套接字*/if (0 > listen(sockfd, 5)){perror("listen");return -1;}printf("listen.................\n");/*4. 接受客户端的连接,并生成通信套接字*/int connfd = accept(sockfd, NULL, NULL);if (connfd < 0){perror("accept");return -1;}printf("accept success!\n");/*接收超时*/struct timeval tv = {1};if (0 > setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))){perror("setsockopt");return -1;}/*5. 与客户端通信*/int ret;char buf[1024];while (1){memset(buf, 0, sizeof(buf));ret = read(connfd, buf, sizeof(buf));if (ret < 0){perror("read");continue;}else if (0 == ret){printf("write close!\n");break;}printf("recv: %s\n", buf);
/*if (0 > write(connfd, buf, ret)){perror("write");break;}
*/}/*6. 关闭套接字*/close(connfd);close(sockfd);return 0;
} 

2.网络超时检测

网络超时

在网络通信中,很多操作会使得进程阻塞

TCP套接字中的recv/ accept/ connect

UDP套接字中的recvfrom

超时检测的必要性
避免进程在没有数据时无限制地阻塞(例如有两个客户端连接同一个服务器,当客户端1没有输入数据时,服务器就会阻塞等待1的输入,大大浪费了服务器的资源,所以采用网络超时检测,循环检测,当客户端2输入时也能检测出运行到)
当设定的时间到时,进程从原操作返回继续运行

1)setsockopt

/*接收超时*/
struct timeval tv = {1};
if (0 > setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
{perror("setsockopt");return -1;
}ret = read(connfd,buf,sizeof(buf));if(ret < 0){perror("read");continue;}else if(ret == 0){printf("write close\n");break;}printf("recv:%s\n",buf);

2)select/poll

struct timeval va ={1,1000000};//2.监测是否有文件描述符产生了事件int  retval = select(maxfd+1,&rfds,NULL,NULL,&va);if(retval < 0){perror("select");break;}else if(0 == retval){printf("timeout------\n");continue;}
若没数据输入则每隔2s打印timeout------

3)sigaction()

设置定时器(timer), 捕捉SIGALRM信号
参考代码如下void  handler(int signo)     {   return;  }
//一旦进程收到这个信号,执行完信号处理函数之后,下一个函数就会直接返回,不阻塞在那里struct sigaction act;sigaction(SIGALRM, NULL, &act);  //获取信号原有的actact.sa_handler = handler;act.sa_flags &= ~SA_RESTART;   //把原有的act改成需要的actsigaction(SIGALRM, &act, NULL); //设置信号新的act/*5. 与客户端通信*/int ret;char buf[1024];while (1){alarm(1);  //闹钟函数,产生SIGALRM信号recv()}
|= :添加某功能&= ~:去除某功能

3.广播

在这里插入图片描述

前面介绍的数据包发送方式只有一个接受方,称为单播

如果同时发给局域网中的所有主机,称为广播

只有用户数据报(使用UDP协议)套接字才能广播

广播地址

​ 以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址

​ 发到该地址的数据包被所有的主机接收

​ 255.255.255.255在所有网段中都代表广播地址

1. 发送方创建用户数据报套接字缺省创建的套接字不允许广播数据包,需要设置属性setsockopt可以设置套接字属性接收方地址指定为广播地址以及端口信息发送数据包2. 接收方创建用户数据报套接字绑定IP地址(广播IP或0.0.0.0)和端口绑定的端口必须和发送方指定的端口相同等待接收数据#include <stdio.h>
#include "net.h"
/接收方
int main()
{/*1. 创建用户数据报套接字*/int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket");return -1;}/*允许重用本地地址和端口*/int on = 1;if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))){perror("setsockopt");return -1;}/*2. 绑定广播地址和端口*/SAI bcaddr = {.sin_family = AF_INET,.sin_port = htons(6666),.sin_addr.s_addr = inet_addr("192.168.2.255") //广播IP};if (0 > bind(sockfd, (SA*)&bcaddr, sizeof(bcaddr))){perror("bind");return -1;}/*3. 接收数据*/SAI sndaddr;socklen_t addrlen = sizeof(sndaddr);int ret;char buf[SIZE];while (1){ret = recvfrom(sockfd, buf, SIZE, 0, (SA*)&sndaddr, &addrlen);if (ret < 0){perror("recvfrom");break;}printf("recv: %s\n", buf);}return 0;
}/
发送方#include <stdio.h>
#include "net.h"int main()
{/*1. 创建用户数据报套接字*/int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket");return -1;}/*2. 设置允许发送广播包*/int on = 1;if (0 > setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on))){perror("setsockopt");return -1;}/*3. 接收方地址指定为广播地址以及端口信息*/SAI rcvaddr = {.sin_family = AF_INET,.sin_port = htons(6666),.sin_addr.s_addr = inet_addr("192.168.2.255") //广播IP};/*4. 发送数据包*/char buf[SIZE];while (1){printf("send: ");fgets(buf, SIZE, stdin);if (0 > sendto(sockfd, buf, SIZE, 0, (SA*)&rcvaddr, sizeof(rcvaddr))){perror("sendto");break;}}return 0;
}

4.组播

单播方式只能发给一个接收方。广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。多播方式既可以发给多个主机,又能避免象广播那样带来过多的负载(每台主机要到传输层才能判断广播包是否要处理)组播地址:224.0.0.1239.255.255.2551. 发送方创建用户数据报套接字接收方地址指定为组播地址以及端口信息发送数据包2. 接收方a. 创建用户数据报套接字b. 加入多播组struct ip_mreq{struct  in_addr  imr_multiaddr; struct  in_addr  imr_interface;};struct  ip_mreq  mreq;bzero(&mreq, sizeof(mreq));mreq.imr_multiaddr.s_addr = inet_addr(224.10.10.1); //组播IPmreq.imr_interface.s_addr = htonl(INADDR_ANY);  //本机IPsetsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,  sizeof(mreq));c. 绑定IP地址(组播IP或0.0.0.0)和端口绑定的端口必须和发送方指定的端口相同d. 等待接收数据接收数据#include <stdio.h>
#include "net.h"int main()
{/*1. 创建用户数据报套接字*/int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket");return -1;}/*2. 加入多播组*/struct ip_mreq{struct  in_addr  imr_multiaddr; struct  in_addr  imr_interface;};struct  ip_mreq  mreq;bzero(&mreq, sizeof(mreq));mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.1"); //组播IPmreq.imr_interface.s_addr = htonl(INADDR_ANY);  //本机IPif (0 > setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,  sizeof(mreq))){perror("setsockopt");return -1;}/*允许重用本地地址和端口*/int on = 1;if (0 > setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))){perror("setsockopt");return -1;}/*3. 绑定组播地址和端口*/SAI mcaddr = {.sin_family = AF_INET,.sin_port = htons(6666),.sin_addr.s_addr = inet_addr("224.0.0.1") //组播IP};if (0 > bind(sockfd, (SA*)&mcaddr, sizeof(mcaddr))){perror("bind");return -1;}/*4. 接收数据*/SAI sndaddr;socklen_t addrlen = sizeof(sndaddr);int ret;char buf[SIZE];while (1){ret = recvfrom(sockfd, buf, SIZE, 0, (SA*)&sndaddr, &addrlen);if (ret < 0){perror("recvfrom");break;}printf("recv: %s, %hu, %s\n", inet_ntoa(sndaddr.sin_addr), \ntohs(sndaddr.sin_port), buf);}return 0;
}/
发送数据#include <stdio.h>
#include "net.h"int main()
{/*1. 创建用户数据报套接字*/int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("socket");return -1;}/*2. 接收方地址指定为组播地址以及端口信息*/SAI rcvaddr = {.sin_family = AF_INET,.sin_port = htons(6666),.sin_addr.s_addr = inet_addr("224.0.0.1") //组播IP};/*3. 发送数据包*/char buf[SIZE];while (1){printf("send: ");fgets(buf, SIZE, stdin);if (0 > sendto(sockfd, buf, SIZE, 0, (SA*)&rcvaddr, sizeof(rcvaddr))){perror("sendto");break;}}return 0;
}

5.unix域套接字

socket同样可以用于本地通信

创建套接字时使用本地协议PF_UNIX(或PF_LOCAL, AF_UNIX, AF_LOCAL)。

分为流式套接字和用户数据报套接字

和其他进程间通信方式相比使用方便、效率更高

常用于前后台进程通信

本地地址结构struct sockaddr_un        //  <sys/un.h>{sa_family_t  sun_family;char  sun_path[108];         // 套接字文件的路径};
填充地址结构struct sockaddr_un myaddr;bzero(&myaddr,  sizeof(myaddr));myaddr.sun_family = PF_UNIX; strcpy(myaddr.sun_path,  “mysocket”);流式:server:socket(AF_UNIX, SOCK_STREAM, 0);bind(本地地址);listen();accept();recv/send();close();client:socket(AF_UNIX, SOCK_STREAM, 0);connect(本地地址);send/recv();close();
数据报:server:socket(AF_UNIX, SOCK_DGRAM, 0);bind(本地地址);sendto/recvfrom();close();client:socket(AF_UNIX, SOCK_DGRAM, 0);recvfrom()/sendto();close();
/*===============================================
*   文件名称:client.c
*   创 建 者:     
*   创建日期:2023年05月19日
*   描    述:
================================================*/发送端#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/un.h>int main(int argc, char *argv[])
{ /*1. 创建套接字*/int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);if (sockfd < 0){perror("socket");return -1;}printf("socket...........\n");/*2. 主动连接服务器*/struct sockaddr_un srvaddr;memset(&srvaddr, 0, sizeof(srvaddr));srvaddr.sun_family 	= AF_UNIX;  //地址族strcpy(srvaddr.sun_path, "mysocket");if (0 > connect(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) {perror("connect");return -1;}printf("connect................\n");/*3. 与服务器通信*/int ret;char buf[1024];while (1){printf("Send: ");fgets(buf, sizeof(buf), stdin);if (0 > write(sockfd, buf, sizeof(buf))){perror("write");break;}}/*4. 关闭套接字*/close(sockfd);return 0;
} *===============================================
*   文件名称:server.c
*   创 建 者:     
*   创建日期:20230518*   描    述:
================================================*/接收方    
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/un.h>int main(int argc, char *argv[])
{ /*1. 创建套接字*/int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);if (sockfd < 0){perror("socket");return -1;}printf("socket..............\n");/*2. 绑定本地地址*/struct sockaddr_un srvaddr;memset(&srvaddr, 0, sizeof(srvaddr));srvaddr.sun_family 	= AF_UNIX;  //地址族strcpy(srvaddr.sun_path, "mysocket");if (0 > bind(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr))) {perror("bind");return -1;}printf("bind................\n");/*3. 设置监听套接字*/if (0 > listen(sockfd, 5)){perror("listen");return -1;}printf("listen.................\n");/*4. 接受客户端的连接,并生成通信套接字*/int connfd = accept(sockfd, NULL, NULL);if (connfd < 0){perror("accept");return -1;}printf("accept success!\n");/*5. 与客户端通信*/int ret;char buf[1024];while (1){memset(buf, 0, sizeof(buf));ret = read(connfd, buf, sizeof(buf));if (ret < 0){perror("read");break;}else if (0 == ret){printf("write close!\n");break;}printf("recv: %s\n", buf);
/*if (0 > write(connfd, buf, ret)){perror("write");break;}
*/}/*6. 关闭套接字*/close(connfd);close(sockfd);return 0;
} 

http://www.ppmy.cn/news/93306.html

相关文章

Java-API简析_java.lang.Class类(基于JDK1.8)(浅析源码)

【版权声明】未经博主同意&#xff0c;谢绝转载&#xff01;&#xff08;请尊重原创&#xff0c;博主保留追究权&#xff09; https://blog.csdn.net/m0_69908381/article/details/130838927 出自【进步*于辰的博客】 其实我的【Java-API】专栏内的博文对大家来说意义是不大的。…

GATK最佳实践之数据预处理SnakeMake流程

<~生~信~交~流~与~合~作~请~关~注~公~众~号生信探索> 写的数据预处理snakemake流程其实包括在每个单独的分析中比如种系遗传变异和肿瘤变异流程中&#xff0c;这里单独拿出来做演示用&#xff0c;因为数据预处理是通用的&#xff0c;在call变异之前需要处理好数据。 数据…

GPC_UICC Configuration

GPC_UICC Configuration_v2.0.pdf 1 简介 本文档规定了在 ETSI 规范 TS 102 221 [TS 102 221]、TS 102 223 [TS 102 223] 中指定的 UICC 平台上实施 GlobalPlatform 规范的配置要求&#xff0c; TS 102 225 [TS 102 225] 和 TS 102 226 [TS 102 226]。 GlobalPlatform Common …

叮咚音乐门铃芯片方案推荐 WTN6006-8S 低功耗 高性价比

​ 随着物联网技术的不断发展&#xff0c;智能家居已经成为了生活中不可或缺的一部分。作为智能家居中的重要组成部分&#xff0c;门铃同样需要进行智能化升级&#xff0c;在改善用户体验、保障家庭安全方面起到了重要作用。本文将介绍一种基于音乐芯片的叮咚门铃应用方案…

金融、医疗、教育等各场景下小程序SDK的应用

近年来&#xff0c;随着数字经济的飞速发展和移动终端的迅速普及&#xff0c;移动互联网全面覆盖&#xff0c;各类应用服务层出不穷&#xff0c;涵盖了方方面面的生活、工作和学习。 而小程序作为一种轻量级的应用形态&#xff0c;越来越受到开发者和用户的欢迎。为了满足不同行…

Git进阶·GitFlow·壹

文章目录 1 Git进阶——GitFlow工作流程1.1 master与develop分支1.1.1 master1.1.2 develop 1.2 feature分支1.3 Release分支1.4 hotfix分支1.5 GitFlow示例1.5.1 在master上新建dev分支1.5.2 基于dev创建feature分支1.5.3 feature分支上开发业务代码1.5.4 将feature合并到dev1…

【C++】unordered_map unordered_set 练习题

文章目录 unordered系列关联式容器unordered_mapunordered_map的文档介绍unordered_map的构造接口使用: unordered_multimapunorder_map&&unorder_multimap对比:unordered_setunordered_set的文档介绍unordered_set的构造接口使用 unordered_multisetOJ练习961.在长度2…

13. InnoDB引擎底层原理及Mysql 8.0 新增特性详解

MySQL性能调优 一、InnoDB引擎底层原理1. 深入理解Redolog日志底层原理1.1 innodb引擎底层事务原理1.1.1 WAL 2. redolog日志文件2.1 为什么要redolog日志文件2.2 redolog的内部结构2.3 redolog的刷盘时机2.4 Log Sequence Number2.5 innodb_flush_log_at_trx_commit 3. undolo…