深入学习理解UNIX网络编程

  • 一、OSI模型
  • 二、传输层协议
  • 三、传输控制协议(TCP)
  • 四、TCP连接的建立和终止
    • 1. 三路握手
    • 2. TCP选项
    • 3. TCP连接终止(4次挥手)
    • 4. TCP状态转换图
    • 5. 观察分组
  • 五、TIME_WAIT 状态
  • 六、端口号
  • 七、TCP端口号与并发服务器
  • 八、缓冲区大小及限制
  • 九、套接字地址结构
    • 1. IPv4套接字地址结构
    • 2. 通用套接字地址结构
    • 3. 套接字地址结构的比较
  • 十、socket函数
  • 十一、connect函数
  • 十二、bind函数
  • 十三、listen函数
  • 十四、accept函数
  • 十五、fork和exec函数
  • 十六、并发服务器
  • 十七、close函数
  • 十八、I/O模型
    • 1. 阻塞式I/O模型
    • 2. 非阻塞式I/O模型
    • 3. I/O复用模型
    • 4. 信号驱动式I/O模型
    • 5. 异步I/O模型
    • 6. 各种I/O模型的比较
  • 十九、select函数
  • 二十、poll 函数

一、OSI模型

  • OSI(open systems interconnection):开放系统互连模型
  • 七层模型:
    • 1,2层:设备驱动程序和网络硬件
    • 网络层
    • 传输层:TCP和UDP(原始套接字:网络应用可以绕过传输层直接使用网络层的IP协议)
    • 5,6,7:应用层(Web客户浏览器、Telnet客户、Web服务器、FTP服务器)
  • 套接字编程接口:从应用层进入传输层的接口
  • 接口在传输层的原因:分别处理应用细节和通信细节、分隔用户进程和操作系统内核
    • 应用层(顶上三层):

      • 处理具体网络应用(FTP、Telnet、HTTP)的所有细节,却对通信细节了解很少
      • 构成用户进程
    • 底下四层:
      • 对具体网络应用了解很少,却处理所有通信细节(发送数据、等待确认、数据排序、计算验证校验和等等)
      • 作为操作系统内核的一部分

二、传输层协议

  • 传输层:TCP、UDP、SCTP(Stream Control Transmission Proto:流控制传输协议)
  • UDP:简单的、不可靠的、无连接、数据报协议
    TCP:复杂的、可靠的、有连接、字节流协议(无消息边界、流量控制、全双工)
  • SCTP:可靠的、提供消息边界、传输级别多宿支持

三、传输控制协议(TCP)

  • 有连接:TCP提供客户与服务器之间的连接,通过连接交换数据
  • 可靠性:TCP向另一端发送数据时,要求对端返回一个确认,若没有收到确认,TCP就自动重传数据并等待更长的时间,数次重传失败后,TCP才放弃(4~10分钟)
  • 往返时间(RTT:round-trip time):客户与服务器之间数据传输的往返时间(TCP通过动态估算RTT来确定等待一个确认的时间)
  • 排序:给每字节关联一个序列号(可以判断数据是否重复传递)
  • 流量控制:TCP一次能够从另一端接收多少字节(通告窗口)
  • 通告窗口(通告形式告知滑动窗口大小):指出接收缓存区中当前可用的空间量
  • 全双工(full-duplex):在TCP连接的进出两个方向可以既发送数据又接收数据

四、TCP连接的建立和终止

1. 三路握手

  • 被动打开:服务器必须准备好接受外来的连接(通过调用socket、bind函数)
  • 主动打开:客户端通过调用connect函数发起主动打开,导致客户端发送一个SYN(同步)分节,包含服务器将发送数据的初始序列号
  • 服务器确认客户的SYN并发送ACK(确认),同时自己发送一个SYN分节(服务器将在连接中发送数据的初始序列号)
  • 客户确认服务器的SYN

2. TCP选项

  • 每一个SYN可以含有多个TCP选项
  • TCP选项:
    • MSS(maximun segment size):最大分节大小
    • 窗口规模:告知对端的最大窗口大小(65535)
    • 时间戳:防止失而复现的分组可能造成的数据损坏

3. TCP连接终止(4次挥手)

  • 主动关闭:某个应用程序先调用close函数,发送一个FIN分节,表示数据发送完毕
  • 被动关闭:接收到FIN的对端,将FIN作为一个文件结束符传递给自己的应用进程,并确认FIN
  • 一段时间后,接收到这个文件结束符的应用进程调用close关闭socket,导致TCP发送一个FIN
  • 接收到最终FIN的原发送端确认这个FIN,发送ACK

4. TCP状态转换图

5. 观察分组

五、TIME_WAIT 状态

  • TIME_WAIT状态:执行主动关闭的一端
  • 持续时间(2MSL):最大分节生命期(MSL)的两倍,一般
  • 分组在网络中迷途:路由异常
    • 发生原因:

      • 某个路由器崩溃
      • 某两个路由器之间的某个链路断开
    • 导致:TCP超时重传该分组,迷途的分组最终也到达,导致重复分组
  • TIME_WAIT状态存在理由:
    • 可靠地实现TCP全双工连接的终止:服务器没有收到最终的ACK会再次发生FIN,TIME_WAIT状态允许客户端重新发送最终ACK
    • 允许老的重复分节在网络中消逝:TIME_WAIT状态持续2MSL,使老的重复分组最多存活MSL秒即被丢弃,保证建立新的TCP连接时,来自该连接先前化身(同一个IP和端口)的老的重复分组已经消逝

六、端口号

  • Port number:端口号(16位整数)

    • 众所周知的端口:0~1023(例如:80分配给Web服务器)
    • 已登记的端口:1024~49151
    • 临时端口:49152~65535(动态的、私有的)
  • 套接字对(socket pair):TCP连接两端的四元组
    • 本地IP地址
    • 本地TCP端口号
    • 外地IP地址
    • 外地TCP端口号
  • 一个套接字:IP地址 + 端口号

七、TCP端口号与并发服务器

  • 服务器被动打开21端口,客户使用记号 {:21,:*} 指出服务器的套接字对
  • 监听套接字:“* . *”
  • 21端口存在3个套接字
    • 分节递送给子进程1:来自206.168.112.219:1500,目的地为12.106.32.254:21
    • 分节递送给子进程2:来自206.168.112.219:1501,目的地为12.106.32.254:21
    • 分节递送给服务器主进程的监听套接字:目的端口号为21的其他TCP连接

八、缓冲区大小及限制

  • MTU(maximun transmi unit):最大传输单元
  • 路径MTU:两个主机之间的路径最小的MTU
  • IPv4数据报的最大大小是65535字节,最小链路MTU68字节
  • 以太网MTU:1500字节
  • 分片:IP数据报的大小超过相应链路的MTU
    • 主机和路由器都会分片
    • 若设置了IP数据报首部的“不分片”位(DF位),即不允许被分片,若超过大小,则导致ICMP出错消息
    • 可利用DF位发现路径MTU
  • 最小重组缓冲区大小:IPv4的最小数据报大小(576字节)
  • MSS:TCP最大分节大小
    • 重组缓冲区大小的实际值,避免分片(超过MTU将分片,MSS始终小于MTU)
    • 经常设置成MTU减去IP和TCP首部的固定长度(1500-20-20=1460字节)
  • TCP输出:
    • 某个应用进程调用write:操作系统内核从应用进程的缓冲区中复制所有数据到套接字的发送缓冲区中
    • 可通过SO_SNDBUF(套接字选项)更改TCP套接字发送缓冲区大小
    • 若发送缓冲区无法容纳数据:应用进程将被置于休眠状态,内核将不从write系统调用返回,直到所有数据都复制到发送缓冲区中
    • TCP提取套接字发送缓冲区中的数据,并把它发送给对端TCP
    • 对端TCP必须确认(ACK)收到的数据,本端TCP才能从发送缓冲区中丢弃已确认的数据

九、套接字地址结构

1. IPv4套接字地址结构

  • IPv4套接字地址结构:网际套接字地址结构,以sockaddr_in命名
  • 说明:地址结构大小至少16字节
    • sin_len:长度字段
    • sin_family、sin_addr、sin_port:POSIX规范需要的三个字段
    • IP地址和端口号在套接字地址结构中以网络字节序来存储
    • 结构本身不在主机之间传递

2. 通用套接字地址结构

  • 套接字函数:以指向某个通用套接字地址结构的一个指针作为参数之一
  • 通用套接字地址结构的用途:对指向特定于协议的套接字地址结构的指针执行类型强制转换

3. 套接字地址结构的比较

  • IPv4和Ipv6的套接字地址结构长度是固定的
  • 长度是可变时,当把指向某个套接字地址结构的指针作为一个参数传递给某个套接字函数时,会把该结构的长度作为另一个参数传递给这个函数

十、socket函数

  • 调用socket函数,指定期望的通信协议类型

    • family:协议族(常值)
    • type:套接字类型(常值)
    • protocol:某个协议类型常值或为0
    • 成功时返回:小的非负整数(套接字描述符:sockfd)
  • AF_XXX和PF_XXX对比:address family、protocol family(AF_:地址族、PF_:协议族)

十一、connect函数

  • TCP客户用 connect 函数建立与TCP服务器的连接:

    • sockfd:socket函数返回的套接字描述符
    • 第二、三个参数:指向套接字地址结构的指针、该结构的大小
  • 调用connect函数将激发TCP的三路握手过程,连接建立成功或出错时返回:
    • 出错返回:

      • ETIMEDOUT:TCP客户未收到SYN分节的响应(多次发送后,未收到)
      • ECONNREFUSED(硬错误):服务器对客户SYN的响应是RST(复位),服务器主机在指定端口上没有进程等待连接
      • ICMP错误(软错误):客户发出的SYN在中间的某个路由器引发了目的对不可达的ICMP错误
    • 套接字从CLOSED状态转移到SYN_SENT状态
      • 成功:转移为ESTABLISHED状态
      • 失败:该套接字不可用,必须关闭并重新调用socket

十二、bind函数

  • bind函数把一个本地协议地址赋予一个套接字:

    • sockfd:socket函数返回的套接字描述符
    • 第二、三个参数:指向特定于协议的地址结构的指针、该结构的长度
    • 返回错误:EADDRINUSE(地址已使用)
  • 进程可以把一个特定的IP地址捆绑到它的套接字上
    • TCP客户:为在该套接字上发送的IP数据报指派了源IP地址
    • TCP服务器:限定该套接字只接收那些目的地为这个IP地址的客户连接

十三、listen函数

  • listen函数由TCP服务器调用,在调用socket和bind函数之后,调用accept函数之前
  • listen函数把一个未连接的套接字转换成一个被动套接字(被动打开),指示内核应接受指向该套接字的连接请求(套接字从CLOSED状态转换到LISTEN状态)
  • 第二个参数(backlog):规定了内核应该为相应套接字排队的最大连接个数
  • 内核为监听套接字维护了两个队列:
    • 未完成连接队列:服务器正在等待完成TCP三路握手过程,套接字处于SYN_RCVD状态

      • 当客户的SYN到达时,TCP在未完成连接队列中创建一个新项
      • 响应三路握手的第二个分节(服务器的SYN响应和对客户SYN的ACK),该新项一直保留到客户的ACK到达前,或该项超时
    • 已完成连接队列:已完成三路握手过程,套接字处于ESTABLISHED状态
      • 该项从未完成连接队列移到已完成连接队列的队尾

  • backlog参数:
    • backlog曾经规定了两个队列总和的最大值
    • Berkeley实现给backlog增设一个模糊因子(该因子乘以1.5得倒未处理队列最大长度)
    • 不要把backlog定义为0,(不同实现有不同解释)
    • 设定一个默认值,并允许通过命令行选项或环境变量覆盖这个默认值

十四、accept函数

  • accept函数由TCP服务器调用,用于已完成连接队列队头返回下一个已完成连接,若队列为空,则进程被置于休眠状态
  • 参数:
    • sockfd:监听套接字描述符(由socket创建,随后用作bind和listen的第一个参数的描述符)
    • cliaddr和addrlen(值-结果参数)用来返回已连接的客户协议地址
  • accept返回值
    • 成功:

      • 由内核自动生成的一个全新描述符(已连接套接字描述符),代表与所返回客户的TCP连接
      • 客户进程的协议地址(cliaddr)以及该地址的大小(addrlen)(可将两个设置为空指针,不返回协议地址)
    • 失败:出错指示的整数

十五、fork和exec函数

  • fork(派生)函数:

    • 调用一次,返回两次:

      • 调用进程(父进程)中,返回新派生进程(子进程)的进程ID号
      • 子进程中返回0
        • 子进程只有一个父进程,父进程可以有很多子进程
        • 通过子进程调用getppid可以获得父进程ID
        • 无法通过父进程获得子进程ID
      • 调用accept后调用fork:已连接套接字在父进程和子进程之间共享
      • 用法:
        • 一个进程创建自身的副本(子进程):每个副本可以在另一个副本执行其他任务的同时处理各自的操作(网络服务器)
        • 一个进程想要执行另一个程序:进程创建自身的副本后,其中一个副本调用exec把自身替换成新的程序(shell程序)
  • exec函数(6个):把当前进程映像(副本)替换成新的程序(进程ID不变)
    • 调用进程:调用exec的进程
    • 新程序:新执行的程序

十六、并发服务器

  • 并发服务器:同时服务多个客户
  • 编写方法:fork一个子进程来服务客户(通过已连接套接字描述符:connfd),父进程等待新客户连接(通过监听套接字描述符:listenfd)
  • 父进程对connfd调用close后,不会终止服务器与客户的连接(引用计数:2变为1,不等于0)

十七、close函数

  • 关闭套接字,终止TCP连接,调用后发送一个FIN
  • 并发服务器中:父进程close导致相应描述符的引用计数值减一,直到子进程close将计数值减为零时,才断开TCP连接

十八、I/O模型

  • I/O复用:

    • 进程预先告知内核的能力(使内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程)
    • 由select和poll两个函数支持
  • I/O复用使用场景:
    • 客户处理多个描述符(交互式输入和网络套接字)
    • 一个客户同时处理多个套接字
    • 一个TCP服务器既处理监听套接字,又处理已连接套接字
    • 一个服务器既处理TCP,又处理UDP
    • 一个服务器处理多个服务或多个协议
  • I/O模型
    • 五种IO模型:阻塞式IO、非阻塞式IO、IO复用(select和poll)、信号驱动式IO、异步IO
    • 一个输入操作包括两个阶段:
      • 等待数据准备好(等待数据从网络中到达)
      • 从内核向进程复制数据(把数据从内核缓冲区复制到应用进程缓冲区)

1. 阻塞式I/O模型

  • BIO:Blocking I/O(阻塞IO)
  • 进程在从调用recvfrom(系统调用)开始到返回的整段时间内是被阻塞的

2. 非阻塞式I/O模型

  • 进程把一个套接字设置成非阻塞,是在通知内核:当所请求的I/O操作非得把本进程置于休眠状态才能完成时,拒绝把进程置于休眠,而是返回一个错误
  • 通过轮询(poll)内核,循环调用recvfrom来完成非阻塞IO(耗费大量CPU时间)

3. I/O复用模型

  • 可以调用select或poll,阻塞在这两个系统调用中的某一个上,而不是阻塞在真正的I/O系统调用上
  • 阻塞于select调用,等待数据报套接字变为可读(select返回套接字可读时,再调用recvfrom复制数据)

4. 信号驱动式I/O模型

  • 使用信号,让内核在描述符就绪时,发送SIGIO信号通知
  • 需要先开启套接字的信号驱动式I/O功能,再通过sigaction系统调用安装一个信号处理函数(调用将立即返回),进程此时继续执行(非阻塞)

5. 异步I/O模型

  • AIO(asynchronous IO):告知内核启动某个操作,并让内核在整个操作完成后通知我们
  • 调用aio_read函数,告诉内核当整个操作完成时如何通知我们,该系统调用立即返回,且在等待IO完成期间,进程不被阻塞(下图:要求内核完成操作时产生某个信号)

6. 各种I/O模型的比较

  • 前四种模型主要区别在第一阶段,第二阶段都是一样的(数据从内核复制到调用者的缓冲区期间,进程阻塞于recvfrom调用:同步I/O)
  • 同步IO(前四种IO):导致请求进程阻塞,直到I/O操作完成
  • 异步IO:请求进程不会被阻塞

十九、select函数

  • 允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或经历一段指定的时间后才唤醒它
  • 例:调用select告知内核对哪些描述符的哪些事件感兴趣以及等待多长时间
    • {1,4,5}中的任何描述符准备好读
    • {2,7}中的任何描述符准备好写
    • {1,4}中的任何描述符有异常条件待处理
    • 经历10.2秒
  • 参数:
    • timeout:告知内核等待所指定描述符中的任何一个就绪可花多长时间

      • 永远等待下去:设置为空指针
      • 等待一段固定时间:设置秒数和微秒数
      • 根本不等待(轮询:poll):检查后立即返回,设置为0
    • 中间三个参数readset、writeset、exceptset:指定要让内核测试读、写和异常条件的描述符(使用描述符集fd_set,整数数组)
    • maxfdp1:指定待测试的描述符的个数(最大描述符+1:即 0,1,2,…,maxfdp1-1)
  • 描述符就绪条件:
    • 套接字准备好读:读操作不会阻塞

      • 该套接字接收缓冲区中的数据字节数大于等于套接字接收缓冲区低水位标记的当前大小
      • 该连接的读半部关闭(接收了FIN的TCP连接)
      • 该套接字是一个监听套接字且已完成的连接数不为0
      • 其上有一个套接字错误待处理,读操作返回错误(待处理错误)
    • 套接字准备好写:
      • 该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓冲区低水位标记的当前大小
      • 该连接的写半部关闭
      • 使用非阻塞式connect的套接字已建立连接(或者connect失败了)
      • 其上有一个套接字错误待处理
    • 套接字有异常条件待处理:该套接字存在带外数据或者仍处于带外标记
  • 低水位标记设置为64:至少存在64字节的数据,否则应用进程不工作(防止少于64字节的数据准备好读时select唤醒进程)
  • select的最大描述符数

二十、poll 函数

  • 与select类似,不过在处理流设备时,能够提供额外的信息
  • poll识别三类数据:流的实现
    • 普通:所有正规TCP数据和UDP数据、读半部关闭时、监听套接字上有新的连接可用
    • 优先级带:TCP的带外数据
    • 高优先级
  • 参数:
    • fdarray:指向一个结构数组第一个元素的指针(每个数组元素都是一个pollfd结构:用于指定测试某个给定描述符fd的条件)

      • evenets:测试条件
      • revents:返回该描述符的状态
    • nfds:结构数组中元素的个数
    • timeout:指定poll函数返回前等待多长时间
      • INFTIM(负值):永远等待
      • 0:立即返回,不阻塞进程
      • 大于0:等待指定数目的毫秒数
  • 返回:
    • 就绪描述符的个数(revents成员值非0的描述符个数)
    • 0:定时器到时之前没有任何描述符就绪
    • -1:发生错误

深入学习理解UNIX网络编程相关推荐

  1. 《Unix网络编程(第3版)》代码编译的一些问题

    现在学习<UNIX网络编程(第3版)>一书,书中源代码有一些默认情况下编译不能通过,要经过一些修改都行.这编文档将记录下我遇到的不能正常编译的程序的修改步骤. 28章:traceroute ...

  2. 再读Socket编程——《UNIX网络编程(卷一)》学习点滴

    原先曾以Socket编程为入口开始自己的新的学习,毕竟未曾致用,时至今日已比较生疏.借着阅读<UNIX网络编程(卷一)>(简称UNPv1)的机会,正好复习一番,而且希望将新的感受记录下来. ...

  3. UNIX网络编程学习笔记(代码超详细解析)(持续更新)

    1. 其他函数准备 1. TCP 回射服务器程序: str_echo 函数 #include "unp.h"void str_echo(int sockfd) {ssize_t n ...

  4. 《UNIX网络编程 卷1:套接字联网API》学习笔记——基本TCP套接字编程

    UNIX网络编程--基本TCP套接字编程 socket 函数 connect 函数 bind 函数 listen 函数 accept 函数 fork 和 exec 函数 并发服务器 close 函数 ...

  5. Unix网络编程学习日记

    今天开始拜读<Unix网络编程>.找到的源代码在Linux下有各种问题,最后决定还是自己从头写比较好. 从第一个时间服务程序开始学习.今天先看一下主要的头文件的作用. 在common.h中 ...

  6. Unix网络编程 学习记录01

    写在前面 秋招面试微信折戟,决定好好折腾一下c++和网络,武装一下自己的技术栈,先从最基本的网咯编程开始.<UNIX网络编程 卷1>有不少实践的机会,于是先从这本书下手. 能坚持多久是多久 ...

  7. Unix网络编程 chart

    前言 在最初接触网络这一领域的时候,就是傻傻地抱着一本TCP/IP协议详解来学习,主要学习协议的原理并研究协议相关的算法,大家都知道协议纯理论的学习是比较枯燥和复杂的,看着看着就睡着了.由于项目需要, ...

  8. UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订)...

    UNIX网络编程.卷1,套接字联网API(第3版)(中文版)(Stevens经典著作,两位顶级网络编程专家应邀执笔修订) 基本信息 原书名: Unix Network Programming, Vol ...

  9. unix网络编程源代码编译

    最近开始研究unix网络编程,正所谓"学习网络编程的最好方法就是下载这些程序,对其进行修改和改进.只有这样才能深入理解与有关概念和方法". 1.首先下载源代码,不多说了: 2.照着 ...

  10. UNIX网络编程之旅-配置unp.h头文件环境

    最近在学习Unix网络编程(UNP),书中steven在处理网络编程时只用了一个#include "unp.h"  相当有个性并且也很便捷 于是我把第三版的源代码编译实现了这个过程 ...

最新文章

  1. Android logcat命令详解
  2. 学习OpenCV——SVM
  3. R语言-RStudio打开中文注释的脚本后出现乱码
  4. VPTR的分步初始化
  5. HBase停止集群报错,pid: No such file or directory
  6. RN通信机制和渲染流程
  7. 1134 Vertex Cover
  8. eclipse 环境安装
  9. 【PSFTP】Windows从Linux获取文件或目录
  10. systrace简介
  11. 三星识别文字_免费文字识别
  12. 阿里云封禁端口25,导致smtp.126.com邮件无法发送-解决方案+springboot配置
  13. After 500:写500篇博客其实和写一篇是一样的
  14. Hive总结及常见语法
  15. 手机上怎么照证件照照片?教你两招轻松拍出证件照
  16. Java中mongodb指定DB通过aggregate聚合查询操作示例
  17. 阿里云服务器调用微信支付接口慢的解决方案 (api.mch.weixin.qq.com)
  18. 关于 麒麟系统启动应用报错“undefined symbol: __cxa_throw_bad_array_new_length, version Qt_5“ 的解决方法
  19. Python的静态成员变量和非静态成员变量
  20. iOS中 WGAFN_网络监控 技术分享

热门文章

  1. 在线摩尔斯密码加密解密工具
  2. 做完c语言通讯录系统后的小结,c语言通讯录管理系统的总结
  3. html文件导入奥维,【干货】奥维地图 | 如何导入高程数据
  4. 执行安装操作的时候,出现丢失MSVCR120.dll的解决方法
  5. WinHex18.4算法分析
  6. 小规模票表比对不通过_实用 | 增值税纳税申报比对失败怎么办?操作指南来啦...
  7. opencv实现超像素分割(slic实现)
  8. UWB源码资料 研创物联源码资料 可二次开发 dwm1000模块 双边双向测距
  9. 微带线特性阻抗计算公式_传输线特性阻抗计算方式
  10. 十天征服单片机百度云_51单片机 郭天祥十天学会单片机教学视频