编写网络服务程序的常用编程模型

应用层网络服务程序简介 应用层网络服务程序简介一.HTTP协议和服务1.HTTP协议概述2.HTTP协议的基本过程二.FTP协议和服务1.FTP协议概述2.FTP协议的工作模式3.FTP协议的传输方式4.一个简单的FTP过程5.常用的FTP工具三.TELNET协议和服务1.远程登录的基本概念2.使用TELNET协议进行远程登录的工作过程3.TELNET协议四.NFS协议和服务1.安装NFS服务器和客户端2.服务器端的设定3.客户端的操作4.showmount命令五.自定义网络服务1.xinetd/inetd2.xinetd服务配置 阅读详情

今天大概总结一下编写服务端程序常用的编程模型。参考UNP第三版第三十章和陈硕的muduo那本书,强烈建议仔细阅读。注意以下代码只是为了显式框架或者说编程模型,不是完整的程序,深夜写的比较任性,不要见怪。

accept + read/write

这个不是并发服务器,而是迭代服务器(iterative server)。 它一次服务一个客户。不适合长连接,适合 daytime 这种 write-only 短连接服务。

 void handle(client_socket, client_address)
{
while(true)
{
data = client_socket.recv(4096);
if(data)
client_socket.send(data);
else
do_error();
}
}
while(true){
connfd = server_socket.accept();
handle(client_socket, client_address);
}

accept + fork

这种模型也叫 process-per-connection,编写出的是多进程并发服务器程序,为每个客户调用 fork 派生一个子进程,使得服务器能够同时服务多个客户。这是传统的 Unix 并发网络编程方案,适合并发连接数不大的情况,适合“计算响应的工作量远大于 fork() 的开销” 这种情况,每个客户现场 fork 一个子进程比较耗费 CPU 时间,这样连接数目的限制是能够开多少个线程。

while(true){
connfd = accept(listenfd, clientaddr, &clientlen);
if((childpid = fork()) == 0){ // child process
close(listenfd);
handle_client(connfd);
exit(0);
}
close(connfd);
}

accept + thread

这种模型也叫 thread-per-connection,是把上面的进程换成了线程,即为每个客户请求创建一个线程,是一个多线程模型,这样开销就相对进程来说小了不少,这样现场为每个客户创建一个线程比为每个客户现场派生一个进程快很多,而且一般这样比预先派生子进程还要快。

while(true)     // 主线程循环阻塞在 accept调用
{
connfd = accept(listenfd, clientaddr, &clientlen);  // 当返回一个连接时
pthread_create(&tid, NULL, &doit, (void *)connfd);  //就创建一个新线程,将connfd传递给线程
}

void* doit(void *arg)
{
……
handle_client((int)arg);
close((int)arg);
……
}

pre fork

这种方案不像传统的并发服务器那样为每个客户现场派生一个进程,而是在启动阶段预先派生一定数量的子进程,当多个客户连接到达时,这些子进程立即就能为它们服务。预先创建完进程之后,之后可以有几种不同的处理,比如每个子进程调用 accept(无锁保护)、以文件上锁方式保护 accept、以线程互斥锁上锁方式保护 accept、由父进程向子进程传递套接字描述符。

显而易见优点是省去了现场 fork 子进程的开销,缺点是要猜测预先派生多少子进程,这一点可以通过手段来应对,父进程持续监视可用/闲置子进程数,一旦该值降低到某个阀值就派生额外的子进程;同样一旦该值超过另一个阀值就终止一些过剩的子进程。

看第一种:子进程调用accept,无上锁保护

static pid_t   *pids;               // 存放各个子进程ID的数组
pids = alloc_mem(childnumbers, sizeof(pid_t));      // 申请空间

for(i=0; i<childnumbers; i++)
pids[i] = child_make(i, listenfd, addrlen);     //  创建各个子进程

pid_t child_make(int i, int listenfd, int addrlen)
{
pid_t pid;
if( (pid=fork()) > 0)   // 父进程
return pid;     
child_main(i, listenfd, addrlen);   // 在这个里面循环调用 accept ,然后处理客户请求,最后关闭连接
}
void child_main(i, listenfd, addrlen)
{
while(1)
{
connfd = accept(listenfd, clientaddr, &clientlen);  // 接受连接
handle_client(connfd);  // 处理
close(fd);  // 关闭
}
}

像这种方式会出现“惊群”现象,怎么说?服务器进程在程序启动阶段派生N个子进程,它们各自调用 accept 并因而均被投入睡眠,当第一个客户连接到达时,所有N个子进程均被唤醒(因为所有子进程所用的监听描述符指向同一个socket结构,都睡在这个 socket 结构的等待队列上),只有最先运行的子进程获得那个客户连接,其余继续睡眠。这样就会导致性能受损,测量性能受多少影响就超出了本文要讲的东西了(我也不会……)。

那么如何解决呢?让应用进程在调用accept前后安置某种形式的锁,这样任意时刻只有一个子进程阻塞在 accept 调用中,其他子进程则阻塞在试图获取用于保护 accept 的锁上。 这就是上面提到的 以文件上锁方式保护 accept、以线程互斥锁上锁方式保护 accept,代码这里不贴了。

还有一个修改版本是只让父进程调用 accept,然后把所接受的已连接套接字“传递”给某个进程,这样做绕过了为所有子进程的 accept 调用提供上锁保护的可能需求。这个代码实现更复杂,要会用进程间通信,父进程还要跟踪子进程的忙闲状态,以便给空闲进程传递新的套接字。

pre threaded

预先创建一个线程池,并让每个线程各自调用 accept,取代让每个线程都阻塞在 accept 调用,我们用互斥锁以保证任何时刻只有一个线程在调用 accept。

thread_make();  // 首先创建指定数量的线程

void* thread_func(void *arg)
{
while(1){
pthread_mutex_lock(&mutex);
connfd = accept(listenfd, clientaddr, &clientlen);
pthread_mutex_unlock(&mutex);
handle_client(connfd);
close(connfd);
}
}

还有一个修改版本是主线程统一 accept,并把各个客户连接传递给池中某个可用线程。问题在于主线程如何把一个已连接的套接字传递给线程池中某个可用线程,有很多方式。

细分开来上面一共提到9个不同的服务器程序设计模型,它们都是阻塞式网络编程,程序流程通常阻塞在 read() 上,等待数据到达。但是 TCP 是全双工协议,同时支持 read() 和 write() 操作,当进程/线程阻塞在 read() 上,但程序又想给这个 TCP 连接发送数据该怎么办?比如 echo client、proxy。

一种方法是用两个进程/线程,一个负责读,一个负责写。另一种方式是使用 IO multiplexing,也就是 select/poll/epoll/kqueue 这一系列多路复用手段,让一个程序流程能处理多个连接。IO复用其实复用的不是IO连接,而是复用IO线程,讲到这里就要引出 Reactor 模式了,它的主要意义是将消息(IO事件)分发到用户提供的处理函数,并保持网络部分的通用代码不变,独立于用户的业务逻辑,没听过的请自行查询。

poll(reactor)

这是单线程的 reactor 方案,在没有事件的时候,线程等待在select/poll/epoll_wait 等函数上,事件到达后开始 read->decode->compute->encode->write。只有一个线程事件是顺序处理的,一个线程同时只能做一件事情,如果要执行延迟计算(把compute过程推迟100ms),那么应该注册超时回调,不能调用sleep()之类的阻塞调用,以避免阻塞当前 IO线程(Reactor事件循环所在的线程通常叫IO线程)。

while(true){
    n = epoll_wait(epfd, events, maxevents, -1);
    for(i=0; i<n; i++){
        if(events[i] == listenfd)
            // 处理连接
        else if (events[i].events & EPOLLIN)
        {
            // read compute write
            // 修改标识符
        }
        else
            // 其他处理
    }
}

reactor + thread-per-task

这是一个过渡方案,收到客户请求后,不在 IO 线程计算,而是创建一个新的线程去计算,以充分利用多核。需要注意它为每个请求(而不是每个连接)创建了一个新线程,这个开销可以用线程池来避免,后面会说到。还有一个问题是 out-of-order,即同时去创建多个线程去计算同一个连接上收到的多个请求,那么算出接锅的次序是不确定的,可能第二个请求计算时间更短,比第一个先返回。

reactor + worker thread

为了让返回结果的顺序确定,我们可以为每个连接创建一个计算线程,每个连接上的请求固定发给同一个线程去计算,先到先得。这样的话并发连接数受限于线程数目,

reactor + thread pool

这个方案是弥补方案7中为每个请求创建线程的缺陷,使用固定大小的线程池。全部的IO工作都在一个Reactor线程完成,而计算任务交给 thread poll。同样有乱序的可能。
线程池的另外一个作用是执行阻塞操作,让线程池来调用阻塞的IO函数等阻塞操作,可以避免阻塞IO线程,不会影响其他的客户连接。

这里写图片描述

reactors in threads

muduo、netty 内置的多线程方案,特点是 one loop per thread。有多个Reactor线程,muduo 采用的是固定大小的 Reactor pool,有一个 main Reactor 负责 accept 连接,然后把连接挂在某个 sub Reactor 中。这样该连接的所有操作都在那个 sub Reactor 所处的线程中完成,因此请求的顺序性有保证;多个连接可能被分到多个线程,以充分利用CPU。

这里写图片描述

reactors in processes

这是 Nginx 的内置方案,如果连接之间无交互,这种方案是很好的选择。工作进程之间相互独立,可以热升级(第一次听说……)。

reactors + thread pool

把方案9和方案10混合,既使用多个 Reactor 来处理 IO,又使用线程池来处理计算。这种方案适合既有突发IO(利用多线程处理多个连接上的IO),又有突发计算(利用线程池把一个连接上的计算任务分到多个线程去做)的应用。

这里写图片描述

构建基于网络服务的Windows应用程序 本文介绍如何在Visual Studio中创建和连接基于网络服务的Windows应用程序。通过示例代码和步骤说明了如何将网络服务嵌入到Windows窗体应用程序中,并设计相应的用户界面。此外,还探讨了通过网络服务实现特定功能的客户端代码编写,并提供了如何在应用程序中访问和使用网络服务的详细指导。 阅读详情

相关推荐

Rust编程在网络服务开发中的实践探索

本篇博客文章深入探讨了《Rust编程在网络服务开发中的实践探索》这本书的章节内容,重点介绍了Rust语言在网络编程中的应用,包括Rust工具链的使用、RESTful网络服务的构建、数据库持久化的实现、错误处理以及API的维护和重构等关键概念。文章不仅提供了对书中内容的详细解读,还结合了实际案例和个人感悟,旨在帮助读者深入理解Rust在网络服务开发中的优势。

weixin_33431149的博客 387

简单的TCP网络程序·单进程(后端服务器)

简单的TCP网络程序·单进程

weixin_67595436的博客 1944

网络服务程序

网络与单片机的学习,主要是单片机的学习,网络程序是非常难得

网络编程(全)

TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。服务端程序,需要事先启动,等待客户端的连接。客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。类表示。创建对象,相当于开启一个服务,并等待客户端的连接。

weixin_45012397的博客 1万+

数学建模10大最常用算法、程序源码打包.zip

数学建模10大最常用基本算法和程序源码打包都在这里边了

网络文件服务程序

基于Java Socket TCP和UDP实现一个简易的网络文件服务程序,包含服务器端FileServer和客户端FileClient

Java高级编程——网络编程

Java高级编程——网络编程

qq_61524266的博客 2299

多线程服务器的常用编程模型

文中的“ 多线程服务器” 是指运行在 Linux 操作系统上的独占式网络应用程序。硬件平台为 Intelx64 系列的多核 CPU, 单路或双路 SMP 服务器(每台机器一共拥有四个核或八个核,十几 GB 内存) ,机器之间用百兆或千兆以太网连接。这大概是目前民用 PC 服务器的主流配置。

(P8)socket编程三:处理多客户连接(process-per-connection),点对点聊天程序实现

文章目录 服务器端处于TIME_WAIT端是无法bind地址的 在TIME_WAIT状态还未消失的时候,就允许他重启 服务器端处理多个客户端连接 父进程用来接收客户端的连接 子进程处理与客户端的通信细节 服务端分为:监听套接字和已连接套接字 监听套接字用来接收三次握手数据,三次握手完成,就将其放到已连接队列中,accept函数从已连接队列返回一个连接,即已连接套接字。 已连接套接字不能接收连接,他是主动套接字 客户端只有已连接套接字 当父进程退出时,例可通知子进程退出 ...

喜欢打篮球的普通人 655

AD20.多对多工程中将某个原理图导入PCB工程

AD20将多个原理图中的一个或多个导入到PCB文件中

鬼爷杂货铺 1万+

二进制、八进制、十六进制、十进制转换详解

二进制是计算机的语言,正如我们使用汉语表达与交流信息一样,任何信息在计算机中都以二进制的形式存储和计算。了解二进制与十进制等进制之间的转换方法,有助于我们了解计算机内部的数字计算过程,可以帮助我们在一定程度上理解信息在计算机中的编码和解码过程。本文先简单介绍位置化数字系统的概念,然后详细讲解二进制、八进制、十六进制、十进制之间的转换方法与原理。

2301_81674797的博客 2095

Netty线程模型

一:Thread per Connection在没有nio之前,传统的网络编程采用的线程模型。当连接建立后,创建新的线程/从线程池中取一个,处理连接。这个优缺点很明显。优点:实现简单,缺点:受到了线程数的限制。二:Reactor in Single Thread有了nio,就可以采用io的多路复用机制。我们抽取出一个单线程版的reactor模型,时序图见下文,该方案只有一个线...

weixin_34413802的博客 181

DAIC-WOZ抑郁数据集实战:从申请到特征提取的全流程避坑指南

本文详细解析了DAIC-WOZ抑郁数据集从申请到特征提取的全流程,提供了实用的避坑指南和代码模板。涵盖数据获取技巧、多模态特征工程(面部动作单元、音频特征、文本处理)以及样本不平衡解决方案,帮助研究者高效处理这一经典抑郁分析数据集。

weixin_29196315的博客 171

使用 C++ 编写高并发网络服务程序模型

使用 C++ 编写高并发网络服务程序模型

小桥流水 的专栏 832

EtherCAT-1 技术概述

1.EtherCAT简介①开放的技术EtherCAT全称EtherNet Control Automation Technology,是由德国倍福(Beckhoff)公司提出的一种实时以太网技术。EtherCAT是一种开放但不开源的技术,意味着您可以任意使用这项技术,但若要进行相关设备的开发,则需要向倍福公司获取相关授权。②快速性相比传统现场总线,EtherCAT的数据传输速率有了极大的提升,可选10Mbit/s或100Mbit/s,甚至依托补充

tianya_xuan的博客 3543

Unity AssetBundle 之 (入门)简单的资源打包成AB包的方法

Unity AssetBundle 之 (入门)简单的资源打包成AB包的方法 目录 Unity AssetBundle 之 (入门)简单的资源打包成AB包的方法 一、简单介绍 二、实现原理 三、注意事项 四、效果预览 五、实现步骤 六、关键代码 一、简单介绍 Unity中的一些基础知识点。 本节介绍,Asset Bundle 在 Unity中的使用,入门第一篇,如何AssetBundle标记,如何打包成AssetBundle的操作方法,有不对的地方欢迎指正。...

仙魁XAN 1万+

利用Python的SocketServer框架编写网络服务程序

1.前言:        虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也是Python标准库中很多服务器框架的基础。     2.网络服务类: SocketServer提供了4个基本的服务类: TCPSer

勤能补拙 1万+

通过Power Platform自定义D365 CE 业务需求 - 4. 使用Power Automate

Power Automate构建块Power Automate中的连接器流的类型和用途流中的动态值和表达式使用变量、循环和条件在Power Automate中处理审批业务场景、用例和实现Power Virtual Agent的概念Power Virtual Agent的构建块配置Power Virtual Agent示例在公共网站/门户中部署Power Virtual Agent业务场景、用例和实现。

Martin-Mei的博客 557

常用的网络服务以及端口的分类

常用的网络服务TCP/IP的各层的PDU传输层的协议和他们的区别TCP传输协议和UDP传输协议的区别客户端和服务器的关系常用的网络服务和它们的作用以及端口端口号公认端口注册端口私有端口 TCP/IP的各层的PDU 分层 PDU 传输层 数据段 网络层 数据包 数据链路层 数据帧 物理层 比特流 传输层的协议和他们的区别 传输层协议有TCP传输协议和UDP传输协议 TCP传输协议和UDP传输协议的区别 -TCP传输协议 :一TCP将报文划分为较小的片段,称为数据段。 一数据段按

weixin_50345059的博客 2302

【零基础入门unity游戏开发——2D篇】SpriteEditor图片编辑器

【零基础入门unity游戏开发——2D篇】SpriteEditor图片编辑器

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 2326

Oracle EBS R12 主 / 辅助分类帐(主 / 辅助帐套)落地

本文提供了Oracle EBS R12主/辅助分类帐实施的全套文档模板,包含主帐套(法定帐套)和辅助帐套的完整配置清单、科目映射规则模板、4类典型调整分录示例(存货跌价、折旧差异、往来重分类、外币折算)以及税会/IFRS案例分录。重点阐述了主/辅助帐套的配置要点、SLA子分类帐关键配置、科目映射规则(一对一、多对一、重分类等类型)以及调整分录的自动生成逻辑。文档可直接用于项目实施,包含总体架构图、科目映射矩阵表、会计规则集配置等交付物,并附有Excel版模板便于直接使用。适用于需要实现多准则并行记账、税务调

金牌架构师 385

高性能网络服务程序简介

网络服务程序一般分为:单线程重复式、多进程网络服务、多线程网络服务、线程池网络服务及IO多路复用。   1、单线程重复式,是一种简单的阻塞等待,侦听服务,连接,提供服务,释放,连接,服务,释放......... 若当前处于连接状态,有新的连接到达,则进入排队队列等待服务。    单线程重复式实现简单,但是确定也明显,每次只能为一个用户提供服务,若服务时间长,则对排队连接的用户体验产生不利影响。

dadidiaosu的博客 520

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

python可以用来编写计算机网络程序吗_python实现strstr()利用Python的SocketServer框架编写网络服务程序...

1.前言:虽说用Python编写简单的网络程序很方便,但复杂一点的网络程序还是用现成的框架比较好。这样就可以专心事务逻辑,而不是套接字的各种细节。SocketServer模块简化了编写网络服务程序的任务。同时SocketServer模块也是Python标准库中很多服务器框架的基础。2.网络服务类:SocketServer提供了4个基本的服务类:TCPServer针对TCP套接字流UDPServer...

weixin_39676034的博客 154

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

OSI七层参考模型与TCP/IP五层参考模型、数据的封装与常用的网络服务

将复杂的流程分解为几个功能相对单一的子过程就叫分层思想;OSI七层参考模型是:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。TCP/IP五层参考模型:应用层、传输层、网络层、数据链路层、物理层。

weixin_42449832的博客 809

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

UNIX网络编程:高性能网络服务

Linux的一个应用优势是可用于设计各种高性能网络服务程序,高性能得一个特点就是实现并发访问处理,及服务程序能够同时为多个在线用户提供服务,高性能服务程序得应用非常广泛,在当前流行得Web服务器,各种游戏服务器中都能看到它的身影,而多进程网络服务程序,多线程网络服务程序,以及线程池网络服务程序的实现会更加提高网络服务的性能。高性能网络服务程序简介 高性能网络服务程序在当前的LINUX环境下应用非常

Dandelion_gong的博客 2404
上一篇: Muduo网络库源码分析之TcpConnection Class
下一篇: 复合&继承关系下的构造和析构
Tanswer_
博客等级 码龄11年 231粉丝 91原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值