网络编程大并发实战-基础概述

作为基础介绍的篇章,网络编程大并发要介绍的基础还是计算机网络的基础。网络基础的内容其实还是很多的,本篇只做一个指引性的介绍,如果要了解详情还是推荐查看书籍《网络编程卷一套接字》。本篇主要介绍网络协议及sock相关知识点.

在大学的时候我们应该都学过OSI网络体系都是分7层,但实际上的上的TCP/IP网络编程都是是5层,甚至作为程序员我们只关注4,3层相关的协议内容。具体见下图:

image

主要从数据流向连接关系两个方向介绍下传输层,网络层,网络接口层的内容及功能,当然还有一些基本概念。

 

连接关系

各层在连接中解决的问题

网络接口层(数据链路层)

网络接口层主要以网卡的MAC地址作为识别对象,借助网卡,集线器双绞线通过太网协议(广播的方式)等实现(同一网络)不同机器之间的数据通讯。协议规定传输的单位数据为一组电讯号组成的数据包叫:帧。同时一般来说每次帧大小的限制为 1518字节。这一层功能保证数据在物理设备上的无错误传输。

网络层

以太网协议可以实现同一局域网内不同机器之间的数据通讯。而网络层,主要以IP地址为识别对象,借助路由器通过IP协议等实现不通子网的数据通讯。这一层传输的是IP数据包。大小为同时IP协议的功能是无连接数据报传输、数据报路由选择和差错控制。其可靠性需要靠传输层保障。

传输层

有上面两个分层的协议及mac地址及IP地址的时候,我们已经可以在无限制的任意网络中的两台主机进行数据通讯了,而传输层可以将通讯数据准确的定位到具体的程序进程中。其依靠端口号为识别对象 (在网卡中绑定端口号及进程号),借助网卡通过TCP协议等实现进程之间的通讯。这一层中,TCP协议传输对象是数据段,UDP协议传输对象是数据报。

小结:基于分层协议,借助网卡,网关,路由器等为载体,以mac地址,ip地址,端口为识别对象,使得数据能够在任意位置的进程间进行通讯。

数据流向

  进行数据通讯过程中的各层数据流向及条件。

image

 

image

(TCP/IP数据流向)

基础概念

PCI:  协议控制信息

PDU:协议数据单元(计算机网络对等层协议交换数据的单位信息)

SDU: 服务数据单元((层与层协议之间的交互)层用户与层协议之间的传递数据单位信息)

SDU(n)  = xPDU(n+1) ;             

PDU(n) = PCI(n)+ySDU(n) = PCI(n)+zPDU(n+1);        (x,y,z>0)

segment(分节):TCP传输层的PDU。

传输层

应用层的数据字节流,并且大小不一,而到了传输层,其传输的应用层数据就要被切分格式化层message,如果是TCP协议,则为segment,这个协议传输的大小一般需要小于 MSS及MTU,原因是为了传输效率,如果把每一层的数据协议容量大小叠加起来看成漏斗,为了在传输过程中不卡壳数据重新切分(这样做会增加传输协议头相关的内容),那么最好在一开始的传输层就把数据切分处理好,方便直接通行。同时MSS这个阈值是在计算机作为参数设置的,所以也是作为调优的手段。

网络层

网络层的PDU是IP数据报,其中,IPV4协议的规定的最大容量是65535字节,IPV6协议规定的容量是65575字节,在看了后面的网络接口层厚我们可以知道这一层中容量不会成为这个传输漏斗的瓶颈。

网络接口层(链路层)

网络接口层的PDU 是 frame, 由于以太网传输电气方面的限制,每个以太网帧都有最小的大小64bytes,最大不能超过1518bytes。当两台远程PC互联的时候,它们的数据需要穿过很多的路由器和各种各样的网络媒介才能到达对端,网络中不同媒介的MTU各不相同,就好比一长段的水管,由不同粗细的水管组成(MTU不同 :))通过这段水管最大水量就要由中间最细的水管决定。同时当今的网络设备一般都支持本端网络接口层在通信前一般就会先检测出整个链路的 MTU。

小结:

从上面我们可以得出数据的流向,及其一些特性,为了在网络层不将数据进行分片,从而导致数据头传输增加,传输顺序变化等一些问题的产生,一般我们需要在传输层就把segment数据的大小限制住,这样三层结构的容量形状就如果一个菱形如下:

image

MSS的大小调整一般需要有MTU决定。

 

SOCKET

TCP连接与分组交换

 

 

image

为了建立一个可靠的TCP连接并传输数据,从图中我们看到,前3个分节用于建立连接(三路握手),最后四个分节用于终止连接。同时为了保证可靠传输,在数据应答中捎带了一个应答确认的请求(ACK),客户端还必须发送1个应答分节,为了传输一个小于服务器通告的最大分节的数据(MSS),需要至少额外 8 个分节的辅助才能完成。

但是这额外的八个分节开销给我们提供了诸如如下的特性:可靠性,动态估算客户端与服务器往返时间(RTT算法),分节排序,重传,拥塞控制(通告窗口:告知对端任何时刻它一次性能够接收多少字节数据,MSS:本连接每个分节能够接收数据大小,都在 SYN 连接分节中设置告知对端),全双工(在一条建立的连接上即可以发送数据也可以接收数据),最大分节生命周期MSL。

为了减少数据开销,目前也有把UDP协议在应用层封装上部分TCP的特性进行可靠的UDP数据通信。

上述的TCP通信状态中需要特别说明下 TIME_WAIT 状态,该停留时间最大为2倍的最大分节生命周期,2*MSL,而MSL一般为30秒左右。同时每个IP数据报都有一个TTL,限跳阈值,防止路由回路等迷路现象。TIME_WAIT 的存在有如下两个作用:

当ACKN+1丢失后,允许接收端在TIME_WAIT内重新接收到对端发来的FIN N。可靠的实现全双工连接的终止。

当某个老的重复分节数据因为回路修复等在超过MSL的时间内重新出现后,新的连接就可能收到老的分节,TIME_WAIT保证在在关闭当前连接后的TIME_WAIT时间内不能重新发起新的连接,当前连接需要等待2MSL时间才会回到初始状态,即直到所有老的分节都过期消逝。这就允许了老的重复分节的消逝。

在本进程用socket:close()关闭一个链接,会进入TIME_WAIT状态,从应用层来看该连接已经不能使用该,但从传输层来说,还是会尝试吧sendbuffer 都发送出去再发起4次握手进行连接的关闭。如果是个多进程,socket:close 只会把引用计数减一,其他进程还是可以使用该链接。

而使用该socket :shutdown,可以选择关闭数据流通方向,不可读,不可写,双边不可用。同时该效果会作用在其他进程。

 

完结

参考:

《UNIX网络编程卷1:套接字联网API》

《计算机网络》

http://www.ruanyifeng.com/blog/2012/05/internet_protocol_suite_part_i.html

 

返回

网络编程大并发实战-开篇

技术革新日新月异,特别是互联网的井喷式发展也同样敦促着网络编程技术的革新。特别是C10K,C100K,C1M这些问题的出现,成本的不断升高,都督促着网络技术的发展。本系列将从各类规模并发长连接问题实战角度出发,通过:

分析问题,解决问题,优化分析。

来对IO模型及IO框架及编程模型等一些技术在不同规模场景下的选型抛砖引玉。

本篇幅只考虑服务器编程中的网络IO部分,对于其他诸如逻辑处理部分及存储部分未有提及。

基本规划目录如下:

网络编程大并发实战-基础概述

网络编程大并发实战-IO模型介绍

网络编程大并发实战-IO模式介绍

网络编程大并发实战-IO并发编程模型

网络编程大并发实战-IO常见框架服务器介

网络编程大并发实战-C10K(问题历史分析,最优选型,问题解决,实现分析(瓶颈,优化点)

网络编程大并发实战-C100K

网络编程大并发实战-C1M

网络编程大并发实战-新技术预览

 

PS:希望本系列可以早点结束

计算机原理-IO(三)

CPU和存储器之间是如何相互之间通信的呢,IO性能的瓶颈又是在哪里,希望看了这节可以给你解答。

总线

目前软件架构都流行ESB企业总线来处理消息的相互流转,这个架构灵感就参考了计算机中的总线。所谓总线是各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束。我们知道计算机有运算器,控制器,存储器,输入输出设备这五大组件,所以总线就是用来连接这些组件的导线。

按照计算机所传输的信息种类,计算机的总线可以划分为

  • 数据总线: 数据总线DB是双向三态形式的总线,即它既可以把CPU的数据传送到存储器或输入输出接口等其它部件,也可以将其它部件的数据传送到CPU。数据总线的位数是微型计算机的一个重要指标,通常与微处理的字长相一致。我们说的32位,64位计算机指的就是数据总线。
  • 地址总线: 地址总线AB是专门用来传送地址的,由于地址只能从CPU传向外部存储器或I/O端口,所以地址总线总是单向三态的,这与数据总线不同。地址总线的位数决定了CPU可直接寻址的内存空间大小
  • 控制总线:控制总线主要用来传送控制信号和时序信号。控制总线的传送方向由具体控制信号而定,一般是双向的,控制总线的位数要根据系统的实际控制需要而定。其实数据总线和控制总线可以共用。

总线也可以按照CPU内外来分类:

  • 内部总线:在CPU内部,寄存器之间和算术逻辑部件ALU与控制部件之间传输数据所用的总线称为片内部总线。
  • 外部总线:通常所说的总线指片外部总线,是CPU与内存RAM、ROM和输入/输出设备接口之间进行通讯的通路,也称系统总线。

控制芯片

主要控制CPU和存储器,I/O设备等进行交互。一般都集成在主板上,所以攒机也不能忽略主板的。

对于目前的计算机结构来说,控制芯片集成在主板上,典型的有南北桥结构和单芯片结构。与芯片相连接的总线可以分为前端总线(FSB)、存储总线、IQ总线,扩展总线等。

  • 南北桥芯片结构
    • 北桥芯片,它控制着CPU的类型,主板的总线频率,内存控制器,显示核心等。它直接与CPU、内存、显卡、南桥相连,所以它数据量非常大;
      • 前端总线:是将CPU连接到北桥芯片的总线。FSB的频率是指CPU和北桥之间的数据交换速度。速度越快,数据带宽越高,计算机性能越好;
      • 内存总线:是将内存连接到北桥芯片的总线。用于和北桥之间的通信;
      • 显卡总线:是将显卡连接到北桥芯片的总新。目前有AGP,PCI-E等接口。其实并没有显卡总线一说,一般认为属于I/O总线;
    • 南桥芯片,它主要负责外部接口和内部CPU的联系;
      • I/O总线:连接外部I/O设备连接到南桥的总线, 比如USB设备,ATA,SATA设备,以及一些扩展接口;
      • 扩展总线:主要是主板上提供的一些PCI,ISA等插槽;
  • 单芯片结构: 单芯片组主要是是取消了北桥,因为现在CPU中内置了内存控制器,不需要再通过北桥来控制,这样就能提高内存控制器的频率,减少延迟。而现在一些CPU还集成了显示单元。也使得显示芯片的频率更高,延迟更低。

频率=性能

数据带宽 = (总线频率*数据位宽)/ 8

(每秒传送次数*每次传送大小)/单位大小转换

外频:系统总线的工作频率,一般的电脑这个是连接其他部件工作的基础频率。

分频:外部IO频率比系统总线低,采用2/3或1/3分频的方式就能使得CPU和外设同步的工作了。

倍频:CPU比外频高,所以需要一个倍频来协调工作。

CPU工作频率=外频*倍频

内存总线频率:

因为内存总线频率不同,所以内存和CPU之间存在同步和异步两种工作方式。

  • 同步方式:内存总线频率和CPU外频相同。比如以前的PC133和P3处理器,他们以同步的方式工作在133MHZ下。而当你超频时就需要拥有更高总线频率的内存。当然也需要北桥芯片的支持。
  • 异步方式:内存总线频率和CPU外频不同。睡着CPU外频的提高,内存也必须更新,所以出现了异步的方式。比如P4 CPU外频为200MHz,而内存却可以使用DDR333来运行。同时异步方式也在超频时经常使用。一般来说会有一个内存异步比率。在BIOS中会有相应的选项。

从性能上来讲,同步方式的延迟要好于异步方式,这也是为什么以前会说P4 200外频的CPU要使用DDR400才能发挥最大功效。但这也不是绝对的。比如我的I5处理器CPU外频工作在100MHz,而我使用的DDR3-1600的总线频率在200MHz,虽然不同步,但是拥有更高的传输速率。所以不能一概而论。

外部IO:

前面主要介绍了系统总线和CPU与内存之间的通信,最后一部分简单介绍一下CPU和I/O设备是如何通信的。对于计算机来说输入输出设备也是五大组件。我们知道相对于CPU,I/O设备的工作频率要慢的很多。比如早期的PCI接口工作频率只有33MHz,硬盘的IDE-ATA6的传输速率也只有133MB/s。而现在的 SATA3接口速率能达到600MB/s。

I/O设备一般由机械部件和电子部件两部分组成。电子设备一般称为设备控制器,在计算机上一般以芯片的形式出现,比如我们前面介绍的南桥芯片。不同的控制器可以控制不同的设备。所以南桥芯片中包含了多种设备的控制器,比如硬盘控制器,USB控制器,网卡、声卡控制器等等。而通过总线以及卡槽提供和设备本身的连接。比如PCI,PCI-E,SATA,USB等。

每个控制器都有几个寄存器和CPU进行通信。通过写入这些寄存器,可以命令设备发送或接受数据,开启或关闭。而通过读这些寄存器就能知道设备的状态。因为寄存器数量和大小是有限的,所以设备一般会有一个RAM的缓冲区,来存放一些数据。比如硬盘的读写缓存,显卡的显存等。一方面提供数据存放,一方面也是提高I/O操作的速度。

现在的问题是CPU如何和这些设备的寄存器或数据缓冲区进行通信呢?存在两个可选方案:

  1. 为每个控制器分配一个I/O端口号,所有的控制器可以形成一个I/O端口空间。存放在内存中。一般程序不能访问,而OS通过特殊的指令和端口号来从设备读取或是写入数据。早期计算机基本都是这种方式。
  2. 将所有控制器的寄存器映射到内存空间,于是每个设备的寄存器都有一个唯一的地址。这种称为内存映射I/O。

    另一种方式是两种的结合,寄存器拥有I/O端口,而数据缓冲区则映射到内存空间。Pentinum就是使用这种方式,所以在IBM-PC兼容机中,内存的0-640K是I/O端口地址,640K-1M的地址是保留给设备数据缓冲区的。(关于内存分布后面文章会介绍)

    对于我们程序员来说这两种方案有所不同

    1. 对于第一种方式需要使用汇编语言来操作,而第2种方式则可以使用C语言来编程,因为他不需要特殊的指令控制,对待I/O设备和其他普通数据访问方式是相同的。
    2. 对于I/O映射方式,不需要特殊的保护机制来组织对I/O的访问,因为OS已经完成了这部分工作,不会把这一段内存地址分配给其他程序。
    3. 对于内存可用的指令,也能使用在设备的寄存器上。

    任何技术有有点就会有缺点,I/O内存映射也一样:

    1. 前面提到过Cache可以对内存进行缓存,但是如果对I/O映射的地址空间进行缓存就会有问题。所以必须有机制来禁用I/O映射空间缓存,这就增大了OS的复杂性。
    2. 另一个问题是,因为发送指令后需要判断是内存还是I/O操作,所以它们需要能够检查全部的内存空间。以前CPU,内存和I/O设备在同一个总线上,所以检查很方便。但是后来为了提高CPU和内存效率,CPU和内存之间有一条高速的总线(比如QPI)。这样I/O设备就无法查看内存地址,因为内存地址总线旁落到了内存和CPU的高速总线上,所以需要一个额外的芯片来处理(北桥芯片,内存控制器的作用),增大了系统的复杂度。

 

瓶颈:1.寄存器-》内存(倍频-外频-内存频率)(普通应用可以忽略)

            2.IO瓶颈    IO速度实在太低(高性能程序都使用预先加载到内存的方式或者内存映射的方式来处理IO瓶颈:以时间,空间,金钱换取速度)