Notes on UNPv1 Ch.7

  1. 对于getsockoptsetsockopt, 它们的第一个参数sockfd必须是一个打开的socket描述符. (Page.192)

  2. socket的选项分为两种基本类型: 一种是标记型(只有启用和禁用), 一种是数值型(指定选项的参数). (Page.192)

  3. 某些系统可能会定义一些socket选项, 但却没有实现这些选项, 如果使用这些选项会触发ENOPROTOOPT错误(具体那些选项没有实现根据系统而定). (Page.197)

  4. 某些选项的设置或获取需要考虑到时机, 例如TCP的链接socket只有在完成3次握手之后才会获得, 而某些选项必须在建立连接前确定(SO_RCVBUF影响窗口扩大选项, TCP_MAXSEG影响对方MSS). 因此连接socket的部分选项是由监听socket继承而来, 包括SO_DEBUG, SO_DONTROUTE, SO_KEEPALIVE, SO_LINGER, SO_OOBINLINE, SO_RCVBUF, SO_RCVLOWAT, SO_SNDBUF, SO_SNDLOWAT, TCP_MAXSEG, TCP_NODELAY. 如果想要对连接socket设置某些选项, 应该先设置对应监听socket的该选项(针对server, client可以直接调用socket函数获得socket描述符). (Page.198)

  5. 如果终点地址为广播地址, 而又没有设置SO_BROADCAST选项, 会引发EACCESS错误. (Page.199)

  6. SO_DEBUG选项只对TCP有效, 开启这个选项后, 内核会记录在这个socketTCP通信的一些细节, 并将其存放在一个环形内存中, 这个内存可以通过trpt程序查看. (Page.199)

  7. 由于socket的通信与程序本身的运行不是同步的, 所以如果socket上发生了错误, 内核协议模块会把错误的错误码存放在socketso_error变量中, 等到适合的时机返回给程序. 这种错误叫做pending error(待定错误). (Page.199)

  8. 想要在socket发生错误时马上得知, 可以有两种方法, 一种是利用I/O多路复用阻塞进程, socket发生错误的时候就会马上返回; 一种是利用信号驱动的I/O, 在错误发生的时候收到SIGIO作为通知. (Page.200)

  9. 如果一个socket发生错误, a.可以通过SO_ERROR选项获取socket的错误; b. 进行read操作时, 在读取完内存中的数据后, read会返回对应的错误码; c. 进行write操作时, 会返回对应的错误码. 无论以哪种方式获得错误码, socket的变量so_error在此之后都会被重置. (Page.200)

  10. SO_ERROR只能获取不能设置. (Page.200)

  11. SO_KEEPALIVE选项是用以在一个链接长时间没有通信时探询对方是否仍然”存活”, 具体方法是在指定时间没有通信后发送一个特殊报文(没有数据, 序列号为对方确认号减1)给对方并等待确认. SO_KEEPALIVE的发送间隔是系统级的, 一旦设置, 整个系统的所有TCP链接都会受到影响. (Page.201)

  12. 在默认的情况下, 在系统调用close, close函数会立刻返回, 而在缓存中的数据会由系统发送出去, 但是程序不会知道这些数据是否被成功发送. (Page.202)

  13. 可以通过设置SO_LINGER选项改变程序在调用close函数后的行为. SO_LINGER被打开(l_onoff=1), 如果l_linger不为0, 那么在调用close函数后, 如果socket描述符没有设置非阻塞, 那么close会阻塞l_linger设定的时间, 在这段时间内系统会尝试把数据发送出去; 而如果l_linger0, 那么在调用close函数后, 无论缓存中还有无数据要发送, 系统会马上抛弃这些数据, 并且发送RST给对方(可以理解为即使是FIN这样的也是要发送的数据之一, 所以l_linger0时无论如何都会发送RST). (Page.202)

  14. 如果打开了SO_LINGER, 并且l_linger不为0, 如果socket描述符设置了非阻塞, 那么close就不会阻塞, 立刻返回, 但系统仍然会在指定的时间内尝试发送数据; 而如果socket没有设置非阻塞, 那么如果close会阻塞到数据全部发送完或者发送等待超时后返回, 并且超时的返回是EWOULDBLOCK. (Page.203)

  15. 如果SO_LINGER选项的l_linger足够大, 那么close返回成功的时机是在自己发送的FIN被确认之后, 而不是等到自己发送了最后的ACK进入TIME_WAIT状态才返回. (Page.204)

  16. 在没有设置SO_LINGER情况下的close返回, 我们只能知道我们把数据发送了, 但是无法知道对方是否已经收到了这些数据; 如果设置了SO_LINGER, 如果在超时前返回, 那么可以知道我们发送的数据已经被对方的协议层(TCP)接受, 但是无发确定对方的应用层是否已经接收了数据. 如果想要了解对方的应用是否已经接收数据, SO_LINGER+close是不够的. 如果已经没有数据要发给对方, 而又想知道对方应用是否接收到数据, 正确的方法是使用半关闭(half-close)函数shutdown+SHUT_WR, 或者使用应用级别的ACK. (Page.205)

  17. 设置了SO_LINGER的非阻塞socket描述符只有在引用计数器到达0时才会真正被close, 所以linger的阻塞也只会发送在最后的close. (Page.207)

  18. 由于TCP有窗口扩大协议, 所以接受接收缓存(SO_RCVBUF)选项必须在链接建立前设定(如果需要改变的话). 对于server, 应在listen前设定, 对于client, 应在connect之前设定. (Page.208)

  19. 对于TCP, 接受缓存的大小应设为MSS4, 否则的话会影响到快重传算法的工作(3个乱序包和一个空位留给正确的未到达的顺序包). (Page.208)

  20. 设置SO_RCVBUF, 应注意把值设为MSS的整数倍, 一是为了充分利用空间(TCPMSS为发送单位, 发送不到MSS的数据往往会有限制如nagle算法等); 二是某些系统在设置了不是MSS整数倍的SO_RCVBUF, 实际分配的空间会向上调整为MSS的整数倍, 而调整多出来的那部分空间却又不能使用. (Page.208)

  21. 根据不同的情况, 发送方的发送缓存, 接收方的接收缓存, 都不小于链路的带宽延时积时, 才能充分利用链路的性能. (Page.209)

  22. 尝试对系统的缓存调整前, 最好能先确定系统的默认值, 避免本来想要改大, 可是修改的值还没有系统原来的值大. (Page.209)

  23. TCPSO_SNDLOWAT选项是指发送缓存有该选项指定大小以上的空间时, 对该socket的写操作才不会阻塞, 默认值为2048; TCPSO_RCVLOWAT选项是指接受缓存中有该选项指定大小以上的数据时, 对该socket的读操作才不会阻塞. (Page.209)

  24. UDPSO_SNDLOWAT只是一个大小限制, 因为UDP并没有真正的发送缓存, 只要SO_SNDLOWAT指定的值不大于SO_SNDBUF, 那么UDP的写操作一直不会阻塞; SO_RCVLOWATTCP的大概一样. (Page.209)

  25. 可以通过SO_RCVTIMEOSO_SNDTIMEO设定TCP的写超时和读超时, 设定的参数为timeval结构体, 精度为微秒. timeval的成员值设为0即可关闭超时选项. 这两个选项默认时都是关闭的. (Page.210)

  26. SO_REUSEADDR主要有以下四个作用:

    1. 可以重用相同的IP和端口号, 只要重用和远端的ip和端口号确定链接不同, 例如前一个ip:port确定的连接处于ESTABLISHEDTIME_WAIT状态, 那么第二个连接在声明了SO_RESUEADDR后可以再次绑定这个IP:prot用以listen, connect以建立另一个不同的连接. (Page.210)

    2. 可以用来同时绑定相同port下的通配ip地址和普通ip地址. 某些系统会由于安全原因只允许通配ip地址在最后绑定, 如果绑定了通配ip地址, 那么就不能再绑定普通ip地址了. 对于linux来说, 相同port, 不能同时绑定普通ip和通配ip

    3. 可以用来同时绑定相同端口下的不同的普通ip, linux不用声明这个SO_REUSEADDR即可绑定相同端口下的普通ip.

    4. 用以给UDP绑定相同的IP:PORT(多用于多播系统). 在这种情况下, 如果收到一个数据的目的地址是多播或者广播, 那么绑定这个ip:port的所有socket都会收到这个数据, 而如果这个数据的目的地址是单播, 那么只有一个socket会收到这个数据, 具体是哪一个更具系统实现决定. (Page.210-212)

  27. 某些系统为了细化SO_REUSEADDR, 特地把条目26中的功能4细化出来独立成一个SO_REUSEPORT, 但不是每个系统都有这个选项, 例如linux. (Page.212)

  28. 总结来说, 在编写TCP server程序时, 需要先行设定SO_REUSEADDR选项以便绑定端口(可以重用某些处于连接的ip:port); 而在编写多播程序时, 也应设定SO_REUSEADDR(SO_REUSEPORT)以便多个多播程序绑定同一个多播地址. (Page.212)

  29. socket API不提供在握手阶段就拒绝链接的方法. (Page.213)

  30. IP_RECVDSTADDRIP_RECVIF选项分别用来获取数据包的目标ip地址和接受到数据包的接口index, 他们都利用recvmsg中返回的附属数据作为载体. (Page.214)

  31. IP_TTL只能设置单播数据包的TTL, 而多播数据的TTL需要用IP_MULTICAST_TTL来设置. 没有任何办法获得接收到的包的TTL. (Page.215)

  32. UDP没有选项字段, 所以在通过IP层和通用socket选项来配置和获取信息; TCP有自己的选项, 可以通过setsockoptgetsockopt进行配置. (Page.218)

  33. 可以通过TCP_MAXSEG设置和获取MSS, 也就是自己能够发给对方的数据的最大长度. 通常这个选项的设置是有限制的: 只能减小不能增大. (Page.219)

  34. 如果TCP使用了某些选项(如时间戳选项), 那么会导致实际能够发送的最大数据量比MSS, 因为选项增加了头部的长度. (Page.219)

  35. 如果在建立连接前就去获取MSS的值, 返回值会是如果对方的SYN中不带MSS协议时的默认值. (Page.219)

  36. TCP_NODELAY用以关闭Nagle算法, 默认的时候这Nagle算法是会被启用的. 详细的Nagle可以参考TCP/IP详解的19. (Page.220)

  37. SIGIO(信号驱动I/O)SIGURG(带外数据)需要设定socket的拥有者才能接收到这两个信号. Socket的拥有者可以是一个进程, 或者是一个进程组. 如果拥有者是一个进程, 那么信号就只有这个进程可以收到, 如果是一个进程组, 那么进程组中的所有进程都能收到信号.

  38. 在一个sokcet刚创建的时候, 没有拥有者. 可以通过setsockopt设置socket的拥有者, 参数为正时设置拥有者为单个进程, 而参数为负时设置拥有者为整个进程组. socket的拥有者是可以继承的(通过accept获得链接socket). (Page.235)

  39. 可以使用fcntl函数设置非阻塞I/O, 异步I/O, 但是要注意的是这两个其实是文件描述符的属性, 不限于socket, 并且其设置是通过位操作来实现, 记得要先保存原状态然后用或操作进行设置, 取消反之. 而不是像setsockopt设置某些选项那样用0/1设置. (Page.235)

你可能感兴趣的:(Note)