TCP_IP编程读书笔记

TCP/IP 编程读书笔记


  1. **TCP/IPd定时器种类、个数
    4种定时器、7个
    重传计时器:Retransmission Timer
    坚持计时器:Persistent Timer
    保活计时器:Keeplive Timer
    时间等待计时器:Time_Wait Timer
  2. TCP/IP是一种流协议

    意味着数据是以字节流的形式传递给接收者,没有固定的“报文”或者“报文边界”。
    Send函数只是将数据复制到发送端的协议栈中,然后就返回。至于要发送多少数据由TCP决定(前提是tcpsocket)。
    尽管数据是以IP分组的形式传输的,但是分组中的数据量与send调用中传递的TCP多少数据并没有直接关系。而且程序中
    没有什么可靠的方法是可以判断数据是如何分组的,因为在连词recv调用之间可能会有多个分组到来。TCP会记录它发送
    多少字节以及确认的字节,但它不会记录这些字节是如何分组的。实际上分组重传丢失的分组的时候传送的数据可能比原来
    的多一些或者少一些。

    1
    主机A---M2-----M1-----主机B

    在接收端,对于调用recv,都不会对tcp发送给他的数量做任何假设。对于接收端读取第一条报文可能有四种结果。

    1. 没有数据可读,应用程序阻塞(没有设置非阻塞标识位)或者返回错误说明没有数据可读(设置非阻塞位)
    2. 应用程序只读取了M1中部分报文而不是全部
    3. 应用程序获取了报文M1中所有的数据,除此之外没有任何其他内容
    4. 应用程序获取报文M1的所有数据,以及报文M2的部分或者全部数据
  3. 理解TCP写操作

    1. 从应用程序的角度看写操作(写操作返回错误)
      1. 套接字描述符无效
      2. 使用Send及其兄弟函数时文件描述符指向的不是套接字
      3. 调用中制定的套接字不存在或者未连接
      4. 缓冲地址参数指向无效地址
    2. 从TCP角度看写操作

      写操作负责将数据从应用程序的写缓冲区搬移到内核中去,并通知TCP有来自应用程序的新数据需要处理。

      1. 第一种流量控制算法被称为慢启动
        “慢慢的”将向网络发送数据的速率增加到一个门限值。
      2. TCP输出例程,在符合下列某一项数据就会被传送出去:
        1. 可以发送一个完整的MSS尺寸的段
        2. 连接空闲,并且可以清空发送缓冲区
        3. Nagle 算法被禁止,并且可以清空发送缓冲区
        4. 有紧急数据需要发送
        5. 有一小段“暂时”无法发送的数据
        6. 对等实体的接受窗口至少是半开的
        7. 需要重传一个段
        8. 需要为来自对等实体的数据传回一个ACK
        9. 需要发布一次窗口更新
  4. 理解TCP的有序释放操作

    1. 连接建立阶段
    2. 数据传输阶段
    3. 连接拆除阶段

    链接的一端完成了数据的发送,并将此事通知对等实体,但同时仍然在接收数据。因为
    TCP连接是全双工的,而且一个方向的数据流与另一个方向的数据流是相互独立的,所以可能会出现这种情况。

  5. shutdown调用有乾坤

    1
    int shutdown(int s,int how)
      how| 动作|
    ---|----|
    SHUT_RD(0)|关闭连接的接收端|
    SHUT_WT(1)|关闭连接的发送端|
    SHUT_RDWR(2)|两端都关闭|
    

    shutdown(s,2) 等效于close(s)

    有序释放的目的是确保两端都能在连接拆除之前收到所有来自其对等实体的数据。

  6. 文件描述符就绪的条件

    1. socket可读
      1. socket 内核接收缓存区中的字节数大于或者等于其低水位标记SO_RCVLOWAT。此时我们可以无阻塞的读该socket,并且读取操作返回的字节大于0
      2. socket通信的对方关闭连接。此时对socket的读操作将返回0.
      3. 监听socket上有新的连接请求。
      4. socket 上有未处理的错误,此时我们可以使用getsockopt来读取和清除该错误。
    2. socket可写

      1. socket 内核发送缓存区中的可用字节数大于或者等于其低水位标记SO_SNDLOWAT。此时我们可以无阻塞的写该socket,并且写操作返回的字节数大于0.
      2. socket的写操作被关闭,对写操作被关闭的socket执行写操作将处罚一个SIGPIPE信号
      3. socket使用非阻塞connect 连接成功或者失败(超时)之后
      4. socket上有未处理的错误,此时我们可以使用getsockopt来读取和清除该错误

      注意:网络程序中select能处理的异常情况只有一种:socket接收带外数据。(MSG_OOB)

  7. accept 发生在哪个阶段
    首先tcp有两个队列:syn和accept队列,完成三次握手之后将会从syn队列移到accept队列,如果发现accpet队列已经满,则根据配置决定是否要应答RST报文。

    Because of the 3-way handshake used by TCP, an incoming connection goes through an intermediate state SYN RECEIVED before it reaches the ESTABLISHED state and can be returned by the accept syscall to the application (see the part of the TCP state diagram reproduced above). This means that a TCP/IP stack has two options to implement the backlog queue for a socket in LISTEN state:

    1. The implementation uses a single queue, the size of which is determined by the backlog argument of the listen syscall. When a SYN packet is received, it sends back a SYN/ACK packet and adds the connection to the queue. When the corresponding ACK is received, the connection changes its state to ESTABLISHED and becomes eligible for handover to the application. This means that the queue can contain connections in two different state: SYN RECEIVED and ESTABLISHED. Only connections in the latter state can be returned to the application by the accept syscall.
    2. The implementation uses two queues, a SYN queue (or incomplete connection queue) and an accept queue (or complete connection queue). Connections in state SYN RECEIVED are added to the SYN queue and later moved to the accept queue when their state changes to ESTABLISHED, i.e. when the ACK packet in the 3-way handshake is received. As the name implies, the accept call is then implemented simply to consume connections from the accept queue. In this case, the backlog argument of the listen syscall determines the size of the accept queue.
  1. Epoll LT 、ET模式注意
    • LT工作模式的文件描述符,当epoll_wait 检测到其上有事件发生并且将此时间同时应用程序后,应用程序可以不立即处理该时间,这样当应用程序下次调用epoll_wait时,还会再次向应用程序通告此事件,直到改时间被处理。
    • ET工作木事的文件描述符,当epoll_wait检测到其上有时间发生并且此事件通知应用程序之后,应用程序必须立即处理该事件,因为后续的epoll_wait调用将不再向应用程序通知这一事件
  1. Epoll EPOLLONESHOT事件
    即使我们采用了ET模式,一个soket上的某个事件还是可能被触发多次,这在冰法程序中就会引发一个问题,比如一个线程或者进程在读取完某个socket上的数据后开始处理这些数据,在数据的处理过程中该socket上又有新数据可读,此时另外一个线程被唤醒来读取这些新的数据。于是就出现了两个线程同时操作一个socket的局面。我们希望的是一个socke连接在任一时刻都只能被一个线程处理。对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上的一个可读、可写或者异常事件,且只触发一次,除非我们使用epoll_ctl函数重置该文件描述符上注册的EPOLLONESHOT事件。反过来,注册了EPOLLONESHOT事件的socket一旦被某个线程处理完毕,该线程就应该立即重置这个socket上的EPOLLONESHOT事件,以确保这个socket下次可读时,其EPOLLIN事件能被触发,进而让其他工作线程有机会继续处理这个socket。

  2. I/O复用区别

    系统调用|select | poll| epoll|
    ——-|——-|—–|——|
    事件集合|用户通过3个参数分别传入感兴趣的可读可写及异常等事件,内核通过对这些参数的在线修改反馈其中的就绪事件这使得用户这次调用select都要重置这三个参数|统一处理所有事件类型因此秩序一个事件集参数。用户通过poll.events传入感兴趣的事件,内核通过修改pollfd.revents反馈其中就绪的事件|内核通过一个事件表直接管理用户感兴趣的所有事件因此每次调用epoll_wait时无须反复传入用户感兴趣的事件,epoll_wait系统调用的参数events仅用来反馈就绪的事件。|
    应用程序索引就绪文件描述符的事件复杂度|O(n)|O(n)|O(1)|
    最大支持文件描述符数|一般由最大值限制|65535|65535|
    工作模式|LT|LT|LT\ET|
    内核实现和工作效率|采用轮询方式来检测就绪事件,算法事件复杂度为O(n)|采用轮询方式来检测就绪事件,算法事件复杂度为O(n)|采用回调方式来检测就绪事件,算法事件复杂度为O(1)|

坚持原创技术分享,您的支持奖鼓励我继续创作!