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

Linux操作系统——TCP服务端并发模型

TCP:建立连接,一对一

要实现多任务并发,就引出了并发模型

一、多进程与多线程

1.在相同资源情况下,进程资源开销大,但其安全性高

2.线程相对于进程资源开销小,且并发量比进程大

①多进程并发基础代码

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>int init_tcp_http()    
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket error");return -1;}struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(51000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.183");int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind error");return -1;}ret = listen(sockfd, 10);if (ret < 0){perror("listen error");return -1;}return sockfd;
}int main(int argc, char const *argv[])
{struct sockaddr_in cliaddr;socklen_t clien = sizeof(cliaddr);int sockfd = init_tcp_http();if(sockfd < 0){return -1;}while(1){int connfd =accept(sockfd,(struct sockaddr *)&cliaddr,&clien);if(connfd < 0){perror("accept error");return -1;}pid_t pid = fork();if(pid > 0){}else if(pid == 0){char buff[1024];while(1){memset(buff,0,sizeof(buff));ssize_t cnt = recv(connfd,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");return -1;}else if(cnt == 0){printf("[%s : %d], online\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));}printf("[%s : %d]  %s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buff);strcat(buff,"----->ok");cnt = send(connfd,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");return -1;}}close(connfd);}}close(sockfd);return 0;
}

②多线程并发基础代码实现

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/ip.h> /* superset of previous */
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>int init_tcp_http()    
{int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){perror("socket error");return -1;}int optval = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(51000);seraddr.sin_addr.s_addr = inet_addr("192.168.0.183");int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("bind error");return -1;}ret = listen(sockfd, 10);if (ret < 0){perror("listen error");return -1;}return sockfd;
}void *task(void *arg)
{   int connfd = *(int *)arg;char buff[1024] = {0};while(1){memset(buff,0,sizeof(buff));ssize_t cnt = recv(connfd,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");close(connfd);return NULL;}else if(cnt == 0){printf("online\n");break;}printf("buff = %s\n",buff);strcat(buff,"----->ok");cnt = send(connfd,buff,sizeof(buff),0);if(cnt < 0){perror("recv error");close(connfd);return NULL;}}close(connfd);
}int main(int argc, char const *argv[])
{    pthread_t tid; struct sockaddr_in cliaddr;socklen_t clien = sizeof(cliaddr);int sockfd = init_tcp_http();if(sockfd < 0){return -1;}while(1){int connfd = accept(sockfd,(struct sockaddr *)&cliaddr,&clien);if(connfd < 0){perror("accept error");return -1;}pthread_create(&tid,NULL,task,&connfd);pthread_detach(tid); }close(sockfd);return 0;
}

二、线程池

     为了解决多线程或者多进程模型,在服务器运行过程,频繁创建和销毁线程(进程)带来的时间消耗问题。基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程框架。

简单的例子:主线程是一个餐厅,餐厅有四个服务员(此线程),当客人到餐厅就餐时,服务员前去接待,当四个服务员都没有空闲时,客人进行排队,就好比一个队列(先进先出),当服务员空闲来,就去“队列”中接待客人(task)

三、IO多路复用   

I-->O:fd
对多个文件描述符的读写可以复用一个进程。

      在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时监测

 阻塞IO模式:

1. 多个任务之间是同步的效果

      1)select
2)poll
3)epoll

2.相应函数解释

1)select实现IO多路复用:

      1. 创建文件描述符集合                  fd_set
2. 添加关注的文件描述符到集合   FD_SET();
3. 使用select传递集合表给内核,内核开始监测事件  select()
4. 当内核监测到事件时,应用层select将解除阻塞,并获得相关的事件结果
5. 根据select返回的结果做不同的任务处理

位图为1024是因为文件描述符是0~1023(前三位是标准输出,标准输入,标准出错设备)

       void FD_CLR(int fd, fd_set *set);
int  FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

这是系统自带的一些带参宏

FD_CLR     是将当前套接字置零

FD_ISSET  检测套接字是否置为1

FD_SET      将定义的套接字插入文件描述符集合

FD_ZERO    将文件描述符全清零

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds,

                                                                                                 struct timeval *timeout);
功能:传递文件描述符结合表给内核并等待获取事件结果
参数:
nfds :        关注的最大文件描述符+1
readfds:   读事件的文件描述符集合
writefds:   写事件的文件描述符集合
exceptfds:其他事件的文件描述符集合
timeout:    设置select监测时的超时时间
NULL : 不设置超时时间(select一直阻塞等待)

        返回值:
成功:返回内核监测的到达事件的个数
失败:-1
0 : 超时时间到达,但没有事件发生,则返回0

http://www.dtcms.com/a/353989.html

相关文章:

  • Java全栈开发面试实战:从基础到复杂场景的深度解析
  • 【51单片机】【protues仿真】基于51单片机点阵屏系统
  • 全域管控,一触可达:复合机器人远程监控方案重塑智能制造
  • Boosting(提升法)详解
  • Spring Boot + Dubbo 实战教程:打造高性能微服务架构
  • 深度学习12 Reinforcement Learning with Human Feedback
  • openwrt ubus 深入分析
  • C# 字符和字符串
  • 怎么解决大模型幻觉问题
  • 【完全二叉树】 P10990 [蓝桥杯 2023 国 Python A] 彩色二叉树|普及+
  • 车辆识别码vin构成
  • python // 和%区别
  • K8S EFK日志收集全流程实战
  • MySQL数据库精研之旅第十二期:探秘视图,数据库中的 “虚拟表” 魔法
  • stm32 hal库spi dma_tx_rx的几个关键函数执行过程jlink trace分析
  • Rust 登堂 之 迭代器Iterator(三)
  • 如何构建灵活、可控、可扩展的多云网络底座
  • 【高级机器学习】1. Hypothesis 与 Objective Function
  • solidworks2024保姆级安装教程及解锁版安装包下载!
  • 【编号478】美国土地利用数据本土、阿拉斯加、夏威夷岛土地利用数据
  • 蛋白质残基 - 残基距离计算:单蛋白工具与批量处理方案
  • 【目标检测】论文阅读5
  • 记录一次内存问题排查
  • 比赛竞猜算法设计思路
  • MySQL InnoDB vs MyISAM
  • 【系统分析师】高分论文:论信息系统开发方法及应用
  • 前端漏洞(下)- 会话固定漏洞
  • Databend 亮相 DTCC 2025:存算分离架构引领湖仓一体化
  • 漫谈《数字图像处理》之霍夫变换
  • 一文辨析编程语言的强类型与弱类型、静态类型与动态类型