`
顽石
  • 浏览: 166131 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

socket编程:同步异步与阻塞非阻塞的辨析

 
阅读更多

1.socket io同步异步/阻塞非阻塞的概念和本质区别

异步操作相比同步操作可以提高应用的响应性和cpu的利用率,增大应用吞吐量。在socket编程中也存在异步io和同步io,再加上阻塞和非阻塞这些,这两组概念碰到一起,很容易让人混淆,搞不清楚同步异步和阻塞非阻塞这两组概念的本质区别。

其实在W. Richard Stevens的《UNIX网络编程》(1) 6章第6.2io模型中对这些讲的比较清楚了。

socket异步io unix/linux中使用aio*系列io方法(aio_readaio_write)才是异步socket iowindows中的完成端口之类的也是异步。异步当然是非阻塞的。为何叫异步:socket io读写方法(例如linux中的aio_readaio_write)和io的实际执行是独立的,分开/分离的:aio_read读方法和aio_write写方法只是将读写请求(指令)排队后就返回,这两个方法并不执行实际的io读写操作,也不关心实际的读写操作是否执行完成,实际的读写由操作系统在幕后执行。待读写操作有最终结果后,操作系统再通过回调方式或信号(singal)通知应用,告知先前的操作结果和数据;这个异步和我们在公司填请假电子流类似,提交请假申请就完事,工作流系统到时会发邮件告知请假审批结果。

socket同步io:调用recv/read send/write 方法的就属于同步socket io,因为在这些api方法中会执行实际的io操作,也就是读写方法和实际的io执行是绑在一起的。

那阻塞/非阻塞与同步/异步有啥区别?

这是两个不同的维度:

阻塞/非阻塞是指 io方法是否会阻塞线程,阻塞:如果socket读缓冲区为空,或缓冲区可读字节数少于你要读的字节数时读方法阻塞;如果写缓冲区满,或写的字节数大于写缓冲区的空闲字节数,此时执行写操作就会阻塞。非阻塞:不管是否能完成读写任务都不会阻塞,不能进行读写时立即返回(有相应的错误码),不会阻塞线程。在非阻塞情况下,写方法可能写出部分数据(部分字节),读方法读到的字节数可能少于要求的字节数。

具体来说就是如果socket不可读或不可写时(不可读:socket本地读缓冲区没数据或没有足够多的数据;不可写:写缓冲区满或空闲空间较少),此时执行io方法是否会阻塞线程;同步/异步是指io方法(api层面调用的方法)和io执行是否绑在一起。

 这两个维度合在一起可有四种组合:同步阻塞、同步非阻塞、异步阻塞、异步非阻塞。前面的请假例子中,请假的实际处理(领导审批)很显然不会成为阻碍你提交请假申请的一个因素,与此类似,实际情况中是没有异步阻塞这种组合的。采用异步io时,实际的io是在操作系统另外的线程中执行,从aio_readaio_write 这些异步api方法层面来看,它们只是提交了io请求或io申请而已,socket io的实际执行或socket是否可读可写并不会阻塞这些方法的执行。所以实际上只有三种组合:同步阻塞、同步非阻塞、异步非阻塞

2.多路复用

另外selectpollepoll之类的方法是为了在服务器端有效处理大量的网络io(例如大量的tcp连接)的情况下引入的多路复用机制,可用少量线程资源处理大量的tcp连接,可伸缩性好,解决C1xK问题会用到epoll。多路复用:可理解为悲观策略,先用selectepoll检测socket的读写状态(可读:socket本地读缓冲区中有数据。可写:本地写缓冲区有空闲空间),检测到可读或可写状态事件后,才派发事件:调用recv/read send/write这些同步方法发起读写操作,既然采用这些同步方法,那就仍是同步io,不过配合多路复用,这些读写方法一般是非阻塞模式,因为如果采用阻塞模式,即使检测到socket可读写后,后续的读写方法仍可能会阻塞住线程,例如检测到有可接受的tcp连接(可读)后,后续调用accept接受连接时,连接却中途被关闭或取消,此时accept就会阻塞,或者在数据socket上检测到可读,读缓冲区只有10个字节,但后续的读操作却要求读出大于10字节的数据,此时recvread就会阻塞。线程阻塞后,很可能会延误其他tcp连接上的读写,不符合多路复用的初衷。所以多路复用大多采用同步非阻塞模式。

多路复用情况下,存在平(水平)触发和边沿触发的概念,这两个概念来自数字电路的高低电平和脉冲的上升沿下降沿。可读可写可理解为高电平1,电平触发是只要socket高电平(可读可写),都会唤醒,最终会触发读写操作;边沿触发是上升沿(低电平变成高电平)才唤醒触发一次,如果后续仍为高电平,不会再唤醒触发,除非变成低电平后再变成高电平才行。

select是电平触发,epoll可支持电平触发和边沿触发。


   3.多路复用中的编程注意事项

多路复用中的电平触发编程较简单,但也有一些需要注意的地方,特别是在检测可写状态时,如果检测到可写,却没有需要写出的数据,那就会产生持续的多次触发,导致cpu负载过高,也浪费了cpu资源,因没有数据写出,因此采用电平触发时,一般采用按需检测可写状态事件的策略:只有当数据准备好需要写出时,才进行selectpollepoll的可写状态检测,写完后不要继续监测可写事件,如果单次写不完,可以放到缓冲区中,继续监测可写状态,待下次监测到可写时继续写出,或者循环多次写,直到写完为止(如果数据太多,可能对其他socket不公平),在循环写过程中,如果写方法返回不可写的错误码,将剩余的数据放到缓冲区,结束写,继续监测可写状态。在udp可写状态更要注意该问题,因为udp socket总是可写,当然tcp也要注意该问题,只是udp情况更严重。在写时要注意同步,防止有先前挂起的(pending)的写操作,在每个socket上应关联一个是否有挂起的写操作的状态标志,如有挂起的写操作时不能直接调用写方法,而是要将数据追加到应用层写缓冲区,后续的可写事件会把这些缓冲区数据写出。对读操作简单些,可以完全依赖可读事件来驱动实际的读操作,和写类似,可以单次读也可循环读,循环读(前面提到过多路复用一般采用非阻塞,循环读,直到读方法实际读出的字节数少于希望读的字节数或读方法返回相应的错误码指示没有数据了为止)

在边沿触发时,触发的次数少了,上升沿触发一次后,虽然后续仍保持可读可写状态也不触发了。在读写时,不能完全依赖边沿触发来驱动读写操作,特别是写操作,上层应用的写请求和底层socket的可写状态本来就是两码事,是独立的异步的。边沿触发模式的读写策略:对读,和电平触发的循环读相同,在检测到某个socket可读事件后,不考虑公平性,一直读(循环读),读到没数据为止(读方法实际读出的字节数少于希望读的字节数或读方法返回相应的错误码指示没有数据了为止);对写,先触发io线程直接写(要注意同步,防止socket上有挂起的写操作,如果有挂起的写操作就不能直接写,应将数据字节追加到应用层的写缓冲区,这些缓冲区的数据会由边沿触发的可写事件来启动写出操作),如果把数据全部写出,那ok完事,如果只写出部分数据(写方法的返回值大于零且小于需要写出的字节数),那就继续重复写剩余的字节数据,直到全部写出,如果在重复写的过程中返回错误码(为不可写导致的错误码),此时将剩余的数据字节追加到缓冲区,启动该socket的可写检测。当检测到可写后,触发io线程把写缓冲区的数据写出,这个仍是遵从前面的模式,重复写,写完后也不要继续监测可写状态。

可见无论是电平触发和边沿触发,对写都是按需检测可写状态事件,都要注意写时的同步。电平触发可采用单次读写或循环读写,对边沿触发一般是采用循环读写。边沿触发通常高效,但编程较复杂。

 

另外如果要支撑的tcp连接数较少,可采用每tcp连接一个线程或一个进程来进行处理,此时一般采用同步阻塞模式,编程简单。

 

  • 大小: 8.2 KB
分享到:
评论

相关推荐

    tcp socket同步 异步 阻塞 非阻塞 的解释.zip

    tcp socket同步 异步 阻塞 非阻塞 的解释.zip 基本概念:同步、异步、阻塞和非阻塞的区别 同步异步与阻塞非阻塞的区别 C++ 同步异步与阻塞非阻塞的区别

    C++网络编程例子说明异步非阻塞Socket的基本原理和工作机制

    用一个最简单的例子说明异步非阻塞Socket的基本原理和工作机制

    Linux UDP socket 设置为的非阻塞模式与阻塞模式区别

    - **非阻塞模式**:与此相反,在非阻塞模式下,如果执行I/O操作的条件不满足,则立即返回一个错误码或特定值,而不是挂起进程。 #### 三、UDP Socket 模式详解 ##### 3.1 UDP Socket 的阻塞模式 在阻塞模式下,UDP ...

    同步异步阻塞非阻塞

    同步异步阻塞非阻塞 IO 模型 在 Linux 环境下的网络 IO 中,有五种基本的 IO 模型:阻塞 IO、非阻塞 IO、IO 多路复用、信号驱动 IO 和异步 IO。其中,信号驱动 IO 不常用,因此主要介绍其余四种 IO 模型。 1. 阻塞...

    Linux下的Socket编程实例(阻塞和非阻塞)

    Linux下的Socket编程实例(阻塞和非阻塞) 通过分析给定的文件信息,我们可以生成以下知识点: Socket编程概述 Socket 编程是指使用操作系统提供的 socket 编程接口来实现网络通信的编程方式。Socket 编程可以实现...

    详解socket阻塞与非阻塞,同步与异步、I/O模型

    Socket编程中的阻塞与非阻塞、同步与异步是两个独立的概念,它们涉及的是不同层面的操作机制。这里我们将详细探讨这两个概念以及I/O模型。 首先,同步与异步是客户端(C端)调用服务端(S端)时的行为模式。同步...

    IO中同步、异步与阻塞、非阻塞的区别

    阻塞调用与同步调用不一样,同步调用时线程通常是激活状态,只是逻辑上没有返回。 B. 非阻塞 非阻塞调用则是指在结果未返回前,不会阻塞当前线程,而是会立即返回。线程不会处于等待状态,可以继续执行后续代码。...

    C++ socket 阻塞与非阻塞

    本话题主要探讨的是在C++ Socket编程中的阻塞模式与非阻塞模式,以及它们在多线程环境下的应用和资源管理。 一、阻塞与非阻塞模式 1. **阻塞模式**:在阻塞模式下,当一个Socket调用(如recv或send)执行时,如果...

    Socket C++ TCP阻塞\非阻塞 服务器 客户端 开发

    通过本篇内容的学习,您将了解到设置socket函数为非阻塞模式的方法,并且能够深入了解阻塞与非阻塞模式在socket编程中的应用。此外,我们还将探讨SOCKET类的设计与实现。 #### 一、基本概念与环境搭建 **1.1 基本...

    C# Socket 同步异步编程实例

    本篇将深入探讨C#中的Socket同步和异步编程实例,帮助开发者理解和掌握这两种模式的使用。 **1. Socket基础知识** Socket是网络编程中的一个抽象概念,它是应用程序与网络协议栈之间的接口,提供了发送和接收数据的...

    Socket编程 (异步通讯) (Tcp,Udp)

    Socket编程 (异步通讯) (Tcp,Udp) ...Socket编程 (异步通讯) (Tcp,Udp)Socket编程 (异步通讯) (Tcp,Udp)Socket编程 (异步通讯) (Tcp,Udp)Socket编程 (异步通讯) (Tcp,Udp) Socket编程 (异步通讯) (Tcp,Udp)

    异步非阻塞socket聊天室程序

    在IT领域,网络编程是不可或缺的一部分,特别是在开发实时交互系统如聊天室时。"异步非阻塞socket聊天室程序"是一个...理解并掌握异步非阻塞的socket编程对于任何希望从事网络编程的IT专业人员来说都是至关重要的技能。

    windows的阻塞和非阻塞Socket编程

    非阻塞模式适用于需要高效处理多个连接,且不能长时间等待I/O完成的情况,如服务器端的多线程或异步编程。 在Windows中,可以使用WSAAsyncSelect()或WSAEventSelect()函数将Socket设置为非阻塞模式,并与Windows...

    C__Socket编程_同步以及异步通信.pdf

    ### C__Socket编程_同步以及异步通信 #### 套接字简介 套接字是一种用于网络通信的编程接口,最初由Unix系统发展而来,随后Windows和其他操作系统也采纳了这一设计思想。套接字提供了应用程序之间通过网络进行通信...

    网络socket 编程指南

    - send/recv函数:发送和接收数据,理解阻塞与非阻塞模式的区别。 - close函数:关闭Socket,释放资源。 5. 错误处理与调试: - 网络异常处理:如何处理常见的网络错误,如ECONNREFUSED、ETIMEDOUT等。 - 日志...

    Socket客户端,服务端同步异步实现

    Socket编程是计算机网络通信中的重要组成部分,主要用于...理解并熟练掌握Socket同步异步编程,对于开发高并发、高性能的网络应用至关重要。通过阅读和分析提供的源码,我们可以深入理解这些概念,并应用到实际项目中。

    完整版本网络编程 TCP Socket 同步、异步

    在深入学习这部分内容时,你需要理解TCP连接的生命周期、Socket API的使用、多线程或多进程模型在同步和异步编程中的应用,以及如何设计和实现高效的并发处理策略。同时,了解网络编程中的错误处理、异常处理和性能...

    网络IO模型:同步IO和异步IO,阻塞IO和非阻塞IO

    这里我们将深入探讨同步IO、异步IO、阻塞IO和非阻塞IO的概念,理解它们的工作原理以及在实际应用中的差异。 1. 同步IO与异步IO: - **同步IO**:在同步模式下,应用程序执行I/O操作时会等待操作完成。这意味着程序...

    socket 多线程 例程 非阻塞模式

    总之,"socket多线程例程非阻塞模式"是一个实用的编程实例,涵盖了网络通信、多线程以及异步处理的关键技术,对于学习和理解这些概念非常有价值。通过对源代码的分析和实践,开发者能够提升自己的网络编程技能,为...

Global site tag (gtag.js) - Google Analytics