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

网站改版流程怎样在百度上发布免费广告

网站改版流程,怎样在百度上发布免费广告,外贸营销型网站建站,徐汇做网站公司练习题 本节所有代码均为本人手写, 不出意外将全部可以正确执 建议读者学习完基本课程后, 最后统一复习用,建议读者先根据题目自己写, 有问题自己先调试 文章目录 练习题错误号返回值考虑EAGIN: 非阻塞状态下, 操作暂时无法完成, 会自动重试EINTR: 系统调用被信号中断。处理方…

练习题

本节所有代码均为本人手写, 不出意外将全部可以正确执
建议读者学习完基本课程后, 最后统一复习用,建议读者先根据题目自己写, 有问题自己先调试

文章目录

  • 练习题
    • 错误号返回值考虑
      • EAGIN: 非阻塞状态下, 操作暂时无法完成, 会自动重试
      • EINTR: 系统调用被信号中断。处理方式通常是重新调用
      • mutex增加一个: EBUSY, 在互斥锁中, 表示锁已经被其他进程或线程持有,当前无法获取。
    • 目录操作
      • 实现递归遍历目录
      • ls-R.c
      • ls-R2.c
    • 循环创建n个子进程并使用wait回收
      • 虽然wait有点复杂,但是wait(NULL)更方便,不关心子进程退出状态
    • 使用pipe管道 实现简单的进程间通讯
      • 父子进程间通信 ls | wc -l
      • 兄弟进程间通信
      • loop-fork.c
    • 写一个简单的 fifo管道
      • 实现了 非血缘关系, fifo通讯
      • 这里自己写, 发现了 细节
      • pipe和fifo读写行为, 详看快速复习,有区别
      • 1-pipe.c
      • 2-fifo.c
      • 3-fifo.c
    • 共享内存映射-复杂
      • 1. 实现 无血缘关系 进程通信
      • 2. 实现 父子进程 普通映射通讯
      • 3. 实现 父子进程 匿名映射通讯
      • 1-mmap.c
      • 2-mmap.c
      • 3-mmap.c
      • 4-mmap.c
    • 简单的信号集操作函数例子
      • 将SIGINT,SIGQUIT加入信号集并屏蔽, 并循环打印 未决信号集
      • signal.c
    • 信号捕捉例子
      • 捕捉 SIGINT,并回调打印
      • 注意 测试sa_mask的作用
      • 1-sigaction.c
    • SIGCHILD信号回收子进程
      • sigchild.c
    • 手写一个简单的 守护进程
      • 使用子进程 创建新会话,然后创建守护进程
      • 1-setsid.c
    • 循环创建n个子线程
      • 并 循环回收---难点
      • 1-loop-pthread.c
      • 2-pthread-join-error.c
      • 3-pthread-join-true.c
    • 使用 pthread_cancel函数取消线程
      • 使用默认系统调用 作为取消点
      • 使用 指定的pthread_testcancel()作为取消点
      • pthread-cancel.c
    • 设置 线程分离, 并 jion回收, 查看出错
      • pthread-detach.c
    • 使用线程 属性设置 分离态
      • pthread-attr.c
    • 简单的 互斥锁(4个)
      • 不使用锁的效果
      • 正常加锁解锁
      • tyrlock 替换进去看效果
      • 营造死锁的 问题
      • 1-mutex-no-lock.c
      • 2-mutex-lock.c
      • 3-mutex-trylock.c
      • 4-mutex-si.c
    • 条件变量---重点
      • 生产者和消费者模型
      • producer-customer.c
    • 信号量
      • 实现这个的 生产者和消费者模型, 有点不同
      • 只使用 信号量的 例子,
      • 理解为什么 可以 只使用 信号量
      • sem-pro-cus.c
    • TCP/IP协议模型--练习题
      • server.c
      • client.c
      • 1-socket-server.c
      • 2-socket-client.c
    • 多进程并发 and 多线程并发
      • 特别注意: 多进程并发时, 一定要 对accept 进行判断, 都则可能会 返回-1,还在执行, 会一直打印 read error
      • 特别注意: 端口号, 一定要找 没有被占用的, 否则 也会 一直打印 read error
      • 1-more-fork-socket.c
      • 4-more-pthread-socket.c
    • epoll 服务-客户练习
      • 做 多客户端链接的 服务端, 注意 epoll函数的使用
      • 1-epoll-server.c
      • 2-epoll-client.c

错误号返回值考虑

EAGIN: 非阻塞状态下, 操作暂时无法完成, 会自动重试

EINTR: 系统调用被信号中断。处理方式通常是重新调用

mutex增加一个: EBUSY, 在互斥锁中, 表示锁已经被其他进程或线程持有,当前无法获取。

目录操作

实现递归遍历目录

ls-R.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>void Is_file(char *name);void prr(char *str)
{perror(str);exit(1);
}void Read_dir(char *dir)  
{char buf[256];DIR *dp;struct dirent *sdp;dp = opendir(dir);  // 打开目录if(dp == NULL){prr("opendir error");}sdp = readdir(dp);  // 每读一次, 会向后移动if(sdp == NULL){prr("readdir error");}while(sdp != NULL){if(strcmp(sdp->d_name , ".") == 0 || strcmp(sdp->d_name , "..") == 0)   //忽略 .和..   字符串比较不能使用 ==, 是函数{sdp = readdir(dp);continue;}if((strlen(dir) + strlen(sdp->d_name)) +1 < 256)   // +1 表示 /{snprintf(buf, sizeof(buf), "%s/%s", dir, sdp->d_name);   // 这个函数 打印并拼接Is_file(buf);  // 注意, 必须传进去 正确的 相对路径或者 绝对路径,  否则 找不到路径}else{printf("dir too long: %s/%s", dir, sdp->d_name);}sdp = readdir(dp);}closedir(dp);}void Is_file(char *name) // 是文件, 打印路径和大小, 不是文件, 去递归
{struct stat stat_1;   // 获取name属性int ret = stat(name, &stat_1);if(ret < 0){prr("stat error");}if(S_ISDIR(stat_1.st_mode)){Read_dir(name);}printf("%10s\t%ld\n", name, stat_1.st_size);}int main(int argc, char *argv[])
{   if(argc==1){Is_file(".");  // 无参时,默认显示 当前目录}else{Is_file(argv[1]);}return 0;
}

ls-R2.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <dirent.h>// 修改使用 函数指针 进行调用void Is_file(char *name);void prr(char *str)
{perror(str);exit(1);
}void Read_dir(char *dir, const void(*func)(char *)) 
{char buf[256];DIR *dp;struct dirent *sdp;dp = opendir(dir);  // 打开目录if(dp == NULL){prr("opendir error");}sdp = readdir(dp);  // 每读一次, 会向后移动if(sdp == NULL){prr("readdir error");}while(sdp != NULL){if(strcmp(sdp->d_name , ".") == 0 || strcmp(sdp->d_name , "..") == 0)   //忽略 .和..   字符串比较不能使用 ==, 是函数{sdp = readdir(dp);continue;}if((strlen(dir) + strlen(sdp->d_name)) +1 < 256)   // +1 表示 /{snprintf(buf, sizeof(buf), "%s/%s", dir, sdp->d_name);   // 这个函数 打印并拼接Is_file(buf);  // 注意, 必须传进去 正确的 相对路径或者 绝对路径,  否则 找不到路径}else{printf("dir too long: %s/%s", dir, sdp->d_name);}sdp = readdir(dp);}closedir(dp);}void Is_file(char *name) // 是文件, 打印路径和大小, 不是文件, 去递归
{struct stat stat_1;   // 获取name属性int ret = stat(name, &stat_1);if(ret < 0){prr("stat error");}if(S_ISDIR(stat_1.st_mode)){Read_dir(name, Is_file);}printf("%10s\t%ld\n", name, stat_1.st_size);}int main(int argc, char *argv[])
{   if(argc==1){Is_file(".");  // 无参时,默认显示 当前目录}else{Is_file(argv[1]);}return 0;
}

循环创建n个子进程并使用wait回收

虽然wait有点复杂,但是wait(NULL)更方便,不关心子进程退出状态

使用pipe管道 实现简单的进程间通讯

父子进程间通信 ls | wc -l

  • 父进程将ls的内容发到管道
    子进程用wc -l读取, wc本身带了读取功能
    本联系将会输出 行数

兄弟进程间通信

loop-fork.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>int main(int argc, char *argv[])
{   pid_t pid, wpid;int status;int i = 1;while(i<=5){pid=fork();if(pid < 0){perror("fork error");exit(1);}else if(pid == 0){break;}sleep(1);i++;}if(pid > 0){printf("i am parent, pid is %d\n", getpid());i--;while(i > 0){wpid = wait(&status);  // wait 应用if(wpid < 0){perror("wait error");exit(1);}// status获取if(WIFEXITED(status)){printf("child %d exit with %d\n",i,  WEXITSTATUS(status));}if(WIFSIGNALED(status)){printf("child %d KILL with %d\n",i, WTERMSIG(status));}printf("parent wait child %d, wait pid is %d\n",i ,wpid);i--;}}else{printf("i am %d child, pid is %d, my parent pid is %d\n",i, getpid(), getppid());sleep(10);  //测试 回收每个子进程}return 0;
}

写一个简单的 fifo管道

实现了 非血缘关系, fifo通讯

这里自己写, 发现了 细节

  • 对于fifo文件, open时, 只读,只写,读写, 就表示了 是打开读端, 打开写端, 还是都打开,

这就相当于 pipe的 fd[0]和fd[1]!!!

  • 按理说, 当生产者只打开写端,消费者只打开读端,时

只打开生产者, 由于 两个读端都关闭, 因此会阻塞在write
当生产者以 RDWR打开,将会读写端都打开, 就不会产生阻塞了

pipe和fifo读写行为, 详看快速复习,有区别

1-pipe.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>int main(int argc, char *argv[])
{   int pipefd[2];pid_t pid;int ret = pipe(pipefd);if(ret<0){perror("pipe error");}pid = fork();if(pid<0){perror("fork error");}else if(pid>0){close(pipefd[0]);  // 关闭读dup2(pipefd[1], STDOUT_FILENO);  // 重定向, ls后会直接进入 pipeexeclp("ls","ls", "-l", NULL); // 父进程实现 ls功能, -l 会一行显示一个,并且加了total这一行    默认的, 是有换行符, 虽然一行多个perror("execlp pipefd[1] error");close(pipefd[1]);wait(NULL);}else if(pid==0){close(pipefd[1]); //关闭写dup2(pipefd[0], STDIN_FILENO); // 重定向, 将读出来的传到标准输入execlp("wc", "wc", "-l", NULL);  // wc 实际上是从管道中读取数据。也就 不需要显示调用 读了perror("execlp pipefd[0] error");close(pipefd[0]);}return 0;
}

2-fifo.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>int main(int argc, char *argv[])
{   int fd;// fd = mkfifo("fifoipc.txt")   // 错误的, mkfifo 成功0, 失败 errorint ret = mkfifo("myfifo", 0664);if(ret < 0 && errno!=EEXIST){perror("mkfifo error");exit(1);}fd = open("myfifo", O_WRONLY);if(fd < 0){perror("mkfifo error");exit(1);}write(fd, "hello-1!",strlen("hello-1!"));close(fd);return 0;
}

3-fifo.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>int main(int argc, char *argv[])
{   int fd;char buf[1024];// fd = mkfifo("fifoipc.txt")   // 错误的, mkfifo 成功0, 失败 error// int ret = mkfifo("myfifo", 0664);// if(ret < 0 && errno!=EEXIST)  //忽略文件存在错误// {//     perror("mkfifo error");//     exit(1);// }fd = open("myfifo", O_RDWR);if(fd < 0){perror("open error");exit(1);}int n= read(fd, buf, 1024);while(n>0){n = read(fd, buf, 1024);write(STDOUT_FILENO, buf, n);}close(fd);return 0;
}

共享内存映射-复杂

1. 实现 无血缘关系 进程通信

2. 实现 父子进程 普通映射通讯

  • 注意: mmap返回值, 如果是 结构体, 返回 结构体指针

3. 实现 父子进程 匿名映射通讯

1-mmap.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <stddef.h> // 包含 offsetof 宏// 父子 mmap 普通通信,  父写, 子读struct student 
{int id;char name[256];int score;
};int main(int argc, char *argv[])
{   struct student st = {1, "hzh", 95};int fd;fd = open("1-mmap-txt", O_CREAT | O_RDWR | O_TRUNC, 0664);ftruncate(fd, sizeof(st)); // 将文件 大小 与 数据大小 一样// struct student *p = mmap(NULL, sizeof(st), PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);int *p = mmap(NULL, sizeof(st), PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);if(p == MAP_FAILED){perror("mmap error");exit(1);}pid_t pid;pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid > 0){while(1){memcpy(p, &st, sizeof(st)); //每次修改, 可以看是不是 反映到了 磁盘上st.id++;sleep(2);}}else if(pid == 0){while(1){// printf("id is %d, name is %s\n", p->id, p->name);int *s = (int *)((char *)p+offsetof(struct student, score));   /*offsetof 是一个宏定义,它用于计算结构体成员相对于结构体起始位置的偏移量。返回值是 字节类型, 所以要强转*/printf("id is %d, name is %s, score is %d\n", *p, (char *)(p+1), *s);sleep(2);}}close(fd);munmap(p, sizeof(st));return 0;
}

2-mmap.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>//无血缘关系 mmap通讯int main(int argc, char *argv[])
{   int fd;char *buf = "hello---------\n";fd = open("2-mmap-txt", O_CREAT | O_RDWR, 0664);if(fd < 0){perror("open error");exit(1);}int *p = mmap(NULL, sizeof(buf), PROT_WRITE, MAP_SHARED, fd, 0);if(p == MAP_FAILED){perror("mmap error");exit(1);}printf("%ld\n", strlen(buf));write(fd, buf, strlen(buf));close(fd);munmap(p, sizeof(buf));return 0;
}

3-mmap.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>//无血缘关系 mmap通讯int main(int argc, char *argv[])
{   int fd, n;char buf[1024];fd = open("2-mmap-txt", O_CREAT | O_RDWR, 0664);if(fd < 0){perror("open error");exit(1);}int *p = mmap(NULL, sizeof(buf), PROT_READ, MAP_SHARED, fd, 0);if(p == MAP_FAILED){perror("mmap error");exit(1);}while((n=read(fd, buf, 1024))>0){printf("%ld\n", n);write(STDOUT_FILENO, buf, n);}close(fd);munmap(p, sizeof(buf));return 0;
}

4-mmap.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <stddef.h> // 包含 offsetof 宏// 匿名映射struct student 
{int id;char name[256];int score;
};int main(int argc, char *argv[])
{   struct student st = {1, "hzh", 95};// struct student *p = mmap(NULL, sizeof(st), PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);int *p = mmap(NULL, sizeof(st), PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_SHARED, -1, 0);if(p == MAP_FAILED){perror("mmap error");exit(1);}pid_t pid;pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid > 0){while(1){memcpy(p, &st, sizeof(st)); //每次修改, 可以看是不是 反映到了 磁盘上st.id++;sleep(2);}}else if(pid == 0){while(1){// printf("id is %d, name is %s\n", p->id, p->name);int *s = (int *)((char *)p+offsetof(struct student, score));   /*offsetof 是一个宏定义,它用于计算结构体成员相对于结构体起始位置的偏移量。返回值是 字节类型, 所以要强转*/printf("id is %d, name is %s, score is %d\n", *p, (char *)(p+1), *s);sleep(2);}}munmap(p, sizeof(st));return 0;
}

简单的信号集操作函数例子

将SIGINT,SIGQUIT加入信号集并屏蔽, 并循环打印 未决信号集

signal.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>// 信号集操作函数 使用void print_ped(sigset_t *pedset)
{int i;for(i=1; i<32;i++){if(sigismember(pedset, i)){putchar('1');}  //查看 信号是否在未决信号集里,并打印 代号else {putchar('0');}}printf("\n");
}int main(int argc, char *argv[])
{sigset_t set, oldset, pedset;int ret = 0;sigemptyset(&set); // 清空信号集sigaddset(&set, SIGINT);sigaddset(&set, SIGQUIT); // 加入信号集ret = sigprocmask(SIG_BLOCK, &set, &oldset); // 将信号集 设置为屏蔽if (ret == -1){perror("sigprocmask error");exit(1);}while (1){ret = sigpending(&pedset);  // 查看未决信号集   那些已经到达但由于被屏蔽而未处理的信号。if (ret == -1){perror("sigprocmask error");exit(1);}print_ped(&pedset);sleep(2);}return 0;
}

信号捕捉例子

捕捉 SIGINT,并回调打印

注意 测试sa_mask的作用

1-sigaction.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>void sig_catch(int signo)
{if(signo == SIGINT){printf("SIGINT is %d\n", signo);sleep(3);}else if(signo == SIGQUIT){printf("SIGQUIT is %d\n", signo);}return ;
}int main(int argc, char *argv[])
{   struct sigaction act,oldact;act.sa_handler = sig_catch;   // 回调函数act.sa_flags = 0;sigemptyset(&(act.sa_mask));sigaddset(&act.sa_mask, SIGQUIT);  //将 SIGQUIT 信号添加到 act.sa_mask 中,表示当信号处理程序(回调函数)执行时,SIGINT 信号将被阻塞,不会再被捕获,避免递归调用。    回调函数执行完后, 这个信号会收到, 由于没有捕捉, 将执行 默认动作// 不加这个时, 随时SIGQUIT, 都会退出// 加了这个, 在回调函数期间, 无反应, 完成后, 才会处理这个int ret = sigaction(SIGINT, &act, &oldact );  // 注册信号捕捉函数if(ret < 0){perror("sigaction error");exit(1);}while(1);return 0;
}

SIGCHILD信号回收子进程

sigchild.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>void sig_catch(int signo)
{pid_t wpid;int status;while((wpid = waitpid(-1, &status, 0)) != -1){if(WIFEXITED(status))   // 正常退出, 才打印{printf("waitpid pid is %d, ret = %d\n", wpid, WEXITSTATUS(status));  //打印回收的 pid}}return;}int main(int argc, char *argv[])
{   pid_t pid;sigset_t set, oldset;sigemptyset(&set);// 添加阻塞, 未进入 子进程时, 阻塞这个信号sigaddset(&set, SIGCHLD); int ret = sigprocmask(SIG_BLOCK, &set, &oldset);if(ret < 0){perror("sigprocmask error");exit(1);} printf("SIGCHLD is BLOCK\n");int i;for(i = 0; i<10; i++){pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid == 0){sleep(i);break;}else if(pid > 0){continue;}}if(i == 10){struct sigaction act, oldact;act.sa_handler = sig_catch;sigemptyset(&act.sa_mask);act.sa_flags = 0;ret = sigaction(SIGCHLD, &act, &oldact);if(ret < 0){perror("sigaction error");exit(1);} // 解除阻塞sigprocmask(SIG_UNBLOCK, &set, &oldset);if(ret < 0){perror("sigprocmask error");exit(1);} printf("SIGCHLD is UNBLOCK, i'm parent, pid is %d\n", getpid());while(1);}else{printf("i'm is child--%d, pid is %d\n", i, getpid());}return 0;
}

手写一个简单的 守护进程

使用子进程 创建新会话,然后创建守护进程

1-setsid.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>int main(int argc, char *argv[])
{   pid_t pid;int ret, fd;pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid > 0){exit(0);}pid = setsid(); // 创建会话/*setsid() 创建一个新的会话,并将当前进程从终端(控制终端)中分离出来,成为会话的领导进程。调用该函数后,进程就不再属于任何终端控制,避免后续信号(如 SIGHUP)干扰。*/if(pid < 0){perror("setsid error");exit(1);}ret = chdir("/root/hzhdata/");/*避免了进程保持对根目录(/)的依赖。如果进程工作目录依然在某个已挂载的文件系统上,系统重启或者卸载该文件系统时可能会出问题*/umask(0222);   // 头文件 sys/stat.hclose(STDIN_FILENO);  // 不能乱收东西fd = open("/dev/null", O_RDWR);if(fd < 0){perror("fd error");exit(1);}printf("fd is %d\n", fd);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);while(1);return 0;
}

循环创建n个子线程

并 循环回收—难点

1-loop-pthread.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// 循环创建子线程, 并验证 是不是共享全局变量int var = 100;void *tfn(void *arg)
{int i = (int)arg;var = 400;printf("thread--%d: pid = %d, tid is %lu, var = %d\n", i, getpid(), pthread_self(), var);sleep(2);printf("test ----%d\n", i);return NULL; 
}int main(int argc, char *argv[])
{   pthread_t tid;int i=0;var = 200;while(i<10){int ret = pthread_create(&tid, NULL, tfn, (void*)i);// int ret = pthread_create(&tid, NULL, tfn, (void*)&i);   // 容易导致 内容错乱if(ret < 0){fprintf(stderr, "pthread error: %s\n", strerror(ret));exit(1);}i++;sleep(1);}printf("main: pid is %d, tid is %lu, var = %d\n", getpid(), pthread_self(), var);pthread_exit((void*)0);  // 会退出 当前线程, 也就是只退出了 主线程// return 0;//线程不能使用这个   会出现 子线程还未结束, 整体 进程 就退出
}

2-pthread-join-error.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// pthread_join 测试
// 错误示范, 局部变量 地址 会被释放, 所以一直返回 0int var = 100;void *tfn(void *arg)
{int i = (int)arg;var = 400;printf("thread--%d: pid = %d, tid is %lu, var = %d\n", i, getpid(), pthread_self(), var);sleep(2);printf("test ----%d\n", i);int *r=&i;return (void*)r; 
}int main(int argc, char *argv[])
{   pthread_t tid;int i=0;var = 200;int *tidret;  while(i<10){int ret = pthread_create(&tid, NULL, tfn, (void*)i);if(ret < 0){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}ret = pthread_join(tid, (void **)&tidret);if(ret < 0){fprintf(stderr, "pthread_join error: %s\n", strerror(ret));exit(1);}printf("child %d return %d\n",i, *tidret);i++;sleep(1);}printf("main: pid is %d, tid is %lu, var = %d\n", getpid(), pthread_self(), var);pthread_exit((void*)0);  // 会退出 当前线程, 也就是只退出了 主线程// return 0;//线程不能使用这个   会出现 子线程还未结束, 整体 进程 就退出
}

3-pthread-join-true.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// pthread_join 测试
// 错误示范, 局部变量 地址 会被释放, 所以一直返回 0
// 这是正确示范   使用malloc 可以防止销毁int var = 100;void *tfn(void *arg)
{int i = (int)arg;var = 400;printf("thread--%d: pid = %d, tid is %lu, var = %d\n", i, getpid(), pthread_self(), var);sleep(1);printf("test ----%d\n", i);int *r = malloc(sizeof(int *));   *r = i;   // 不能传地址, 地址会释放, 传递值  注意不是  r=ireturn (void*)r; 
}int main(int argc, char *argv[])
{   pthread_t tid;int i=0;var = 200;// int *tidret;   // 外部容易 内存 泄漏while(i<10){int *tidret; int ret = pthread_create(&tid, NULL, tfn, (void*)i);if(ret < 0){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}ret = pthread_join(tid, (void **)&tidret);if(ret < 0){fprintf(stderr, "pthread_join error: %s\n", strerror(ret));exit(1);}printf("child %d return %d\n",i, *tidret);free(tidret);i++;sleep(1);}printf("main: pid is %d, tid is %lu, var = %d\n", getpid(), pthread_self(), var);pthread_exit((void*)0);  // 会退出 当前线程, 也就是只退出了 主线程// return 0;//线程不能使用这个   会出现 子线程还未结束, 整体 进程 就退出
}

使用 pthread_cancel函数取消线程

使用默认系统调用 作为取消点

使用 指定的pthread_testcancel()作为取消点

pthread-cancel.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
void *tfn1(void *arg)
{printf("thread 1 returning\n");return (void *)111;
}
void *tfn2(void *arg)
{printf("thread 2 exiting\n");pthread_exit((void *)222);
}
void *tfn3(void *arg)
{while (1){// printf("thread 3: I'm going to die in 3 seconds ...\n");  // sleep(1); //这两句 会进入系统调用, 到达 取消点, 若没有这两句, 需手动添加取消点pthread_testcancel(); //自己添加取消点*/}return (void *)666;
}
int main(void)
{pthread_t tid;void *tret = NULL;pthread_create(&tid, NULL, tfn1, NULL);pthread_join(tid, &tret);printf("thread 1 exit code = %d\n\n", (int)tret);pthread_create(&tid, NULL, tfn2, NULL);pthread_join(tid, &tret);printf("thread 2 exit code = %d\n\n", (int)tret);pthread_create(&tid, NULL, tfn3, NULL);sleep(3);pthread_cancel(tid);pthread_join(tid, &tret);printf("thread 3 exit code = %d\n", (int)tret);return 0;
}

设置 线程分离, 并 jion回收, 查看出错

pthread-detach.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>void *tf(void *arg)
{printf("exit ----- \n");return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid;int ret = pthread_create(&tid, NULL, tf, NULL);if(ret != 0){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}ret = pthread_detach(tid);if(ret != 0){fprintf(stderr, "pthread_detach error: %s\n", strerror(ret));exit(1);}sleep(2);  // 不等待,看不出区别ret = pthread_join(tid, NULL);if(ret != 0){fprintf(stderr, "pthread_join error: %s\n", strerror(ret));exit(1);}printf("main----\n");pthread_exit(NULL);
}

使用线程 属性设置 分离态

pthread-attr.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>void *fn(void *arg)
{printf("child-----\n");return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid;pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);int ret = pthread_create(&tid, &attr, fn, NULL);if(ret != 0){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}ret = pthread_join(tid, NULL);if(ret != 0){fprintf(stderr, "pthread_join error: %s\n", strerror(ret));exit(1);}pthread_attr_destroy(&attr);pthread_exit(NULL);
}

简单的 互斥锁(4个)

读写锁差不多,不写案例

不使用锁的效果

正常加锁解锁

tyrlock 替换进去看效果

营造死锁的 问题

1-mutex-no-lock.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// 无锁, 多个线程 会抢占资源, 不能按照顺序打印void *fn(void *arg)
{while(1){printf("hello--2\n");sleep(rand()%3);printf("world--2\n");sleep(rand()%3);}return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid;srand(time(NULL));int ret = pthread_create(&tid, NULL, fn, NULL);if(ret < 0 ){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}while(1){printf("HELLO--1\n");sleep(rand()%3);printf("WORLD--1\n");sleep(rand()%3);}pthread_join(tid, NULL);pthread_exit(NULL);}

2-mutex-lock.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// 无锁, 多个线程 会抢占资源, 不能按照顺序打印
// 加锁,  会 一个线程 一会占用 pthread_mutex_t mutex;  // 定义一个全局, 可以使得 所有线程 都能加锁解锁void *fn(void *arg)
{while(1){pthread_mutex_lock(&mutex);printf("hello--2\n");sleep(rand()%3);printf("world--2\n");pthread_mutex_unlock(&mutex);sleep(rand()%3);}return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid;srand(time(NULL));pthread_mutex_init(&mutex, NULL);int ret = pthread_create(&tid, NULL, fn, NULL);if(ret < 0 ){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));exit(1);}while(1){pthread_mutex_lock(&mutex);printf("HELLO--1\n");sleep(rand()%3);printf("WORLD--1\n");pthread_mutex_unlock(&mutex);sleep(rand()%3);}pthread_join(tid, NULL);pthread_mutex_destroy(&mutex);pthread_exit(NULL);}

3-mutex-trylock.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// 无锁, 多个线程 会抢占资源, 不能按照顺序打印
// 加锁,  会 一个线程 一会占用 pthread_mutex_t mutex;  // 定义一个全局, 可以使得 所有线程 都能加锁解锁void *fn(void *arg)
{if((pthread_mutex_trylock(&mutex) == 0)){printf("thread %ld: acquire lock\n", (long)arg);sleep(2);pthread_mutex_unlock(&mutex);}else{printf("thread %ld: none lock\n", (long)arg);}return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid,tid2;srand(time(NULL));pthread_mutex_init(&mutex, NULL);int ret = pthread_create(&tid, NULL, fn, (void *)1);pthread_join(tid, NULL);   // 这个位置决定了 1结束, 2才会创建, 因此都会打印 拿到锁ret = pthread_create(&tid2, NULL, fn, (void *)2);// pthread_join(tid, NULL);pthread_join(tid2, NULL);pthread_mutex_destroy(&mutex);pthread_exit(NULL);}

4-mutex-si.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>// 反复加锁, 会死锁, 一直阻塞pthread_mutex_t mutex;  // 定义一个全局, 可以使得 所有线程 都能加锁解锁void *fn(void *arg)
{if((pthread_mutex_lock(&mutex) == 0)){printf("thread %ld: acquire lock\n", (long)arg);sleep(2);// pthread_mutex_unlock(&mutex);}else{printf("thread %ld: none lock\n", (long)arg);}return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid,tid2;srand(time(NULL));pthread_mutex_init(&mutex, NULL);int ret = pthread_create(&tid, NULL, fn, (void *)1);pthread_join(tid, NULL);   // 这个位置决定了 1结束, 2才会创建, 因此都会打印 拿到锁ret = pthread_create(&tid2, NULL, fn, (void *)2);// pthread_join(tid, NULL);pthread_join(tid2, NULL);pthread_mutex_destroy(&mutex);pthread_exit(NULL);}

条件变量—重点

生产者和消费者模型

producer-customer.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>pthread_mutex_t mutex;
pthread_cond_t cond_product;struct product *head;
int i=10,j=10;
struct product
{int num;struct product *next;
};void Prr(int ret, char *str)
{if(ret < 0){fprintf(stderr, "%s error : %s\n",str, strerror(ret));exit(1);}
}void *Customer(void *arg)
{struct product *stc;while(i!=0){pthread_mutex_lock(&mutex); while(head == NULL) // 为空, 则条件变量, 阻塞等待   不使用if, 解除阻塞后, 需要再次检查, 若是多个customer, 可能为空{pthread_cond_wait(&cond_product, &mutex);}stc = head;head = head->next;printf("customer ------ %d\n", stc->num);free(stc);pthread_mutex_unlock(&mutex);sleep(rand()%4);i--;}return NULL;
}void *Productor(void *arg)
{struct product *st;while(j!=0){st = malloc(sizeof(struct product));st->num = rand()%1000;printf("product ------ %d\n", st->num);pthread_mutex_lock(&mutex);  // st 链表作为 共享资源, 因此改变链表需要加锁st->next = head;head = st;  // 头插法 head要一直保持在 开头,  新元素 next先指向head, head再指向新元素pthread_mutex_unlock(&mutex);pthread_cond_signal(&cond_product);  // 解锁并 唤醒sleep(rand()%4);j--;}return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid1,tid2;int ret = pthread_create(&tid1, NULL, Productor, NULL);  // 生产和消费的顺序, 决定了 在哪里  mallocPrr(ret,"create");ret = pthread_create(&tid2, NULL, Customer, NULL);Prr(ret, "create");pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_mutex_destroy(&mutex);pthread_cond_destroy(&cond_product);pthread_exit(NULL);
}

信号量

实现这个的 生产者和消费者模型, 有点不同

只使用 信号量的 例子,

理解为什么 可以 只使用 信号量

sem-pro-cus.c

/*信号量实现 生产者 消费者问题*/
// 如果缓冲区是一个固定大小的数组,且生产者和消费者只通过索引访问缓冲区,那么可以只用信号量来控制访问。
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>
#include <semaphore.h>#define NUM 5               int queue[NUM];                                     //全局数组实现环形队列
sem_t blank_number, product_number;                 //空格子信号量, 产品信号量void *producer(void *arg)
{int i = 0;while (1) {sem_wait(&blank_number);                    //生产者将空格子数--,为0则阻塞等待queue[i] = rand() % 1000 + 1;               //生产一个产品printf("----Produce---%d\n", queue[i]);        sem_post(&product_number);                  //将产品数++i = (i+1) % NUM;                            //借助下标实现环形sleep(rand()%1);}
}void *consumer(void *arg)
{int i = 0;while (1) {sem_wait(&product_number);                  //消费者将产品数--,为0则阻塞等待printf("-Consume---%d\n", queue[i]);queue[i] = 0;                               //消费一个产品 sem_post(&blank_number);                    //消费掉以后,将空格子数++i = (i+1) % NUM;sleep(rand()%3);}
}int main(int argc, char *argv[])
{pthread_t pid, cid;sem_init(&blank_number, 0, NUM);                //初始化空格子信号量为5, 线程间共享 -- 0sem_init(&product_number, 0, 0);                //产品数为0pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);pthread_join(pid, NULL);pthread_join(cid, NULL);sem_destroy(&blank_number);sem_destroy(&product_number);return 0;
}

TCP/IP协议模型–练习题

server.c

client.c

1-socket-server.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h> // toupper#define SERV_PORT 9527int main(int argc, char *argv[])
{   int sockfd_1, sockfd_2;// 1. 先把 地址结构 初始化好struct sockaddr_in serv_addr, clit_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);   serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);// 2. 创建套接字sockfd_1 = socket(AF_INET, SOCK_STREAM, 0);if(sockfd_1 < 0){perror("socket error");exit(1);}// 3. 绑定地址结构socklen_t serv_addelen = sizeof(serv_addr);  // bind这个参数 有类型if (bind(sockfd_1, (struct sockaddr*)&serv_addr, serv_addelen) < 0){perror("bind error");exit(1);}// 4.设置监听数量if (listen(sockfd_1, 128) < 0){perror("listen error");exit(1);}// 5. 阻塞监听socklen_t clit_addrlen = sizeof(clit_addr);sockfd_2 = accept(sockfd_1, (struct sockaddr*)&clit_addr, &clit_addrlen); // 注意, 这个地址结构是 传出参数if(sockfd_2 < 0){perror("accept error");exit(1);}// 6. 打印 连接的客户端 ip 和端口号-----学会用函数char client_ip[1024];printf("client ip is %s, port is %d\n", inet_ntop(AF_INET, &clit_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(clit_addr.sin_port));// 7. 实现 服务端的 功能,  小写转大写// 套接字的 数据是一次性的char buf[1024];while(1){int n = read(sockfd_2, buf, sizeof(buf));if (n <= 0){if (n == 0){printf("Client disconnected.\n");  /*当一方关闭连接后,另一方的 read() 操作会收到一个 EOF(End of File)指示,即 read() 返回 0。*/        }else{perror("read error");}break; // 退出循环}write(STDOUT_FILENO, "Received: ", 10);write(STDOUT_FILENO, buf, n);for(int i = 0; i < n; i++){buf[i] = toupper(buf[i]);}write(STDOUT_FILENO, "To upper: ", 10);write(STDOUT_FILENO, buf, n);// 将转换后的数据发送回客户端write(sockfd_2, buf, n);}close(sockfd_1);close(sockfd_2);return 0;
}

2-socket-client.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SERV_PORT 9527   // 无分号
// 不使用 有参命令int main(int argc, char *argv[])
{   int sockfd;// 1. 初始化地址, 客户端里, 这个地址结构是服务器的 各项数据struct sockaddr_in serve_addr;serve_addr.sin_family = AF_INET;serve_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, "127.0.0.1", &serve_addr.sin_addr.s_addr);// 2. 创建套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);// 3. 隐式绑定, 连接客户端socklen_t serve_addrlen = sizeof(serve_addr);if(connect(sockfd, (struct sockaddr*)&serve_addr, serve_addrlen)!=0){perror("connect error");exit(1);}// 4.输入int count = 5;char buf[1024];do{write(sockfd, "hello\n", 7);int n = read(sockfd, buf, sizeof(buf));if(n<=0){if(n==0){printf("finish \n");}else{perror("read error");exit(1);}}write(STDOUT_FILENO, buf, n);} while (count--);close(sockfd);return 0;
}

多进程并发 and 多线程并发

特别注意: 多进程并发时, 一定要 对accept 进行判断, 都则可能会 返回-1,还在执行, 会一直打印 read error

特别注意: 端口号, 一定要找 没有被占用的, 否则 也会 一直打印 read error

1-more-fork-socket.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include <ctype.h>
#define S_PORT 9001void catch_child(int signo)
{// 只捕捉一个, 不用判断while((waitpid(0, NULL, WNOHANG))>0);return ;
}int main(int argc, char *argv[])
{   pid_t pid;// 捕捉信号struct sigaction act;act.sa_handler = catch_child;sigemptyset(&act.sa_mask);act.sa_flags = 0;int ret = sigaction(SIGCHLD, &act, NULL);if(ret < 0){perror("sigaction error");exit(1);}struct sockaddr_in se_addr, cli_addr;bzero(&se_addr, sizeof(se_addr));se_addr.sin_family = AF_INET;se_addr.sin_port = htons(S_PORT);se_addr.sin_addr.s_addr = htonl(INADDR_ANY);int sockfd = socket(AF_INET, SOCK_STREAM, 0);int sockfd_child; // 共享文件描述符socklen_t len_se = sizeof(se_addr);bind(sockfd, (struct sockaddr*)&se_addr, len_se);listen(sockfd, 128);while(1){// sockfd_child 在这里定义, 会是局部的, 不能传递到外面socklen_t len = sizeof(cli_addr);if ((sockfd_child = accept(sockfd, (struct sockaddr*)&cli_addr, &len)) < 0) {perror("accept error");continue; // 如果接受失败,继续等待其他连接}pid = fork();if(pid < 0){perror("fork error");exit(1);}else if(pid == 0){close(sockfd);char buf[1024];while(1) {int n = read(sockfd_child, buf, sizeof(buf));if (n <= 0) {if (n == 0){printf("Client disconnected.\n");  /*当一方关闭连接后,另一方的 read() 操作会收到一个 EOF(End of File)指示,即 read() 返回 0。*/   }else{perror("read error");}close(sockfd_child);// break; // 退出循环exit(0);}for (int i = 0; i < n; i++)buf[i] = toupper(buf[i]);write(sockfd_child, buf, n);write(STDOUT_FILENO, buf, n);}}else{close(sockfd_child);}}// if(pid == 0)// {// }close(sockfd); // 关闭服务器监听套接字return 0;
}

4-more-pthread-socket.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <pthread.h>
#include <ctype.h>
#include <arpa/inet.h>#define S_PORT 9001void* fn(void *arg)
{int sockfd_fn = (int)arg;char buf[1024];while(1){int n = read(sockfd_fn, buf, sizeof(buf));if(n<=0){if(n==0){printf("client disconnect---\n");// exit(0);}else{perror("read error");// exit(1);}close(sockfd_fn);break;}printf("receve :\n");write(STDOUT_FILENO, buf, n);for(int i=0; i<n; i++){buf[i] = toupper(buf[i]);}printf("toupper: \n");write(sockfd_fn, buf, n);write(STDOUT_FILENO, buf, n);}return NULL;
}int main(int argc, char *argv[])
{   pthread_t tid;struct sockaddr_in ser_addr, cli_addr;memset(&ser_addr, 0, sizeof(ser_addr));memset(&cli_addr, 0, sizeof(cli_addr));ser_addr.sin_family = AF_INET;ser_addr.sin_port = htons(S_PORT);ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);int sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd<0){perror("socket error");exit(1);}socklen_t len_ser = sizeof(ser_addr);if(bind(sockfd, (struct sockaddr*)&ser_addr, len_ser)){perror("bind error");exit(1);}if(listen(sockfd, 128)){perror("listen error");exit(1);}socklen_t len_cli = sizeof(cli_addr);while(1){int sockfd_p = accept(sockfd, (struct sockaddr*)&cli_addr, &len_cli);if(sockfd_p < 0){perror("accept error");continue;// exit(1);}// close(sockfd);   // 这里不能关闭, 后续无法继续监听了 线程之间 不共享栈空间int ret = pthread_create(&tid, NULL, fn, (void *)sockfd_p);if(ret < 0){fprintf(stderr, "pthread_create error: %s\n", strerror(ret));close(sockfd_p);continue;}pthread_detach(tid);  // 分离线程,  子线程自动回收}pthread_exit(NULL);}

epoll 服务-客户练习

做 多客户端链接的 服务端, 注意 epoll函数的使用

1-epoll-server.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <ctype.h>#define S_PORT 9003
#define MAX_EVENTS 10
#define BUF_SIZE 1024int set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL, 0);if (flags == -1) return -1;return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}int main(int argc, char *argv[]) {int sockfd, epfd;struct sockaddr_in ser_addr, cli_addr;socklen_t len_cli = sizeof(cli_addr);// 1. 创建监听套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket error");exit(1);}// 2. 绑定地址和端口memset(&ser_addr, 0, sizeof(ser_addr));ser_addr.sin_family = AF_INET;ser_addr.sin_port = htons(S_PORT);ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(sockfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) < 0) {perror("bind error");exit(1);}// 3. 开始监听if (listen(sockfd, 128) < 0) {perror("listen error");exit(1);}printf("Server is listening on port %d...\n", S_PORT);// 4. 创建 epoll 实例epfd = epoll_create1(0);if (epfd < 0) {perror("epoll_create1 error");exit(1);}// 5. 将监听套接字加入 epoll -----  先监听 主要的 sockfd, 用于接收新的客户端连接struct epoll_event event;event.events = EPOLLIN; // 监听读事件event.data.fd = sockfd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event) < 0) {perror("epoll_ctl error");exit(1);}// 6. 事件循环struct epoll_event events[MAX_EVENTS];char buf[BUF_SIZE];while (1) {int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);if (nfds < 0) {perror("epoll_wait error");exit(1);}// 这里是处理 满足事件的 监听点for (int i = 0; i < nfds; i++) {if (events[i].data.fd == sockfd) {// 有新客户端连接int client_fd = accept(sockfd, (struct sockaddr*)&cli_addr, &len_cli);if (client_fd < 0) {perror("accept error");continue;}// 设置非阻塞模式if (set_nonblocking(client_fd) < 0) {perror("set_nonblocking error");close(client_fd);continue;}// 将客户端套接字加入 epollevent.events = EPOLLIN | EPOLLET; // 边缘触发模式event.data.fd = client_fd;if (epoll_ctl(epfd, EPOLL_CTL_ADD, client_fd, &event) < 0) {perror("epoll_ctl error");close(client_fd);}printf("New client connected: fd=%d\n", client_fd);} else {// 处理客户端数据int client_fd = events[i].data.fd;while (1) {ssize_t n = read(client_fd, buf, BUF_SIZE);if (n > 0) {// 输出到标准输出for(int i=0; i<n; i++){buf[i] = toupper(buf[i]);}if(write(client_fd, buf, n)<0){perror("write error");exit(1);}write(STDOUT_FILENO, buf, n);} else if (n == 0) {// 客户端断开连接printf("Client disconnected: fd=%d\n", client_fd);epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, NULL);close(client_fd);break;} else if (errno == EAGAIN || errno == EWOULDBLOCK) {// 数据读取完毕break;} else {perror("read error");epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, NULL);close(client_fd);break;}}}}}close(sockfd);close(epfd);return 0;
}

2-epoll-client.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define SERV_PORT 9003   // 无分号
// 不使用 有参命令int set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL, 0);if (flags == -1) return -1;return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}int main(int argc, char *argv[])
{   int sockfd;// 1. 初始化地址, 客户端里, 这个地址结构是服务器的 各项数据struct sockaddr_in serve_addr;serve_addr.sin_family = AF_INET;serve_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, "127.0.0.1", &serve_addr.sin_addr.s_addr);// 2. 创建套接字sockfd = socket(AF_INET, SOCK_STREAM, 0);// 3. 隐式绑定, 连接客户端socklen_t serve_addrlen = sizeof(serve_addr);if(connect(sockfd, (struct sockaddr*)&serve_addr, serve_addrlen)!=0){perror("connect error");exit(1);}// 4.输入char buf[1024];// set_nonblocking(sockfd);while (1){int n = read(sockfd, buf, sizeof(buf));   // 使得 服务端 一关闭 连接, 就收到if (n < 0) {perror("read error");break;} else if (n == 0) {printf("Server closed the connection\n");break;}n = read(STDIN_FILENO, buf, sizeof(buf));if(n<=0){if(n==0){printf("finish \n");}else{perror("read error");exit(1);}break;}write(sockfd, buf, n);// 从服务器读取响应n = read(sockfd, buf, sizeof(buf));if (n < 0) {perror("read error");break;} else if (n == 0) {printf("Server closed the connection\n");break;}write(STDOUT_FILENO, buf, n);}close(sockfd);return 0;
}
http://www.dtcms.com/wzjs/137785.html

相关文章:

  • 程序_做彩票源码网站开发seoul是什么意思
  • 外贸网站源码是什么写软文
  • POS机网站怎么做西部数码域名注册官网
  • 浙江网商银行厦门seo代理商
  • 西宁网站制作公司seo关键词排名优化联系方式
  • 音乐网站制作课程报告成人职业技能培训班
  • 运城网站推广哪家好最新的新闻 今天
  • 邮件更新wordpress唐山百度提升优化
  • 模板网站可以做seo吗深圳优化排名公司
  • 网站建设丶金手指下拉十五站长论坛
  • 网站图片展示形式韩国热搜榜
  • 网站建设策划方案品牌策略怎么写
  • 张掖高端网站建设公司宣传软文范例
  • 网站设计行业资讯武汉seo霸屏
  • 西安网站建设 美科动西安百度关键词推广
  • ubuntu 建网站宁波seo怎么做引流推广
  • 卡盟怎么做网站疫情放开最新消息今天
  • 个人备案域名可以做哪些网站吗关键词免费下载
  • 安卓做视频网站好南宁seo平台标准
  • 计算机课程网站建设实训报告总结丁香人才网官方网站
  • 安阳网站建设设计优化网站排名推广
  • 网站开发登录要做哪些验证网络营销的常用方法
  • 免费网站导航建设google关键词排名
  • 美女做游戏广告视频网站有哪些搜索词排行榜
  • 本地网站建设教程xampp厦门人才网官方网站
  • wordpress 图文插件优化关键词排名软件
  • 临沂网站建设公司全国网站推广及seo方案
  • 做钢结构网站有哪些北京网站建设专业公司
  • 杭州建设招标网简述影响关键词优化的因素
  • 网站是什么样的杭州seo技术