视频链接

黑马程序员-Linux网络编程_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1iJ411S7UA?p=37

目录

一、高并发服务器

1.1 图示

1.2 分类

二、多进程并发服务器

2.1 回顾前面的内容

2.2 步骤

2.3 多进程并发服务器实现

三、多线程并发服务器

3.1 步骤

3.2 多线程并发服务器实现


一、高并发服务器

1.1 图示

1.2 分类

多进程并发服务器

多线程并发服务器

二、多进程并发服务器

2.1 回顾前面的内容

服务端创建一个套接字 lfd,用来建立连接。客户端有连接请求的时候,借助 lfd 创建一个 cfd 套接字,客户端跟客户端 cfd 建立连接。再来一个客户端是和新创建的 cfd2 建立连接,所以 lfd 就一直处于空闲状态,等待其他客户端过来建立连接

多进程中 cfd 就是子进程的角色,lfd 就是父进程的角色

2.2 步骤

第1步:创建监听套接字 lfd,使用函数 Socket();

第2步:绑定地址结构 Struct scokaddr_in addr;,使用函数 Bind();

第3步:让套接字进入监听状态并响应客户端请求,使用函数 Listen();

第4步:

while(1) {
        cfd = Accpet();  // 接受客户端连接请求
        pid = fork();      // 创建子进程
        if(pid == 0) {     // 子进程执行的操作
                close(lfd); // 关闭用于建立连接的套接字 lfd(这是父进程的任务)
                read();      // 从套接字中读取客户端发来的消息
                小写字母转换成大写字母();
                write();      // 写入用于通信的套接字中
        } else if (pid > 0) {  // 父进程
                close(cfd);  // 关闭用于客户端通信的套接字
                while(1) {  //等待子进程
                        waitpid(0, NULL, 0);  // 在这里循环调用不阻塞回收子进程会存在问题,所以后面写在回调函数中了
                }
                contiue;
        }
}

2.3 多进程并发服务器实现

#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>#include"wrap.h"#define SRV_PORT 9999void perr_exit(const char *s)
{perror(s);exit(1);
}int main(int argc, char *argv[])
{int lfd, cfd;pid_t pid;struct sockaddr_in srv_addr, clt_addr;socklen_t clt_addr_len;char buf[BUFSIZ];int ret, i;// 将地址结构清零 // 第一种方法: // memset(&srv_addr, 0, sizeof(srv_addr));// 第二种方法:bzero(&srv_addr, sizeof(srv_addr));srv_addr.sin_family = AF_INET;srv_addr.sin_port = htons(SRV_PORT);srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);lfd = socket(AF_INET, SOCK_STREAM, 0);bind(lfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));listen(lfd, 128);clt_addr_len = sizeof(clt_addr);while (1) {cfd = accept(lfd, (struct sockaddr *)&clt_addr, &clt_addr_len);pid = fork();if (pid < 0) {perr_exit("fork error");} else if (pid == 0) {close(lfd);break;}else {close(cfd);continue;}}// 子进程执行的操作if (pid == 0) {while(1) {ret = read(cfd, buf, sizeof(buf));if (ret == 0) {close(cfd);exit(1);}   for (i = 0; i < ret; i++) {buf[i] = toupper(buf[i]);}    write(cfd, buf, ret);write(STDOUT_FILENO, buf, ret);}}return 0;
}

因为没回收子进程,所以有很多僵尸进程

增加信号触发用于回收的函数

#include<stdio.h>
#include<ctype.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>#include"wrap.h"#define SRV_PORT 9999void perr_exit(const char *s)
{perror(s);exit(1);
}void catch_child(int signum)
{while (waitpid(0, NULL, WNOHANG) > 0);return ;
}int main(int argc, char *argv[])
{int lfd, cfd;pid_t pid;struct sockaddr_in srv_addr, clt_addr;socklen_t clt_addr_len;char buf[BUFSIZ];int ret, i;// 将地址结构清零 // 第一种方法: // memset(&srv_addr, 0, sizeof(srv_addr));// 第二种方法:bzero(&srv_addr, sizeof(srv_addr));srv_addr.sin_family = AF_INET;srv_addr.sin_port = htons(SRV_PORT);srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);lfd = socket(AF_INET, SOCK_STREAM, 0);bind(lfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));listen(lfd, 128);clt_addr_len = sizeof(clt_addr);while (1) {cfd = accept(lfd, (struct sockaddr *)&clt_addr, &clt_addr_len);pid = fork();if (pid < 0) {perr_exit("fork error");} else if (pid == 0) {struct sigaction act;act.sa_handler = catch_child;sigemptyset(&act.sa_mask);act.sa_flags = 0;ret = sigaction(SIGCHLD, &act, NULL);if (ret != 0) {perr_exit("sigaction error");}close(lfd);break;}else {close(cfd);continue;}}// 子进程执行的操作if (pid == 0) {while(1) {ret = read(cfd, buf, sizeof(buf));if (ret == 0) {close(cfd);exit(1);}  for (i = 0; i < ret; i++) {buf[i] = toupper(buf[i]);}    write(cfd, buf, ret);write(STDOUT_FILENO, buf, ret);}}return 0;
}

三、多线程并发服务器

3.1 步骤

第1步:创建监听套接字 lfd,使用函数 Socket();

第2步:绑定地址结构 Struct scokaddr_in addr;,使用函数 Bind();

第3步:让套接字进入监听状态并响应客户端请求,使用函数 Listen();

第4步:

while(1) {
        cfd = Accpet(lfd);       // 接受客户端连接请求
        pthread_create(&tid, NULL, tfn, NULL);  // 创建子线程
        pthread_detach(tid);  // 前面是线程分离的函数,如果想接受子线程的返回值,需要 pthread_join(tid, void **); 这个函数,但是这个函数是阻塞等待的。我们可以创建一个新的线程专门用于回收这个子线程
}

第5步:

void *tfn(void *arg)
{
        close(lfd);
        read(cfd);
        小 -> 大
        write(cfd);
        pthread_exit((void*)10);
}

3.2 多线程并发服务器实现

#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>#define MAXLINE 8192
#define SERV_PORT 8000struct s_info {  // 定义一个结构体, 将地址结构跟cfd捆绑  struct sockaddr_in cliaddr;int connfd;
};void *do_work(void *arg)
{int n,i;struct s_info *ts = (struct s_info*)arg;char buf[MAXLINE];char str[INET_ADDRSTRLEN];  // #define INET_ADDRSTRLEN 16  可用"[+d"查看  while (1) {n = read(ts->connfd, buf, MAXLINE);  // 读客户端  if (n == 0) {printf("the client %d closed...\n", ts->connfd);break;  // 跳出循环,关闭cfd  }printf("received from %s at PORT %d\n",inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, str, sizeof(str)),ntohs((*ts).cliaddr.sin_port));     // 打印客户端信息(IP/PORT)  for (i = 0; i < n; i++)buf[i] = toupper(buf[i]);  // 小写-->大写  write(STDOUT_FILENO, buf, n);  // 写出至屏幕  write(ts->connfd, buf, n);     // 回写给客户端  }close(ts->connfd);return (void *)0;
}int main(void)
{struct sockaddr_in servaddr, cliaddr;socklen_t cliaddr_len;int listenfd, connfd;pthread_t tid;struct s_info ts[256];  // 创建结构体数组  int i = 0;listenfd = socket(AF_INET, SOCK_STREAM, 0);  // 创建一个socket, 得到lfd  bzero(&servaddr, sizeof(servaddr));  // 地址结构清零  servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  // 指定本地任意IP  servaddr.sin_port = htons(SERV_PORT);  // 指定端口号   bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));  // 绑定  listen(listenfd, 128);  // 设置同一时刻链接服务器上限数  printf("Accepting client connect ...\n");while (1) {cliaddr_len = sizeof(cliaddr);connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  // 阻塞监听客户端链接请求  ts[i].cliaddr = cliaddr;ts[i].connfd = connfd;pthread_create(&tid, NULL, do_work, (void*)&ts[i]);pthread_detach(tid);  // 子线程分离,防止僵线程产生.  i++;}return 0;
}

编译和运行 

Linux网络编程3——多进/线程并发服务器相关推荐

  1. linux网络编程(二)高并发服务器

    linux网络编程(二)高并发服务器 错误处理 高并发服务器 多进程并发服务器 客户端 错误处理 #include "wrap.h"int Bind(int fd, const s ...

  2. Linux网络编程(六)-高并发服务器03-I/O多路复用03:epoll【红黑树;根节点为监听节点】【无宏FD_SETSIZE限制;不需每次都将要监听的文件描述符从应用层拷贝到内核;不需遍历树】

    一.epoll概述 epoll的本质是一个[红黑树].监听结点为根节点. 大量并发,少量活跃效率高. epoll是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并 ...

  3. linux网络编程(四)线程池

    linux网络编程(四)线程池 为什么会有线程池? 实现简单的线程池 为什么会有线程池? 大多数的服务器可能都有这样一种情况,就是会在单位时间内接收到大量客户端请求,我们可以采取接受到客户端请求创建一 ...

  4. 27.Linux网络编程socket变成 tcp 高并发 线程池 udp

    好,咱们开始上课了,从今天开始咱们连续讲 8 天的,网络编程这个还是在linux环境下去讲,咱们先看一下咱们这 8 天都讲什么东西,跟大家一块来梳理一下,你先有个大概的印象,这些你也不要记,那么网络编 ...

  5. 网络编程-C语言实现多进程并发服务器

    在Linux操作系统下,服务器通过fork()复制已调用的进程来创建子进程,以此来实现多进程并发服务器 功能:客户端发送任意字符串,服务器端将字符串小写转大写toupper();并返回给客户端,客户端 ...

  6. UNIX网络编程笔记(3):简单的并发服务器

    上一讲中的简单时间获取服务器是一个迭代服务器,对于获取时间来说够用了.迭代服务器有这样的特点:同一时间只能给一个客户服务.也就是说,如果某一时刻服务器与某个客户正在连接,其它客户必须等到上一个客户与服 ...

  7. linux网络编程学习笔记之三 -----多进程并发服务端

    首先是fork()函数.移步APUE 8.3.  比較清晰的解释能够參考http://blog.csdn.net/lingdxuyan/article/details/4993883和http://w ...

  8. linux网络编程之多路I/O转接服务器poll函数

    (1)poll函数 头文件:#include<poll.h> int  poll(struct  pollfd*fds, nfds_t nfds,int timeout); struct  ...

  9. linux网络编程之多路I/o转接服务器select

    (1)多路IO转接服务器也叫做多任务IO服务器,其主要思想是不再由程序自己监听客户端连接,取而代之的是由内核替应用程序监视文件,具体实现模型如图所示: 当客户端请求和服务器连接时,内核接收到连接指令, ...

最新文章

  1. 在 VS Code 里逛知乎、发文章?Zhihu on VSCode 来啦!重新定义内容创作!
  2. [渝粤教育] 郑州升达经贸管理学院 大学英语混合式课程 参考 资料
  3. 开源TinyXML 最简单的新手教程
  4. 震惊:2/3 被黑的网站隐藏着后门
  5. ascii码和unicode
  6. jQuery 插件使用记录
  7. ubuntu 查找opencv安装路径_Ubuntu系统---配置OpenCV
  8. Java学完哪些内容能够出去找工作
  9. 爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来
  10. 一张图片即可入侵你的电脑
  11. 幻灯片母板_如何在Microsoft PowerPoint中创建幻灯片母版
  12. 思科模拟器配置-生成树协议与链路聚合负载均衡
  13. 网络数据里的身份证实名认证接口,你了解多少?
  14. 清除node本地缓存
  15. 中国联通、华为联合举办国内首例5G异地合奏音乐会
  16. 伸展树(Splay)
  17. 博士五年,我在清华做时序数据库
  18. Padavan老毛子的二级路由,怎样设置与主路由在同一网段
  19. 白鹭引擎 android9,白鹭引擎打包APP过程
  20. Macbook M1开启允许任意来源应用

热门文章

  1. grpc go和python 互通
  2. Vue非父子组件传值
  3. 这是一首比较老的英文歌了《Cry on my shoulder》别嫌弃,啦啦啦!
  4. ‘XXX’can not be represented as java.sql.Timestamp
  5. 小草客户端android2.2.4 g,小草app安卓版2.2.3
  6. Spring之ORM
  7. K8S-遇到问题-解决日记
  8. Java 每半年就会更新一次新特性,再不掌握就要落伍了:Java11 的新特性
  9. redash开发环境搭建
  10. 【干货】S7-PLCSIM Advanced V3.0 无法启动实例( Error Code:-30,LicenseNotFound)问题解决方法