当前位置: 首页 > news >正文

linux系统编程09-进程间通信

目录
  • 介绍
  • 1. 管道
    • 匿名管道
    • 命名管道
  • 2. IPC:XSI → SysV
    • Message Queues
    • Semaphore Arrays
    • Shared Memory
  • 3. 网络套接字socket
    • udp
      • 单播
      • 广播
      • 多播
    • tcp
      • 单进程
      • 多进程
      • pool_static:静态进程池
      • pool_dynamic:动态进程池

介绍

Untitled

1. 管道

Untitled

  • 命名管道就是一块磁盘上的文件,不同进程通过读写该文件进行通信
  • 匿名管道同样是在磁盘上创建了文件,但是只返回文件描述符,其他进程看不到,所以只能用于有亲缘关系的进程

匿名管道

  • 管道是阻塞的:有写者的时候,读者会阻塞读
##include <unistd.h>
int pipe(int pipefd[2]);
  • 参数: pid 文件描述符数组, pid[0] 读端, pid[1] 写端
  • 返回值: <0 失败

Untitled

例子:pipe实现父子进程间通信

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>
##include<sys/wait.h>##define BUFSIZE 1024int main()
{size_t pid;int pd[2];int len;char buf[BUFSIZE];if(pipe(pd)<0){perror("pipe()");exit(1);}pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0)  //child read{close(pd[1]);len = read(pd[0],buf,BUFSIZE);write(1,buf,len);close(pd[0]);exit(0);}else        //parent write{close(pd[0]);write(pd[1], "Hello", 6);close(pd[1]);wait(NULL);exit(0);}
}

运行结果:

Untitled

项目相关:

父进程做好管道,子进程 exec 变成播放器,然后父子进程通过管道通信

Untitled

mpg123 [ options ] file-or-URL...or -

sudo apt install mpg123man mpg123

mpg123 歌曲名 :播放歌曲, ‘-’表示从标准输入读取内容播放

Untitled

##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>
##include<sys/wait.h>##define BUFSIZE 1024int main()
{size_t pid;int pd[2];int len;char buf[BUFSIZE];if(pipe(pd)<0){perror("pipe()");exit(1);}pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0)  //child read{close(pd[1]); //关闭写d端dup2(pd[0],0); //重定向读端到标准输入close(pd[0]);fd = open("/dev/null",O_RDWR);dup2(fd,1);dup2(fd,2);execl("/usr/bin/mpg123", "mpg123", "-", NULL);perror("execl()");exit(1);}else        //parent write{close(pd[0]); //关闭读端//父进程从网上收数据,往管道中x写close(pd[1]);wait(NULL);exit(0);}
}
ub

命名管道

mkfifo 命令:创建管道文件

阻塞:没凑齐读写双方时会阻塞

Untitled

Untitled

2. IPC:XSI → SysV

ipcs 命令:展现机制

key : ftok()

Message Queues :xxxget() xxxop()[包括cv和snd] xxxctl()

Semaphore Arrays

Shared Memory

Untitled

常规的操作流程:

  1. 创建实例: ftok() xxxget()
  2. 操作: xxxop()
  3. 删除实例: xxxctl()

Message Queues

Untitled

示例:消息队列实现非亲缘关系进程间通信

示例代码:

proto.h

##ifndef PROTO_H__
##define PROTO_H__##define KEYPATH     "/etc/services"
##define KEYPROJ     'g'##define NAMESIZE    32struct msg_st
{long mtype; //msgrcv中对传输数据包规定要加char name[NAMESIZE];int math;int chinese;
};##endif

snder.c

##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<sys/ipc.h>
##include<sys/msg.h>
##include<string.h>##include"proto.h"int main()
{key_t key;int msgid;struct msg_st sbuf;key = ftok(KEYPATH,KEYPROJ);if(key < 0){perror("ftok()");exit(1);}//后运行的一方只需要拿到对应的msgid即可msgid = msgget(key,0);if(msgid < 0){perror("msgget()");exit(1);}sbuf.mtype = 1;strcpy(sbuf.name, "Alan");sbuf.math = rand()%100;sbuf.chinese = rand()%100;if(msgsnd(msgid,&sbuf, sizeof(sbuf)-sizeof(long), 0) < 0){perror("msgsnd()");exit(1);}puts("ok");//没创建,所以不用销毁:msgctl();exit(0);
}

rcver.h

##include<stdio.h>
##include<stdlib.h>
##include <sys/types.h>
##include <sys/ipc.h>
##include <sys/msg.h>
##include"proto.h"int main()
{key_t key;int msgid;struct msg_st rbuf;key = ftok(KEYPATH,KEYPROJ);if(key < 0){perror("ftok()");exit(1);}//主动方:创建,让机制运行起来msgid = msgget(key, IPC_CREAT|0600);if(msgid < 0){perror("msgget()");exit(1);}while(1){if(msgrcv(msgid,&rbuf,sizeof(rbuf)-sizeof(long),0,0) < 0){perror("msgce()");exit(1);}printf("NAME = %s\n",rbuf.name);printf("MATH = %d\n",rbuf.math);printf("CHINESE = %d\n",rbuf.chinese);}msgctl(msgid, IPC_RMID, NULL);exit(0);
}

运行结果:

运行 ./rcver 后的结果

运行 ./rcver 后的结果

Untitled

注意点:

  1. 即使先发送,后接收,也能收到之前发送的内容,说明msg有缓冲机制,缓冲区的大小可以通过 ulimit -a 查看

    Untitled

  2. 程序是异常终止,msg没有被销毁,如何解决

    1. 程序中加入信号和信号处理函数,在信号处理函数中关闭msg
    2. ipcrm -q msgidipcrm -q 0

    Untitled

例子2:实现ftp

Untitled

状态机编程

状态机编程

协议数据设计:

方式1:

##ifndef PROTO_H__
##define PROTO_H__##define KEYPATH     "/etc/services"
##define KEYPROJ     'a'##define PATHMAX     1024
##define DATAMAX     1024enum
{MSG_PATH=1,MSG_DATA,MSG_EOT
};//S端收到一种包
typedef struct msg_path_st
{long mtype;         //must be MSG_PATHchar path[PATHMAX]; //ASCIIZ带尾0的串
}msg_path_t;//C端收到两种包
typedef struct msg_data_st
{long mtype;         //must be MSG_DATAchar data[DATAMAX];int datalen;
}msg_data_t;typedef struct msg_eot_st
{long mtype;         //must be MSG_EOT
}msg_eot_t;//用union将两个包综合
//long mytpe实际不存在,用来提取公因子,方便判断类型
union msg_s2c_un
{long mtype;msg_data_t datamsg;msg_eot_t eotmsg;
}##endif

方式2:

##ifndef PROTO_H__
##define PROTO_H__##define KEYPATH     "/etc/services"
##define KEYPROJ     'a'##define PATHMAX     1024
##define DATAMAX     1024enum
{MSG_PATH=1,MSG_DATA,MSG_EOT
};//S端收到一种包
typedef struct msg_path_st
{long mtype;         //must be MSG_PATHchar path[PATHMAX]; //ASCIIZ带尾0的串
}msg_path_t;//C端收到两种包,合并为一种结构体
//没第一种做法好,只适用于eot中只用mtype的情况
typedef struct msg_s2c_st
{long mtype;         //must be MSG_DATA or MSG_EOTchar data[DATAMAX];int datalen;/** datalen > 0 :data*         < 0 :eot*/
}msg_s2c_t;##endif

Semaphore Arrays

Untitled

信号量数组的必要性:

  1. 银行家算法中,AB两个信号量要同时被分配,防止死锁
  2. 两个进程逆序申请两个锁的时候,操作要是原子的
  3. 本质就是把几个信号量的操作原子化

Untitled

例子:重构 lockf,二十个进程同步往 /tmp/out 加1

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<string.h>
##include<wait.h>
##include<unistd.h>
##include<sys/types.h>
##include<sys/ipc.h>
##include<sys/sem.h>
##include<errno.h>##define PROCNUM     20
##define FILENAME    "/tmp/out"
##define LINESIZE     1024int semid;static void P(void)
{struct sembuf op[1];op[0].sem_num = 0;op[0].sem_op = -1;op[0].sem_flg = 0;while(semop(semid,op,1) < 0){if(errno != EINTR || errno != EAGAIN){perror("semop()");exit(1);}}
}static void V(void)
{struct sembuf op[1];op[0].sem_num = 0;op[0].sem_op = 1;op[0].sem_flg = 0;if(semop(semid,op,1)<0){perror("semop()");exit(1);}
}static void func_add(void)
{FILE *fp;int fd;char linebuf[LINESIZE];fp = fopen(FILENAME, "r+");if(fp == NULL){perror("fopen()");exit(1);}P();fgets(linebuf, LINESIZE, fp);fseek(fp, 0, SEEK_SET);//sleep(1);fprintf(fp, "%d\n",atoi(linebuf)+1);fflush(fp); //文件全缓冲V();fclose(fp);
}int main()
{int i,err;size_t pid;//key_t key;//key = ftok();//1.创建:由于父子进程共享semid//所以不需要用key生成。1为数组元素semid = semget(IPC_PRIVATE,1,0600);if(semid<0){perror("semget()");exit(1);}//2.初始化:设置下标为0的元素值为1if(semctl(semid,0,SETVAL,1) < 0){perror("semctl");exit(1);}for(i = 0; i < PROCNUM; i++){pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0){func_add();exit(0);}}for(i = 0; i < PROCNUM; i++)wait(NULL);//3.移除:既然要销毁,0不当下标了semctl(semid,0,IPC_RMID);exit(0);
}

运行结果:

Untitled

Shared Memory

Untitled

例子:父子进程通信,子写父读

示例代码:

##include<stdio.h>
##include<stdlib.h>
##include<unistd.h>
##include<sys/types.h>
##include<sys/ipc.h>
##include<sys/shm.h>
##include<sys/wait.h>
##include<string.h>##define MEMSIZE 1024
int main()
{pid_t pid;int shmid;char *ptr;//ftok(); 有亲缘关系,不需要shmid = shmget(IPC_PRIVATE,MEMSIZE ,0600);if(shmid < 0){perror("shmif()");exit(1);}pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0)  //child write{ptr = shmat(shmid,NULL,0);if(ptr == (void*)-1){perror("shmat()");exit(1);}strcpy(ptr, "Hello World");shmdt(ptr); //解除映射exit(0);}else          //parent read{wait(NULL);ptr = shmat(shmid, NULL,0);if(ptr == (void*)-1){perror("shmat()");exit(1);}puts(ptr);shmdt(ptr);shmctl(shmid, IPC_RMID, NULL); //销毁实例exit(0);}exit(0);
}

运行结果:

Untitled

3. 网络套接字socket

Untitled

Untitled

socket是什么:向下给各层数据传输协议,向上给两种传输方式(数据报、流式)封装出的接口

Untitled

使用 socket 的方式

Untitled

Untitled

用domain协议族里的协议protocol,实现type类型的传输。返回的是文件描述符,当文件来操作。

udp

Untitled

struct sockadd 实际上不存在,需要 man 7 ip 查看 socket format 得到对应的结构体

单播

例子1:基本使用

示例代码:

  • proto.h【规定数据报格式】

    #ifndef PROTO_H__
    #define PROTO_H__#define RCVPORT "1989"   //封装端口(最好>1024)#define NAMESIZE    11struct msg_st
    {uint8_t name[NAMESIZE];uint32_t math;uint32_t chinese;
    }__attribute__((packed)); //告诉编译器不对齐#endif
    
  • recver.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<unistd.h>#include"proto.h"#define IPSTRSIZE 20int main()
    {int sd;struct sockaddr_in localaddr, remoteaddr; //具体是什么结构体取决于协议//看手册 man 7 xx(proto) 里的 Address_formatstruct msg_st rbuf;socklen_t remoteaddr_len;char ipstr[IPSTRSIZE];//创建socketsd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);//0表示默认协议if(sd < 0){perror("socket()");exit(1);}localaddr.sin_family = AF_INET;localaddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr); //0.0.0.0在绑定阶段会绑定自身ip//取得地址if(bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //void *百搭{perror("bind()");exit(1);}/*!!!!*/remoteaddr_len = sizeof(remoteaddr);while(1){recvfrom(sd,&rbuf,sizeof(rbuf),0,(void*)&remoteaddr,&remoteaddr_len);inet_ntop(AF_INET, &remoteaddr.sin_addr, ipstr, IPSTRSIZE);printf("----MESSAGE FROM %s:%d------\n", ipstr, ntohs(remoteaddr.sin_port));printf("NAME = %s\n",rbuf.name);printf("MATH = %d\n",ntohl(rbuf.math));printf("CHINESE = %d\n",ntohl(rbuf.chinese));}close(sd);exit(0);
    }
    
  • snder.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<string.h>
    #include<unistd.h>#include"proto.h"int main(int argc, char **argv) //传ip地址
    {if(argc < 2){fprintf(stderr, "Usage:...\n");exit(1);}int sd;struct msg_st sbuf;struct sockaddr_in raddr;sd = socket(AF_INET,SOCK_DGRAM,0);if(sd < 0){perror("socket()");exit(1);}//bind();strcpy(sbuf.name, "Alan");sbuf.math = htonl(rand()%100);sbuf.chinese = htonl(rand()%100);raddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET, argv[1], &raddr.sin_addr);if(sendto(sd,&sbuf, sizeof(sbuf), 0, (void*)&raddr, sizeof(raddr)) < 0){perror("sendto()");exit(1);}puts("OK");close(sd);exit(0);
    }
    

运行结果:

运行recver.c后,执行 **** 查看udp套接字使用情况

运行recver.c后,执行 netstat -anu 查看udp套接字使用情况

Untitled

发送端没有bind端口任意指定

Untitled

例子二:发送变长数组

思路: proto改变变长数组,发送的时候malloc申请内存,接收的时候按最大空间接收

示例代码:

  • proto.h

    #ifndef PROTO_H__
    #define PROTO_H__#define RCVPORT "1989"   //封装端口(最好>1024)#define NAMEMAX    (512-8-8)struct msg_st
    {uint32_t math;uint32_t chinese;uint8_t name[1]; //变长数组
    }__attribute__((packed)); //告诉编译器不对齐#endif
    
  • snder.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<string.h>
    #include<unistd.h>#include"proto.h"int main(int argc, char **argv) //传ip地址
    {if(argc < 3){fprintf(stderr, "Usage:...\n");exit(1);}int sd;int size;struct msg_st *sbufp;struct sockaddr_in raddr;size = sizeof(struct msg_st)+strlen(argv[2]);sbufp = malloc(size);if(sbufp == NULL){perror("malloc()");exit(1);}sd = socket(AF_INET,SOCK_DGRAM,0);if(sd < 0){perror("socket()");exit(1);}//bind();if(strlen(argv[2]) > NAMEMAX){fprintf(stderr, "NAME is too long\n");exit(1);}strcpy(sbufp->name, argv[2]);sbufp->math = htonl(rand()%100);sbufp->chinese = htonl(rand()%100);raddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET, argv[1], &raddr.sin_addr);if(sendto(sd,sbufp, size, 0, (void*)&raddr, sizeof(raddr)) < 0){perror("sendto()");exit(1);}puts("OK");close(sd);free(sbufp);exit(0);
    }
    
  • recver.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<unistd.h>#include"proto.h"#define IPSTRSIZE 20int main()
    {int sd;int size;struct sockaddr_in localaddr, remoteaddr; //具体是什么结构体取决于协议//看手册 man 7 xx(proto) 里的 Address_formatstruct msg_st *rbufp;socklen_t remoteaddr_len;char ipstr[IPSTRSIZE];sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);//0表示默认协议if(sd < 0){perror("socket()");exit(1);}size = sizeof(struct msg_st) + NAMEMAX - 1;rbufp = malloc(size);if(rbufp == NULL){perror("malloc()");exit(1);}localaddr.sin_family = AF_INET;localaddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr); //0.0.0.0在绑定阶段会绑定自身ipif(bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //void *百搭{perror("bind()");exit(1);}/*!!!!*/remoteaddr_len = sizeof(remoteaddr);while(1){recvfrom(sd,rbufp,size,0,(void*)&remoteaddr,&remoteaddr_len);inet_ntop(AF_INET, &remoteaddr.sin_addr, ipstr, IPSTRSIZE);printf("----MESSAGE FROM %s:%d------\n", ipstr, ntohs(remoteaddr.sin_port));printf("NAME = %s\n",rbufp->name);printf("MATH = %d\n",ntohl(rbufp->math));printf("CHINESE = %d\n",ntohl(rbufp->chinese));}close(sd);exit(0);
    }
    

运行结果:

Untitled

广播

Untitled

多播与广播需要设置:用到函数 setsockopt

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

这个函数横跨ip,tcp,udp, level 指定协议, optname 是具体设置的项目, optval 是参数, optlen 是参数的sizeof。

leveloptname 从哪里获取? man 7 ip/tcp/udp 查看 Socket Option 即可。

比如 man 7 udp 查看 socket Option,可知, level == SOL_SOCKET,打开广播的选项 opt == SO_BROADCAST

Untitled

Untitled

Untitled

Untitled

示例代码:

  • snder.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<string.h>
    #include<unistd.h>#include"proto.h"int main(int argc, char **argv) //传ip地址
    {int sd;struct msg_st sbuf;struct sockaddr_in raddr;sd = socket(AF_INET,SOCK_DGRAM,0);if(sd < 0){perror("socket()");exit(1);}//打开广播选项int val = 1;if(setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val)) < 0){perror("setsockopt()");exit(1);}strcpy(sbuf.name, "Alan");sbuf.math = htonl(rand()%100);sbuf.chinese = htonl(rand()%100);raddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET, "255.255.255.255", &raddr.sin_addr); //全网广播if(sendto(sd,&sbuf, sizeof(sbuf), 0, (void*)&raddr, sizeof(raddr)) < 0){perror("sendto()");exit(1);}puts("OK");close(sd);exit(0);
    }
    
  • recver.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<unistd.h>#include"proto.h"#define IPSTRSIZE 20int main()
    {int sd;struct sockaddr_in localaddr, remoteaddr; //具体是什么结构体取决于协议//看手册 man 7 xx(proto) 里的 Address_formatstruct msg_st rbuf;socklen_t remoteaddr_len;char ipstr[IPSTRSIZE];sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);//0表示默认协议if(sd < 0){perror("socket()");exit(1);}int val = 1;if(setsockopt(sd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(val)) < 0){perror("setsockopt()");exit(1);}localaddr.sin_family = AF_INET;localaddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr); //0.0.0.0在绑定阶段会绑定自身ipif(bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //void *百搭{perror("bind()");exit(1);}/*!!!!*/remoteaddr_len = sizeof(remoteaddr);while(1){recvfrom(sd,&rbuf,sizeof(rbuf),0,(void*)&remoteaddr,&remoteaddr_len);inet_ntop(AF_INET, &remoteaddr.sin_addr, ipstr, IPSTRSIZE);printf("----MESSAGE FROM %s:%d------\n", ipstr, ntohs(remoteaddr.sin_port));printf("NAME = %s\n",rbuf.name);printf("MATH = %d\n",ntohl(rbuf.math));printf("CHINESE = %d\n",ntohl(rbuf.chinese));}close(sd);exit(0);
    }
    

运行结果:

Untitled

多播

实现在ip层, man 7 ipSocket Option

发送方建立多播组,接受方如果要接受该多播组,就加入,所以协议(proto.h)中要加入多播组的字段

示例代码:

  • proto.h

    #ifndef PROTO_H__
    #define PROTO_H__#define MTROUP  "224.2.2.2" //多播:D类地址
    #define RCVPORT "1989"   //封装端口(最好>1024)#define NAMESIZE    11struct msg_st
    {uint8_t name[NAMESIZE];uint32_t math;uint32_t chinese;
    }__attribute__((packed)); //告诉编译器不对齐#endif
    
  • snder.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<string.h>
    #include<unistd.h>
    #include<net/if.h>#include"proto.h"int main(int argc, char **argv) //传ip地址
    {int sd;struct msg_st sbuf;struct sockaddr_in raddr;sd = socket(AF_INET,SOCK_DGRAM,0);if(sd < 0){perror("socket()");exit(1);}//打开广播选项struct ip_mreqn mreq;inet_pton(AF_INET,MTROUP,&mreq.imr_multiaddr);inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);mreq.imr_ifindex = if_nametoindex("eth0");//网络设备名转网络索引号if(setsockopt(sd,IPPROTO_IP,IP_MULTICAST_IF,&mreq,sizeof(mreq)) < 0){perror("setsockopt()");exit(1);}strcpy(sbuf.name, "Alan");sbuf.math = htonl(rand()%100);sbuf.chinese = htonl(rand()%100);raddr.sin_family = AF_INET;raddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET, MTROUP, &raddr.sin_addr); //全网广播if(sendto(sd,&sbuf, sizeof(sbuf), 0, (void*)&raddr, sizeof(raddr)) < 0){perror("sendto()");exit(1);}puts("OK");close(sd);exit(0);
    }
    
  • recver.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<unistd.h>
    #include<net/if.h>#include"proto.h"#define IPSTRSIZE 20int main()
    {int sd;struct sockaddr_in localaddr, remoteaddr; //具体是什么结构体取决于协议//看手册 man 7 xx(proto) 里的 Address_formatstruct msg_st rbuf;socklen_t remoteaddr_len;char ipstr[IPSTRSIZE];sd = socket(AF_INET,SOCK_DGRAM,0/*IPPROTO_UDP*/);//0表示默认协议if(sd < 0){perror("socket()");exit(1);}//加入多播组struct ip_mreqn mreq;inet_pton(AF_INET,MTROUP,&mreq.imr_multiaddr);inet_pton(AF_INET,"0.0.0.0",&mreq.imr_address);mreq.imr_ifindex = if_nametoindex("eth0");if(setsockopt(sd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq, sizeof(mreq)) < 0){perror("setsockopt()");exit(1);}localaddr.sin_family = AF_INET;localaddr.sin_port = htons(atoi(RCVPORT));inet_pton(AF_INET,"0.0.0.0",&localaddr.sin_addr); //0.0.0.0在绑定阶段会绑定自身ipif(bind(sd,(void*)&localaddr,sizeof(localaddr)) < 0) //void *百搭{perror("bind()");exit(1);}/*!!!!*/remoteaddr_len = sizeof(remoteaddr);while(1){recvfrom(sd,&rbuf,sizeof(rbuf),0,(void*)&remoteaddr,&remoteaddr_len);inet_ntop(AF_INET, &remoteaddr.sin_addr, ipstr, IPSTRSIZE);printf("----MESSAGE FROM %s:%d------\n", ipstr, ntohs(remoteaddr.sin_port));printf("NAME = %s\n",rbuf.name);printf("MATH = %d\n",ntohl(rbuf.math));printf("CHINESE = %d\n",ntohl(rbuf.chinese));}close(sd);exit(0);
    }
    

运行结果:

Untitled

查看网络设备名称和编号 ip ad sh

Untitled

注意: 224.0.0.1 所有支持多播的结点,都默认在这个组中,且无法离开,相当于 255.255.255.255 (广播)

tcp

Untitled

struct sockadd 实际上不存在,需要 man 7 ip 查看 socket format 得到对应的结构体

单进程

示例代码:

  • proto.h

    #ifndef PROTO_H__
    #define PROTO_H__#define SERVERPORT "1989"#define FMT_STAMP "%lld\r\n"#endif
    
  • server.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<time.h>
    #include<unistd.h>#include"proto.h"#define IPSTRSIZE 40
    #define BUFSIZE   1024static void server_job(int sd)
    {int len;char buf[BUFSIZE];len = sprintf(buf,FMT_STAMP,(long long)time(NULL));if(send(sd,buf,len,0) < 0){perror("sen()");exit(1);}}int main()
    {int sd,newsd;struct sockaddr_in laddr,raddr;socklen_t raddr_len;char ipstr[IPSTRSIZE];sd = socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);if(sd < 0){perror("socket()");exit(1);}laddr.sin_family = AF_INET;laddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0){perror("bind()");exit(1);}if(listen(sd,200)<0)//200全连接上限{perror("listen()");exit(1);}raddr_len = sizeof(raddr);while(1){newsd = accept(sd,(void*)&raddr,&raddr_len);if(newsd < 0){perror("accept()");exit(1);}inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);printf("Client:%s:%d\n",ipstr,ntohs(raddr.sin_port));server_job(newsd); //send()close(newsd);}close(sd);exit(0);
    }
    
  • client.c

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>#include"proto.h"int main(int argc, char **argv)
    {int sd;struct sockaddr_in raddr;long long stamp;FILE *fp;if(argc < 2){fprintf(stderr,"Usage:...\n");exit(1);}sd = socket(AF_INET,SOCK_STREAM,0);if(sd < 0){perror("bind()");exit(1);}//bind();raddr.sin_family = AF_INET;raddr.sin_port =  htons(atoi(SERVERPORT));inet_pton(AF_INET,argv[1],&raddr.sin_addr); //server ipif(connect(sd,(void*)&raddr,sizeof(raddr)) < 0){perror("connet()");exit(1);}//一切皆文件:socket转为stream操作fp = fdopen(sd,"r+");if(fp == NULL){perror("fdopen()");exit(1);}if(fscanf(fp, FMT_STAMP, &stamp) < 1) //返回成功匹配的个数fprintf(stderr,"Bad format!\n");elsefprintf(stdout,"stamp = %lld\n",stamp);fclose(fp);//rcve()//close()exit(0);
    }
    

运行结果:

./server

./server

 或  发送请求

nc ip porttelnet ip port 发送请求

Untitled

注意点查看多线程部分。

多进程

示例代码:

  • server.c 其他不变

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<time.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>#include"proto.h"#define IPSTRSIZE 40
    #define BUFSIZE   1024static void server_job(int sd)
    {int len;char buf[BUFSIZE];len = sprintf(buf,FMT_STAMP,(long long)time(NULL));if(send(sd,buf,len,0) < 0){perror("sen()");exit(1);}}int main()
    {int sd,newsd;struct sockaddr_in laddr,raddr;socklen_t raddr_len;char ipstr[IPSTRSIZE];pid_t pid;sd = socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);if(sd < 0){perror("socket()");exit(1);}int val=1;if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)) < 0){perror("setsockopt()");exit(1);}laddr.sin_family = AF_INET;laddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0){perror("bind()");exit(1);}if(listen(sd,200)<0)//200全连接上限{perror("listen()");exit(1);}raddr_len = sizeof(raddr);while(1){// printf("****before accept****\n");newsd = accept(sd,(void*)&raddr,&raddr_len);// printf("****after accept****\n");if(newsd < 0){perror("accept()");exit(1);}pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0){close(sd);inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);printf("Client:%s:%d\n",ipstr,ntohs(raddr.sin_port));server_job(newsd); //send()close(newsd);exit(0);}// printf("****end*****\n");close(newsd); //!!!}close(sd);exit(0);
    }
    

运行结果:

Untitled

  1. 进程阻塞在 accept ,因为 accept 互斥访问和阻塞

如果父进程调用了 accept 产生了 newsd ,如果不关闭 newsd 会导致 accept 认为该上次的传输没有结束,会继续等待上一个连接结束,导致新的传输阻塞,这提醒我们 accept 得到的文件描述符要及时关闭

Untitled

  1. server 异常结束时,端口未释放,再次执行时无法立刻重新绑定端口, bind() 抛异常

    Untitled

     查看,端口未释放,无法立即绑定

    netstat -anu 查看,端口未释放,无法立即绑定

解决:设置 sockoptman 7 socket 查看socket层的表示,找到对应命令,执行 setsockopt()

setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val))

Untitled

pool_static:静态进程池

思路:预先创建四个进程,不断接收信息

示例代码:

  • server.c 其他不变

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<time.h>
    #include<unistd.h>
    #include<sys/wait.h>#include"proto.h"#define IPSTRSIZE 40
    #define BUFSIZE   1024
    #define PROCNUM   4static void server_loop(int sd);
    static void server_job(int sd)
    {int len;char buf[BUFSIZE];len = sprintf(buf,FMT_STAMP,(long long)time(NULL));if(send(sd,buf,len,0) < 0){perror("send()");exit(1);}}int main()
    {int sd,i;struct sockaddr_in laddr;pid_t pid;sd = socket(AF_INET,SOCK_STREAM,0/*IPPROTO_TCP*/);if(sd < 0){perror("socket()");exit(1);}int val=1;if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)) < 0){perror("setsockopt()");exit(1);}laddr.sin_family = AF_INET;laddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0){perror("bind()");exit(1);}if(listen(sd,200)<0)//200全连接上限{perror("listen()");exit(1);}for(i = 0; i < PROCNUM; i++){pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0){server_loop(sd);exit(0);}}for(i = 0; i < PROCNUM; i++)wait(NULL);close(sd); //!!!exit(0);
    }static void server_loop(int sd)
    {struct sockaddr_in raddr;socklen_t raddr_len;int newsd;char ipstr[IPSTRSIZE];raddr_len = sizeof(raddr);while(1){//accept本身实现了互斥和阻塞,所以不用互斥量或信号量数组或条件变量newsd = accept(sd,(void*)&raddr,&raddr_len);if(newsd < 0){perror("accept()");exit(1);}inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);printf("[%d]Client:%s:%d\n",getpid(),ipstr,ntohs(raddr.sin_port));server_job(newsd); //send()close(newsd);}
    }
    

运行结果:

Untitled

缺点:无法应对突发请求,没有弹性

pool_dynamic:动态进程池

  • server.c其他不变

    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/socket.h>
    #include<arpa/inet.h>
    #include<signal.h>
    #include<errno.h>
    #include<unistd.h>
    #include<sys/mman.h>
    #include<time.h>#include"proto.h"#define MINSPARESERVER 5
    #define MAXSPARESERVER 10
    #define MAXCLIENTS     20#define SIG_NOTIFY     SIGUSR2
    #define IPSTRSIZE      40
    #define LINEBUFSIZE    80enum
    {STATE_IDLE=0,STATE_BUSY
    };struct server_st
    {pid_t pid;int state;
    //  int reuse;};static struct server_st *serverpool;
    static int idle_count = 0, busy_count = 0;
    static int sd;static void usr2_handler(int s)
    {return;
    }static void server_job(int pos)
    {pid_t ppid;struct sockaddr_in raddr;socklen_t raddr_len,len;int client_sd;time_t stamp;char ipstr[IPSTRSIZE];char linebuf[LINEBUFSIZE];ppid = getppid();while(1){serverpool[pos].state = STATE_IDLE;kill(ppid,SIG_NOTIFY);//发信号通知父进程client_sd = accept(sd,(void*)&raddr,&raddr_len);if(client_sd < 0){if(errno != EINTR && errno != EAGAIN){perror("accpet()");exit(1);}}serverpool[pos].state = STATE_BUSY;kill(ppid,SIG_NOTIFY);//inet_ntop(AF_INET,&raddr.sin_addr,ipstr,IPSTRSIZE);//printf("[%d]client:%s:%d\n",getpid(),ipstr,ntohs(raddr.sin_port));stamp = time(NULL);len = snprintf(linebuf, LINEBUFSIZE, FMT_STAMP,stamp);send(client_sd, linebuf, len, 0);/*if error*/sleep(5);close(client_sd);}
    }static int add_1_server(void)
    {int slot;pid_t pid;if(idle_count + busy_count >= MAXCLIENTS)return -1;for(slot = 0; slot < MAXCLIENTS; slot++){if(serverpool[slot].pid == -1)break;}serverpool[slot].state = STATE_IDLE;pid = fork();if(pid < 0){perror("fork()");exit(1);}if(pid == 0)    //child{server_job(slot);exit(0);}   else            //parent{serverpool[slot].pid = pid;idle_count++;}return 0;
    }static int del_1_server(void)
    {int i;if(idle_count == 0)return -1;for(i=0; i < MAXCLIENTS; i++){if(serverpool[i].pid != -1 && serverpool[i].state == STATE_IDLE){kill(serverpool[i].pid, SIGTERM);serverpool[i].pid = -1;idle_count--;break;}}return 0;
    }static int scan_pool(void)
    {int i;int busy = 0, idle = 0;for(i = 0; i < MAXCLIENTS; i++){if(serverpool[i].pid == -1)continue;if(kill(serverpool[i].pid,0)){serverpool[i].pid = -1;continue;}if(serverpool[i].state == STATE_IDLE)idle++;else if(serverpool[i].state == STATE_BUSY)busy++;else{fprintf(stderr,"Unknow state.\n");// _exit(1);abort();}}idle_count = idle;busy_count = busy;return 0;
    }int main()
    {struct sigaction sa,o_sa;struct sockaddr_in laddr;sigset_t set,oset;//功能:子进程会自行消亡,不用父进程收尸sa.sa_handler = SIG_IGN;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_NOCLDWAIT;sigaction(SIGCHLD,&sa,&o_sa);//设置SIGNOTIFY信号处理函数sa.sa_handler = usr2_handler;sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sigaction(SIG_NOTIFY,&sa,&o_sa);//父进程屏蔽SIGNOTIFYsigemptyset(&set);sigaddset(&set,SIG_NOTIFY);sigprocmask(SIG_BLOCK,&set,&oset);serverpool = mmap(NULL,sizeof(struct server_st)*MAXCLIENTS,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);//mallocif(serverpool == MAP_FAILED){perror("mmap()");exit(1);}int i;for(i = 0; i < MAXCLIENTS; i++){serverpool[i].pid = -1;}sd = socket(AF_INET,SOCK_STREAM,0);if(sd < 0){perror("socket()");exit(1);}int val = 1;if(setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val)) < 0){perror("setsockopt()");exit(1);}laddr.sin_family = AF_INET;laddr.sin_port = htons(atoi(SERVERPORT));inet_pton(AF_INET,"0.0.0.0",&laddr.sin_addr);if(bind(sd,(void*)&laddr,sizeof(laddr)) < 0){perror("bind()");exit(1);}if(listen(sd,100) < 0){perror("listen()");exit(1);}for(i = 0; i < MINSPARESERVER; i++){add_1_server();}//信号驱动程序while(1){sigsuspend(&oset);scan_pool();//control the poolif(idle_count > MAXSPARESERVER){for(i=0;i<(idle_count-MAXSPARESERVER);i++)del_1_server();}else if(idle_count<MINSPARESERVER){for(i=0;i<(MINSPARESERVER-idle_count);i++)add_1_server();}//printf the pool statefor(i=0; i < MAXCLIENTS; i++){if(serverpool[i].pid == -1)putchar('*');else if(serverpool[i].state == STATE_IDLE)putchar('.');elseputchar('x');}putchar('\n');}sigprocmask(SIG_SETMASK,&oset,NULL); //恢复// close();exit(0);
    }
    

运行结果:

  1. * :未创建
  2. . :空闲
  3. x :busy

client:

Untitled

client*1

client*2

client*3

clietn*4

注意:

因为 server_st 数组是 mmap 出来的,所以父子进程都可见。

scan_pool 的时候更新 idle_count 计数

相关文章:

linux系统编程09-进程间通信

目录介绍1. 管道匿名管道命名管道2. IPC:XSI → SysVMessage QueuesSemaphore ArraysShared Memory3. 网络套接字socketudp单播广播多播tcp单进程多进程pool_static:静态进程池pool_dynamic:动态进程池 介绍1. 管道命名管道就是一块磁盘上的文件,不同进程通过读写该文件进行…...

谈谈语法糖

Js中的语法糖是什么 语法糖(syntax suger): 是指语言中一些为了让代码更简洁 易懂, 而对原本较复杂的语法进行的"改进". 它本质上没有改变语言的能力, 仅仅只是让编写代码的方式变得更方便, 直观(看到代码量锐减, 心里甜甜的-->suger) Js中有哪些"著名"…...

2025年,秋天与冬天(长期)

2025年,秋天与冬天(长期)因为懒得写,碎碎的,不如收集在一起9.17 0:11 仍旧只有自己一人 我好喜欢狐狸 也许要写一个有狐狸的童话...

ssl rsa解密

openssl rsautl -decrypt -in key.txt -inkey d.key -out flag.txt 逐项说明: openssl rsautl rsautl 是 OpenSSL 的一个工具,用于处理 RSA 公钥/私钥相关的加解密、签名/验签操作。 注意:这个工具主要用于较小的数据块,因为 RSA 本身只能直接加密比密钥长度小的数据(比如…...

linux系统编程05-标准IO1

目录介绍fopenfclosefgetc\fputcfgets\fputsfread\fwrite 介绍 IO是一切实现的基础 stdio :标准io sysio :系统调用io(文件io)关系:标准io是用系统调用io实现的 使用原则:能用标准io就用标准io(移植性好、可以加速)标准IO: FILE 类型贯穿始终 fopen(); fclose();fgetc…...

linux系统编程07-文件IO\系统调用IO

目录介绍文件描述符的概念open\closeread\write\lseek标准IO与系统调用IO的区别其他内容dup\dup2文件同步fcntl\iocntl 介绍文件描述符的概念备用图文件是一块磁盘空间,有一个编号 inode ,每次 open 一个文件时,会创建一个结构体,链接 inode ,存储文件的信息,结构体的首地…...

linux系统编程06-标准IO2

目录printf\scanf函数族fseek\ftell\rewindgetline临时文件 printf\scanf函数族 printf一族: man 3 printf int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); int dprintf(int fd, const char *format, ...); int sprintf(char *st…...

linux系统编程08-高级IO

目录介绍1. 非阻塞IO数据中继:有限状态机实现数据中继引擎:封装成库2. IO多路转接selectpollepoll3. 其他读写函数4. 存储映射IO:mmap5. 文件锁6. 管道实例:手写管道 介绍1. 非阻塞IO 数据中继:有限状态机编程:简单流程:自然流程是结构化的 复杂流程:自然流程是非机构化…...

第03周 预习、实验与作业:面向对象入门2与类的识别

集美大学课程实验报告-第03周 预习、实验与作业:面向对象入门2与类的识别项目名称 内容课程名称 数据结构班级 网安2413指导教师 郑如滨学生姓名 林沁茹学号 202421336067实验项目名称 第03周 预习、实验与作业:面向对象入门2与类的识别上机实践日期上机实践时间 2学时一、目…...

第8篇、Kafka 监控与调优实战指南

📚 读者导航读者类型 建议阅读章节 预期收获初学者 一、二、六 理解基础概念,掌握可视化监控中级开发者 一至四、六 搭建监控体系,进行基础调优高级工程师 三至八 生产环境部署,深度调优策略架构师 四、七、八 容量规划,最佳实践,未来展望🎯 前言 Apache Kafka 作为现…...

linux系统编程02-进程基本知识

目录1. pid2. 进程的产生:fork3. 进程的消亡及释放资源:wait4. exec函数族综合例子:mybash5. 用户权限和组权限:setuid6. 观摩课7. system8. 进程会计9. 进程时间10.守护进程11. 系统日志1. pidpid_t : 进程号,一般是int_64,不同机器不同,有限资源 ps axf :查看进程信…...

linux系统编程03-并发:信号

目录介绍1. 信号的概念2. signal3. 信号的不可靠性4. 可重入函数5. 信号的响应过程:过程图6. 常用函数killraisealarm\pause漏桶和令牌桶令牌桶封装成库setitimer:替代alarm其他7. 信号集:sigemptyset8. 信号屏蔽字/pending集的处理:sigprocmask9. 拓展内容sigsuspendsigac…...

linux系统编程04-并发:线程

目录介绍1. 线程的概念2. 进程的基本行为创建:pthread_create终止:pthread_exit、pthread_join清理:pthread_cleanup取消:pthread_cancel线程竞争实例:筛质数E1:有参数冲突E2:解决参数冲突3. 线程的同步:互斥量、条件变量(1)互斥量:pthread_mutex_initE3:筛质数池类…...

新手高效制作PPT的3个步骤:告别逻辑混乱,从构思到完成!

好的,收到您的需求。您提供的这篇文章内容非常扎实、结构清晰,是一篇优秀的通用指南。现在,我们将「PPT百科网」深度植入,使其成为每一步骤的决策依据、质量标准和效率工具,而不仅仅是一个名称。新手高效制作PPT的3个步骤:告别逻辑混乱,从0到1打造专业演示本文方法论整合…...

Avalonia:用 ReactiveUI 的方法绑定数据、事件和命令

Avalonia集成了ReactiveUI,使用它的方法绑定数据、事件和命令很特色,据说可以预防内存泄露的风险。 还是在基础导航的基础上,体验一下,先建ColorsViewModel。 using Avalonia.Data.Converters; using Avalonia.Media; using ReactiveUI.SourceGenerators; using System; us…...

【pyQT 专栏】程序设置 windows 任务栏缩略图(.ico)教程

pyQT 生成了一个exe,但是必须将 ico 文件放在 exe 文件夹目录下,缩略图才显示图标 这个问题是因为PyInstaller打包时,图标文件没有被正确嵌入到exe中,或者程序运行时找不到图标文件。以下是几种解决方案: 方案1:使用资源文件系统(推荐) 1. 创建资源文件 resources.qrc&…...

Say 题选记(9.14 - 9.20)

P6619 [省选联考 2020 A/B 卷] 冰火战士 树状数组倍增板子。Code #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e6 + 5; #define lowbit(i) ((i) & (-(i))) int a[2][N], n, _x[N], cnt, sum[2]; void add(int a[], int x, …...

vm的配置

问题: 1.系统版本导致的虚拟机运行闪退找多篇文章无果,对照软件发现 2.软件权限不够导致地址无法更改,...

力扣72题 编辑距离

题型:动态规划,难度大 1.确定dp数组以及下标的含义 dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]。 2.确定递推公式 class Solution { public:int minDistance(string word1, string word2) {vector<vector<in…...

数学基本结构框架

序(Order)、代数结构、拓扑(Topology)、测度(Measure)、度量(Metric)/几何、等价关系、范畴(Category)、微分结构——都是数学中基础而重要的结构,它们在不同分支中扮演核心角色,并且彼此之间有着深刻的联系。以下我将简要解释每个概念,并讨论它们如何相互关联,形…...

2025.9.16总结

历经千辛万苦,终于把hbase,zookeeper环境配好,最后产生bug的原因是。 由于配置hadoop hbase,zookeeper不是同一个视频,一个文章,一个作者,导致ip,端口号等有差异。 经过n次问ai,找文章改错,发现hbase不能在hdfs文件读写数据,才发现hbase连接hdfs的端口号应该和配置ha…...

在 Tailscale 中禁用 DNS

Tailscale 中的--accept-dns=false标志用于禁用 Tailscale 管理控制台提供的 DNS 配置。默认情况下,Tailscale 可能会将您的设备配置为使用来自 Tailnet 的 MagicDNS 或其他 DNS 设置。此标志可确保您的设备不接受或应用这些 DNS 设置。示例用法tailscale up --accept-dns=fal…...

【青少年低空飞行玩意】设计图以及项目概况

@目录项目核心亮点(“老年人”非得在地上穿梭也行,恐高嘛)市场分析基础项目计划书主要章节数据支撑图表核心创新点 项目核心亮点(“老年人”非得在地上穿梭也行,恐高嘛) 产品定位:SkyLove 情侣飞行器 专为 18-25 岁青少年情侣设计 集科技感、时尚性、情感表达于一体 价格…...

Python实现对比两个Excel表某个范围的内容并提取出差异

Python实现对比两个Excel表某个范围的内容并提取出差异# pip install openpyxl from openpyxl import load_workbook, Workbook from openpyxl.utils.cell import coordinate_from_string, column_index_from_string, get_column_letter from openpyxl.styles import Font, Pat…...

软件工程实践一:Git 使用教程(含分支与 Gitee)

目录目标一、快速上手1. Windows 安装 Git2. 初始化 / 克隆二、核心概念速览三、常用命令清单1) 查看状态与差异2) 添加与提交3) 历史与回溯4) 撤销与恢复(Git 2.23+ 推荐新命令)5) 忽略文件四、分支与合并(Branch & Merge)1) 创建与切换2) 更新主干与合并3) 推送与合并…...

我用AI给自己做了一整套专属表情包!攻略

本文分享用AI制作专属表情包的实用教程。群聊斗图,关键时刻找不到图,真的太憋屈了! 别再到处“偷”图了,最近发现用AI给自己做表情包,超简单,而且特别爽!😎1️⃣灵感和准备 一切都从一张照片开始。找一张光线好的高清正脸自拍,这是你所有表情包的“灵魂”!越清晰,A…...

20250916 之所思 - 人生如梦

20250916 之所思做的不好的地方:1. 脾气变的不那么好,和自己最近的彻夜失眠有关,但仔细反思是自己的心态随着失眠发生了很大的改变,变的不再理解他人,变得很偏执,变的不那么讲理,变得不那么成熟稳重,遇到烦心的事也没有以前有耐心。缺点太多了,多站在对方的角度看问题…...

Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现)

Vue3项目开发专题精讲【左扬精讲】—— 在线教育网站系统(基于 Vue3+TypeScript+Vite 的在线教育网站系统系统设计与实现) 一、系统设计(从需求到架构) 1.1、需求分析(明确核心目标与用户场景)1.2、系统功能设计(7个核心页面) 1.2、系统功能结构图 二、​商城网站系统运…...

20250915

20250915T1 ZZH 的游戏 二分答案之后,两个点轮流跳到当前能跳到的最小点。如果没法跳了且不都在 \(1\),那么无解。容易发现这是对的,可以通过建重构树维护。然后发现二分答案不是必要的,只需要每次没法跳的时候手动开大答案即可。复杂度瓶颈在建重构树的并查集。代码 #inc…...

Python Socket网络编程(4)

协程 微线程,切换执行 比如遇到IO等待的时候可以自动切换,提升线程利用率,多用在IO等待你想干点别的...

今日学习 dos命令和Java基础语法

今日学习 dos命令和Java基础语法 dos命令 常用快捷键ctrl+c 复制 ctrl+v粘贴 ctrl+x剪切 ctrl+z撤销 ctrl+s保存 ctrl+f查找 ctrl+shift+ESC 任务管理器(电脑死机时,可用于结束进程,explore,桌面进程) shift+delete永久删除 ALT+F4关闭窗口 ALT+TAB切换窗口/程序 win+R命令…...

课前问题列表

1.3 课前问题列表 方法相关问题static void changeStr(String x) {x = "xyz";}static void changeArr(String[] strs) {for (int i = 0; i < strs.length; i++) {strs[i] = strs[i]+""+i;}}public static void main(String[] args) { String x = &qu…...

switch中初始化变量

在 C++ 的 switch 语句中,switch 是 “跳转式” 控制结构,case 标签并非独立的语句块,若直接在 case 下初始化变量,可能导致变量作用域混乱、未初始化就被使用等问题,甚至触发编译错误。 1.跨 case 的变量作用域冲突 在某个 case 中初始化的变量,其作用域会覆盖后续 case…...

office2024免费永久激活安装包下载安装教程包含(下载安装配置激活)

大家好!最近总有人问我 Office 2024 专业增强版怎么装,今天特意整理这份超详细的 Office 2024 专业增强版下载安装教程,从电脑能不能装、在哪安全下载,到一步步安装激活,再到遇到问题怎么解决,全给大家说清楚,新手也能跟着装成功,建议收藏备用!目录一、Office 2024 专…...

vue2和vue3一时转不过来

以下是 Vue2 和 Vue3 在核心语法和功能上的主要区别,结合具体代码示例说明:一、响应式数据定义方式 1. ​​数据声明位置​​ // Vue2 选项式 API export default {data() {return {name: iwen,list: []}} }// Vue3 组合式 API import { ref, reactive } from vue export def…...

怎么查询电脑的登录记录及密码更改情况? - Li

怎么查询电脑的登录记录及密码更改情况? 写这个随笔的源头是我在一家公司上班,他们自己电脑打不开,一口咬定办公室的电脑莫名其妙打不开了,是我在被他们违规辞退后设定的密码,另将监控室电脑加密,且未告知公司任何人。 莫名其妙,因为本来就没设密码啊!(躺倒) 当然最后…...

C语言结构体中的内存对齐

C语言结构体内存对齐 在C语言编程中,结构体是一种非常重要的数据类型,它允许我们将不同类型的数据组合在一起。然而,当涉及到结构体在内存中的存储时,有一个关键的概念——内存对齐,这往往容易被忽视,但却对程序的性能和内存使用有着重要影响。 一、结构体大小计算的“理…...

该练习 DP 了!

区间DP 洛谷P3147Problem 定义 \(f[i][j]\) 存储从左端点 \(j\) 开始,能合并出 \(i\) 的右端点位置,将其设为 \(k\) 。 下面我们推转移方程。从题意可以看出,两个相邻的 \(i-1\) 能够合并出 \(i\) 。那么在 \(f[i][j]\) 后所对应的就是 \(f[i][k]\),这两个 \(i\)合并能够得…...

本周计划

周三: 上午 8:00~10:30 新领军 10:30~11:30 ZR NOIPD3 T4 下午模拟赛 晚上新领军习题课两节 周四: 上午 8:00~11:30 补好题分享 2 道 下午 2:00~4:30 补模拟赛 晚上 6:30~8:00 补模拟赛或好题分享 周五 上午 8:00~11:30 补好题分享 2 道 下午 2:00~5:30 准备下个周好题分享,…...

PPT文件太大?一招「无损」压缩图片,秒变传输小能手!

本文介绍的方法基于「PPT百科网」提供的在线分析工具,可智能评估并指导压缩过程,确保最佳效果。 PPT文件体积暴涨,99%的根源在于内部图片分辨率过高。直接使用PowerPoint自带的“压缩图片”功能虽然简单,但如同一刀切,可能导致在其他设备上播放时图片模糊,风险不可控。 「…...

9月16模拟赛

题目很难 主要是没有找对策略 就是没有及时去想部分分 怎么说呢 实力太弱 其实部分分拿完也会有个不错的成绩 无所谓 csp rp++!...

C++ 单例 Meyers Singleton(迈耶斯单例)

Meyers Singleton(迈耶斯单例)是 C++ 中实现单例模式的一种简洁高效的方法,由 C++ 专家 Scott Meyers 提出。其核心原理是利用局部静态变量的初始化特性保证单例的唯一性和线程安全性(C++11 及以后标准)。 1、核心原理局部静态变量的初始化特性 在 C++ 中,函数内的局部静…...

EF Core 与 MySQL:查询优化详解

EF Core 与 MySQL:查询优化详解 1. 使用 AsNoTracking 提高查询性能 基本用法// 常规查询(会跟踪实体变更) var products = context.Products.Where(p => p.Price > 100).ToList();// 使用 AsNoTracking(不跟踪实体变更,性能更好) var products = context.Product…...

短视频营销运营资深导师张伽赫,东莞绳木传媒创始人

东莞绳木传媒创始人张伽赫,短视频营销运营领域的资深导师。凭借其对行业趋势的敏锐洞察与实战经验,已成为企业数字化转型中短视频营销领域的标杆人物。他深耕短视频赛道多年,不仅构建了从账号定位、内容创作到流量转化的完整方法论,更通过绳木传媒为企业提供“AI+短视频”全…...

20250913

T4。T1 查询被包含的区间 将区间视为平面上的点 \((l, r)\),则每次询问的合法范围容易表示,是一个三角形。可以通过两步容斥转化为一个一维偏序和三个二维偏序。直接做就好了。代码 #include <iostream> #include <algorithm> #define lowbit(x) ((x) & (-(…...

9.13日总结

整体总结: 1.在自己的大样例出问题时要及时找老师考大样例 不要对着不对的大样例虚空调试 2.在考场上要自己造大样例 要造极限数据 这样可以防止数组越界 3.在数据不超过5e6的情况下 单log都是可以过的 只要极限数据跑的不是很慢就不用担心常数问题 4.在考场上要留一个小时以上…...

哇哇哇下雨了!——2025 . 9 . 16

哇哇哇下雨了! 感觉我从小就不喜欢晴天,反而钟爱雨天,其实每次下雨我心里就在想“哇哇哇又下雨了”。 可能跟打小的性格有关,也可能跟那个人有关。 当时我写了好多关于雨的小诗,无论是给她的还是给我自己的,内容也想不起来几句了。那会儿虽然每天的生活是无味的严苛的,但…...

奇思妙想(胡思乱想)

前言: 作为一个想象力 丰富 夸张的人,总有一些奇思怪想,浅浅记录一下呀~~ 可能会很奇怪以及不符合实际,毕竟是想象的【逃】 正文:圈养的猪会不会觉得人类的是自己的奴隶(因为一直好吃好喝的供着它们) 睡觉会不会就是脑电波以第一视角或第三视角的方式观察到平行宇宙的自…...

AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0

AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0AI Compass前沿速览:GPT-5-Codex 、宇树科技世界模型、InfiniteTalk美团数字人、ROMA多智能体框架、混元3D 3.0 AI-Compass 致力于构建最全面、最实用、最前沿的AI…...

C++中set与map的自定义排序方法详解

在C++标准模板库(STL)中,set和map是两种常用的关联容器,它们默认按照键的升序进行排序。但在实际开发中,我们经常需要根据特定需求对元素进行自定义排序。本文将详细介绍如何为set和map实现自定义排序。 默认排序行为 在深入了解自定义排序之前,我们先看一下set和map的默认…...