文章目录

  • 网络协议
    • RFC 相关文档
    • 网络 ip 层
      • ip 头部
    • 传输层
      • tcp
        • 相关概念
          • MSL是Maximum Segment Lifetime,“报文最大生存时间”
          • MSS:Maximum Segment Size
          • RTT:Round Trip Time
          • RTO:Retransmission TimeOut
        • /proc/sys/net/ipv4/
        • tcp 头部
        • tcp 状态转换图
        • tcp 定时器
        • 流量控制和滑动窗口
        • 拥塞控制和拥塞窗口
        • tcp 延时 ACK
        • Time-wait状态(2MSL)
          • 1. 为什么需要TIME_WAIT状态
  • linux网络编程
    • 相关概念
    • 套接字地址结构
      • struct in_addr
      • struct sockaddr和struct sockaddr_in
    • 字节排序函数
    • 地址转换函数:
      • 1. in_addr_t inet_addr(const char *cp)函数转换标准的ASCII以点分十进制的地址值返回为网络字节序二进制值
      • 2. int inet_aton(const char *__cp, in_addr *__inp)转换标准的ASCII以点分十进制的地址值返回网络字节序二进制值
      • 3. char *inet_ntoa (struct in_addr __in)函数转换网络字节序二进制值返回标准的ASCII以点分十进制的地址值
      • 4. 代码示例:
      • 1. int inet_pton(int domain, const char *str, void *addr)将标准的ASCII以点分十进制的地址值转化为网络传输的二进制数值格式
      • 2. const char *inet_ntop(int domain, const void *addr, char *str, socklen_t size)将网络传输的二进制数值转化标准的ASCII以点分十进制的地址值格式
      • 3. 代码示例:
    • TCP通信相关函数
      • 1. int socket(family, type, protocol):创建套接字
      • 2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen):绑定套接字
      • 3. int listen(int fd,int backlog):设置同时通信的最大套接字数量
      • 4. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)阻塞式监听客户端连接
      • 5. int connect(int sockfd, const strcut sockaddr *addr, socklen_t addrlen)用来客户端和tcp服务器建立连接
      • 5. ssize_t read(int fd,void *ptr,size_t nbytes)一次读取指定字节长度数据
      • 6. ssize_t write(int fd,const void *ptr,size_t nbytes)一次写入指定字节长度数据
      • 7. ssize_t Readn(int fd,void *vptr,size_t n);循环读取n个字节数据
      • 8. ssize_t Writen(int fd,const void*vptr,size_t n)循环写入n个字节数据
      • 9. ssize_t Readline(int fd, void *vptr, size_t maxlen) 读到'\n'或者读满缓冲区才返回
      • 10. int close(int fd) 关闭套接字
      • 11. shutdown()函数切断进程共享的套接字的所有连接
      • 12. recv()和send()函数
    • UDP通信相关函数
      • 1. recvfrom()函数
      • 2. sendto()函数
    • UDP组播通信相关函数
    • 常见的错误码
  • 参考文献

网络协议

RFC 相关文档

  • RFC官网
  • RFC 791:INTERNET PROTOCOL
  • RFC 793:Transmission Control Protocol

网络 ip 层

ip 头部

ip消息头可分为 20 个字节的固定头部和最多40字节可扩展头:

    0                   1                   2                   3   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|Version|  IHL  |Type of Service|          Total Length         |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|         Identification        |Flags|      Fragment Offset    |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|  Time to Live |    Protocol   |         Header Checksum       |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                       Source Address                          |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                    Destination Address                        |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                    Options                    |    Padding    |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+Example Internet Datagram Header
类型 长度 描述
Version 4bit 值为4时代表IPV4;值为6时代表IPV6
IHL 4bit ip消息头可分为20个字节的固定头部40字节可扩展头
Type of Service 8bit 服务类型,只有在有QoS差分服务要求时这个字段才起作用
Total Length 16bit 代表总长度,整个IP数据报的长度,包括首部和数据之和,单位为字节,最长65535,总长度必须不超过最大传输单元MTU
Identification 16bit 标识,主机每发一个报文值会加1,分片重组时会用到该字段
Flags 3bit 分片重装时使用:第一位,为0,第二位,DF(Don’t Fragment),能否分片位,0表示可以分片,1表示不能分片;第三位MF(More Fragment),表示是否该报文为最后一片,0表示最后一片,1代表后面还有
Fragment Offset 13bit 片偏移:分片重组时会用到该字段。表示较长的分组在分片后,某片在原分组中的相对位置
Time to Live 8bit 生存时间可经过的最多路由数,即数据包在网络中可通过的路由器数的最大值
Protocol 8bit 标识下一层协议
Header Checksum 16bit 首部校验和,只检验数据包的首部,不检验数据部分
Source Address 32bi 源IP地址
Destination Address 32bit 目的IP地址。
Options 长度可变 选项字段,用来支持排错,测量以及安全等措施。
Padding 长度可变 填充字段,全为0

传输层

tcp

相关概念
MSL是Maximum Segment Lifetime,“报文最大生存时间”

它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。

因为TCP报文(segment)是IP数据报(datagram)的数据部分,而IP头中有一个TTL域,TTL是time to live的缩写,中文可以译为“生存时间”,这个生存时间是由源主机设置初始值但不是存的具体时间,而是存储了一个IP数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减1,当此值为0则数据报将被丢弃,同时发送ICMP报文通知源主机。

RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等
2MSL即两倍的MSL,TCP的TIME_WAIT状态也称为2MSL等待状态,当TCP的一端发起主动关闭,在发出最后一个ACK包后,即第3次握手完成后发送了第四次握手的ACK包后就进入了TIME_WAIT状态,必须在此状态上停留两倍的MSL时间。

等待2MSL时间主要目的是怕最后一个ACK包对方没收到,那么对方在超时后将重发第三次握手的FIN包,主动关闭端接到重发的FIN包后可以再发一个ACK应答包。
在TIME_WAIT状态时两端的端口不能使用,要等到2MSL时间结束才可继续使用。
当连接处于2MSL等待阶段时任何迟到的报文段都将被丢弃。不过在实际应用中可以通过设置SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。

MSS:Maximum Segment Size

对于IPv4,为了避免IP分片,主机一般默认MSS为536字节 (576IP最大字节数-20字节TCP协议头-20字节IP协议头=536字节)。同理,IPv6的主机默认MSS为1220字节(1280IP最大字节数-20字节TCP协议头-40字节IP协议头=1220字节)。

当发送方主机想要调整MSS时,应注意以下几点:

  • MSS不包含TCP及IP的协议头长度。
  • MSS选项只能在初始化连接请求(SYN=1)使用。
  • 发送方与接收方的MSS不一定相等

最大报文段长度(MSS)与最大传输单元(Maximum Transmission Unit, MTU)均是协议用来定义最大长度的。不同的是,MTU应用于OSI模型的第二层数据链接层,并无具体针对的协议。MTU限制了数据链接层上可以传输的数据包的大小,也因此限制了上层(网络层)的数据包大小。例如,如果已知某局域网的MTU为1500字节,则在网络层的因特网协议(Internet Protocol, IP)里,最大的数据包大小为1500字节(包含IP协议头)。MSS针对的是OSI模型里第四层传输层的TCP协议。因为MSS应用的协议在数据链接层的上层,MSS会受到MTU的限制

RTT:Round Trip Time

发送一个数据包收到对应的ACK,所花费的时间

RTO:Retransmission TimeOut

重传时间间隔

/proc/sys/net/ipv4/

参考:/proc/sys/net/ipv4/下网络参数的理解以及sysctl命令修改内核参数

tcp 头部
TCP Header Format0                   1                   2                   3   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|          Source Port          |       Destination Port        |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                        Sequence Number                        |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                    Acknowledgment Number                      |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|  Data |           |U|A|P|R|S|F|                               || Offset| Reserved  |R|C|S|S|Y|I|            Window             ||       |           |G|K|H|T|N|N|                               |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|           Checksum            |         Urgent Pointer        |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                    Options                    |    Padding    |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+|                             data                              |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+TCP Header Format
  1. 端口号:用来标识同一台计算机的不同的应用进程。
    1)源端口:源端口和IP地址的作用是标识报文的返回地址。
    2)目的端口:端口指明接收方计算机上的应用程序接口。

TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接

  1. 序号和确认号:是TCP可靠传输的关键部分
    1)序号是本报文段发送的数据组的第一个字节的序号。在TCP传送的流中,每一个字节一个序号。e.g.一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。
    2)确认号,即ACK,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。

  2. 数据偏移/首部长度:4bits
    由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8 = 60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值。

  3. 保留:为将来定义新的用途保留,现在一般置0。

  4. 控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。

    1)URG:紧急指针标志,为1时表示紧急指针有效,为0则忽略紧急指针。

    2)ACK:确认序号标志,为1时表示确认号有效,为0表示报文中不含确认信息,忽略确认号字段。

    3)PSH:push标志,为1表示是带有push标志的数据,指示接收方在接收到该报文段以后,应尽快将这个报文段交给应用程序,而不是在缓冲区排队。

    4)RST:重置连接标志,用于重置由于主机崩溃或其他原因而出现错误的连接。或者用于拒绝非法的报文段和拒绝连接请求。

    5)SYN:同步序号,用于建立连接过程,在连接请求中,SYN=1和ACK=0表示该数据段没有使用捎带的确认域,而连接应答捎带一个确认,即SYN=1和ACK=1。

    6)FIN:finish标志,用于释放连接,为1时表示发送方已经没有数据发送了,即关闭本方数据流。

  5. 窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小时一个16bit字段,因而窗口大小最大为65535。

  6. 校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。

  7. 紧急指针:只有当 URG标志置1时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同的通道,而是使用另外的通道.linux系统的套接字机制支持低层协议发送和接受带外数据.但是TCP协议没有真正意义上的带外数据.为了发送重要协议,TCP提供了一种称为紧急模式(urgentmode)的机制.TCP协议在数据段中设置URG位,表示进入紧急模式.接收方可以对紧急模式采取特殊的处理.很容易看出来,这种方式数据不容易被阻塞,可以通过在我们的服务器端程序里面捕捉SIGURG信号来及时接受数据或者使用带OOB标志的recv函数来接受

  8. 选项和填充:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。

  9. 数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段

可选项:

  • 选项的第一个字段kind说明选项的类型。有的TCP选项没有后面两个字段,仅包含1字节的kind字段
  • 第二个字段length(如果有的话)指定该选项的总长度,该长度包括kind字段和length字段占据的2字节
  • 第三个字段info(如果有的话)是选项的具体信息。常见的TCP选项有7种,
    Currently defined options include (kind indicated in octal):Kind     Length    Meaning----     ------    -------0         -       End of option list.1         -       No-Operation.2         4       Maximum Segment Size.Specific Option DefinitionsEnd of Option List+--------+|00000000|+--------+Kind=0
  • kind=0是选项表结束选项。

  • kind=1是空操作(nop)选项,没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍。

  • kind=2是最大报文段长度选项。TCP连接初始化时,通信双方使用该选项来协商最大报文段长度(Max Segment Size,MSS)。TCP模块通常将MSS设置为(MTU-40)字节(减掉的这40字节包括20字节的TCP头部和20字节的IP头部)。这样携带TCP报文段的IP数据报的长度就不会超过MTU(假设TCP头部和IP头部都不包含选项字段,并且这也是一般情况),从而避免本机发生IP分片。对以太网而言,MSS值是1460(1500-40)字节。

  • kind=3是窗口扩大因子选项。TCP连接初始化时,通信双方使用该选项来协商接收通告窗口的扩大因子。在TCP的头部中,接收通告窗口大小是用16位表示的,故最大为65535字节,但实际上TCP模块允许的接收通告窗口大小远不止这个数(为了提高TCP通信的吞吐量)。窗口扩大因子解决了这个问题。假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N乘2M,或者说N左移M位。注意,M的取值范围是0~14。我们可以通过修改/proc/sys/net/ipv4/tcp_window_scaling内核变量来启用或关闭窗口扩大因子选项。和MSS选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但同步报文段本身不执行窗口扩大操作,即同步报文段头部的接收通告窗口大小就是该TCP报文段的实际接收通告窗口大小。当连接建立好之后,每个数据传输方向的窗口扩大因子就固定不变了。关于窗口扩大因子选项的细节,可参考标准文档RFC 1323。

  • kind=4是选择性确认(Selective Acknowledgment,SACK)选项。TCP通信时,如果某个TCP报文段丢失,则TCP模块会重传最后被确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段也可能重复发送,从而降低了TCP性能。SACK技术正是为改善这种情况而产生的,它使TCP模块只重新发送丢失的TCP报文段,不用发送所有未被确认的TCP报文段。选择性确认选项用在连接初始化时,表示是否支持SACK技术。我们可以通过修改/proc/sys/net/ipv4/tcp_sack内核变量来启用或关闭选择性确认选项。

  • kind=5是SACK实际工作的选项。该选项的参数告诉发送方本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。每个块边沿(edge of block)参数包含一个4字节的序号。其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。这样一对参数(块左边沿和块右边沿)之间的数据是没有收到的。因为一个块信息占用8字节,所以TCP头部选项中实际上最多可以包含4个这样的不连续数据块(考虑选项类型和长度占用的2字节)。

  • kind=8是时间戳选项。该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为TCP流量控制提供重要信息。我们可以通过修改/proc/sys/net/ipv4/tcp_timestamps内核变量来启用或关闭时间戳选项。

tcp 状态转换图
                              +---------+ ---------\      active OPEN  |  CLOSED |            \    -----------  +---------+<---------\   \   create TCB  |     ^              \   \  snd SYN    passive OPEN |     |   CLOSE        \   \           ------------ |     | ----------       \   \         create TCB  |     | delete TCB         \   \       V     |                      \   \     +---------+            CLOSE    |    \   |  LISTEN |          ---------- |     |  +---------+          delete TCB |     |  rcv SYN      |     |     SEND              |     |  -----------   |     |    -------            |     V  +---------+      snd SYN,ACK  /       \   snd SYN          +---------+|         |<-----------------           ------------------>|         ||   SYN   |                    rcv SYN                     |   SYN   ||   RCVD  |<-----------------------------------------------|   SENT  ||         |                    snd ACK                     |         ||         |------------------           -------------------|         |+---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+|           --------------   |     |   -----------                  |                  x         |     |     snd ACK                    |                            V     V                                |  CLOSE                   +---------+                              | -------                  |  ESTAB  |                              | snd FIN                  +---------+                              |                   CLOSE    |     |    rcv FIN                     V                  -------   |     |    -------                     +---------+          snd FIN  /       \   snd ACK          +---------+|  FIN    |<-----------------           ------------------>|  CLOSE  || WAIT-1  |------------------                              |   WAIT  |+---------+          rcv FIN  \                            +---------+| rcv ACK of FIN   -------   |                            CLOSE  |  | --------------   snd ACK   |                           ------- |  V        x                   V                           snd FIN V  +---------+                  +---------+                   +---------+|FINWAIT-2|                  | CLOSING |                   | LAST-ACK|+---------+                  +---------+                   +---------+|                rcv ACK of FIN |                 rcv ACK of FIN |  |  rcv FIN       -------------- |    Timeout=2MSL -------------- |  |  -------              x       V    ------------        x       V  \ snd ACK                 +---------+delete TCB         +---------+------------------------>|TIME WAIT|------------------>| CLOSED  |+---------+                   +---------+TCP Connection State DiagramFigure 6.
LISTEN:侦听来自远方的TCP端口的连接请求SYN-SENT:再发送连接请求后等待匹配的连接请求(客户端)SYN-RECEIVED:再收到和发送一个连接请求后等待对方对连接请求的确认(服务器)ESTABLISHED:代表一个打开的连接FIN-WAIT-1:等待远程TCP连接中断请求,或先前的连接中断请求的确认FIN-WAIT-2:从远程TCP等待连接中断请求CLOSE-WAIT:等待从本地用户发来的连接中断请求CLOSING:等待远程TCP对连接中断的确认LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认CLOSED:没有任何连接状态
主动端可能出现的状态:FIN_WAIT1、FIN_WAIT2、CLOSING、TIME_WAIT
被动端可能出现的状态:CLOSE_WAIT LAST_ACK
  • SYN_RCVD: 这个状态表示接收到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。如果收到一个RST信号,则返回到LISTEN状态

  • SYN_SENT: 这个状态与SYN_RCVD遥相呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。

  • FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

  • FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

  • TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

  • CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。另外一种情况就是,ACK丢失了。

  • CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

  • LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了

tcp 定时器
  • 超时重传
  • 坚持定时器:
  • keepalive
  • time_wait
流量控制和滑动窗口

滑动窗口协议是传输层进行流控的一种措施,接收方通过通告发送方自己的可以接受缓冲区大小(这个字段越大说明网络吞吐量越高),从而控制发送方的发送速度,不过如果接收端的缓冲区一旦面临数据溢出,窗口大小值也会随之被设置一个更小的值通知给发送端,从而控制数据发送量(发送端会根据接收端指示,进行流量控制)。

发送端:

  • 已发送被确认
  • 已发送未确认
  • 允许发送未发送
  • 暂不允许发送

接收端:

  • 已确认消息
  • 允许接收
  • 接收未发送确认消息
  • 不允许接收
拥塞控制和拥塞窗口

发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞。

发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数

  • 慢开始( slow-start )
  • 拥塞避免( congestion avoidance )
  • 快重传( fast retransmit )
  • 快恢复( fast recovery )
tcp 延时 ACK

参考:TCP/IP卷一:80—TCP数据流与窗口管理之(延时确认(延迟ACK)、Nagle算法

ACK延迟确认机制
接收方在收到数据后,并不会立即回复ACK,而是延迟一定时间。一般ACK延迟发送的时间低于500ms,但这个时间并非收到数据后需要延迟的时间。系统有一个固定的定时器会来检查是否需要发送ACK包。这样做有两个目的。

  • 这样做的目的是ACK是可以合并的,也就是指如果连续收到两个TCP包,并不一定需要ACK两次,只要回复最终的ACK就可以了,可以降低网络流量。
  • 如果接收方有数据要发送,那么就会在发送数据的TCP数据包里,带上ACK信息。这样做,可以避免大量的ACK以一个单独的TCP包发送,减少了网络流量。

不同操作系统对延迟确认的实现

  • 采用延时ACK的方法会减少ACK传输数目,可以一定程度地减轻网络负载。对于批量数据传输通常为 2:1 的比例。基于不同的主机操作系统,延迟发送ACK的最大时延可以动态配置
  • Linux使用了一种动态调节算法,可以在每个报文段返回一个ACK (称为“快速 确认”模式)与传统延时ACK模式间相互切换
  • Mac OS X中,可以改变系统变量net.inet. tcp.delayed_ack值​来设置延时ACK。可选值如下:禁用延时(设为0),始终延时(设为1),每隔一个包回复一个ACK(设为2),自动检测确认时间(设为3)。默认值为3
  • 最新的 Windows版本中,​注册表项中,每个接口的全局唯一标识(GUID)都不同(IG表示被引用的特定网络接口的GUID)。TcpAckFrequency值(需要被添加)可以设为0-255,默认为2。它​代表延时ACK计时器超时前在传的ACK数目​。将其设为1表明对每个收到的报文段都生成相应的ACK。ACK计时器值可以通过TcpDelAckTicks注册表项控制。该值可设为2 - 6,默认为2。它以百毫秒为单位,表明在发送延时ACK前要等待百毫秒数
//在c语言中可以通过设置socket来实现
int quickack = 1; /* 启用快速确认,如果赋值为0表示使用延迟确认 */
setsockopt(fd, SOL_TCP, TCP_QUICKACK, &quickack, sizeof(quickack));
Time-wait状态(2MSL)
1. 为什么需要TIME_WAIT状态

假设最后的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发最后的ACK,否则将会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连接的两个方向,所以client必须进入TIME_WAIT状态。

此外,考虑一种情况,TCP实现可能面临着先后两个相同的五元组。如果前一个连接处于TIME_WAIT状态,而允许另一个拥有相同五元组连接出现,可能处理TCP报文时,两个连接互相干扰。所以使用SO_REUSEADDR选项就需要考虑这种情况。

linux网络编程

相关概念

同步异步

同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO 操作并等待或者轮询的去查看IO 操作是否就绪,而异步是指用户进程触发IO 操作以后便开始做自己的事情,而当IO 操作已经完成的时候会得到IO 完成的通知。

阻塞非阻塞

阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入方法会立即返回一个状态值。

套接字地址结构

struct in_addr

字节顺序为网络顺序(network byte ordered),即该无符号整数采用大端字节序

//ipv4套接字地址结构,在<netinet/in.h>中声明
typedef uint32_t in_addr_t;  //32位(unsigned int)的ip地址,
struct in_addr
{in_addr_t s_addr;
};

struct sockaddr和struct sockaddr_in

struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,
二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。
一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中

//sizeof(sockaddr_in)=16,定义在#include <netinet/in.h>
struct sockaddr_in
{unsigned short int sin_family;    //Address family 2unsigned short int sin_port;      // Port number 2struct in_addr sin_addr;          //Internet address 4unsigned char sin_zero[8];        //未使用 8
};//sizeof(sockaddr)=16,定义在#include <sys/socket.h>
struct sockaddr
{sa_family_t sa_family;            //sa_family_t为unsigned short int,地址家族, AF_INET 2char sa_data[14];                 // 14 bytes of protocol address
};

字节排序函数

首先解释一下字节序的概念,所谓字节序是指多字节数据的存储顺序,比如0x1234要放在0000H和0001H两存储单元,有两种存储方式:大端格式为[0000H]=12,[0001H]=34和小端格式为[0000H]=34,[0001H]=12。

  1. 大端格式:将高位字节数据存储在低地址,低位字节数据存储在高地址

  2. 小端格式:将高位字节数据存储在高地址,低位字节数据存储在低地址

#include <stdio.h>int main(int argc, char *argv[])
{union{short temp;char test[sizeof(short)];}un_tmp;un_tmp.temp = 0x1234;if ((un_tmp.test[0] == 0x12) && (un_tmp.test[1] == 0x34)) {printf("大端格式:高位字节数据存储在低地址,低位字节数据存储在高地址");}if ((un_tmp.test[0] == 0x34) && (un_tmp.test[1] == 0x12)){printf("小端格式:高位字节数据存储在高地址,低位字节数据存储在低地址");}return 0;}

网际协议采取的是大端字节序,我们在编程的时候才需要考虑网络字节许和主机字节序之间的转换。下面是四个转换函数

#include <netinet/in.h>uint16_t htons(uint16_t host16bitvalue);
uint32_t htonl(uint32_t host32bitvalue);  //均返回网络字节序uint16_t ntohs(uint16_t net16bitvalue);
uint32_t ntohl(uint32_t net32bitvalue);  //均返回主机字节序

地址转换函数:

BSD网络软件中包含了inet_addr、inet_aton和inet_ntoa,用来在二进制地址格式和点分十进制字符串格式之间相互转换,但是这三个函数仅仅支持IPv4。(废弃,不建议使用)

1. in_addr_t inet_addr(const char *cp)函数转换标准的ASCII以点分十进制的地址值返回为网络字节序二进制值

如果参数 char *cp 无效则返回-1(INADDR_NONE),但这个函数有个缺点:在处理地址为255.255.255.255时也返回-1,虽然它是一个有效地址,但inet_addr()无法处理这个地址。

#include <arpa/inet.h>in_addr_t inet_addr(const char *cp);  //in_addr_t-->uint32_t

输入是点分的IP地址格式(如A.B.C.D)的字符串,从该字符串中提取出每一部分,转换为ULONG,假设得到4个ULONG型的A,B,C,D,
ulAddress(ULONG型)是转换后的结果,
ulAddress = D<<24 + C<<16 + B<<8 + A(网络字节序),即inet_addr(const char *)的返回结果
另外,我们也可以得到把该IP转换为主机序的结果,转换方法一样
A<<24 + B<<16 + C<<8 + D

2. int inet_aton(const char *__cp, in_addr *__inp)转换标准的ASCII以点分十进制的地址值返回网络字节序二进制值

如果这个函数成功,函数的返回值非零。如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以他的值会被忽略。

#include <arpa/inet.h>
/*** @brief inet_aton* @param __cp     输入参数包含ASCII表示的IP地址* @param __inp    输出参数将要用新的IP地址更新的结构* @return */
extern int inet_aton (const char *__cp, struct in_addr *__inp);

3. char *inet_ntoa (struct in_addr __in)函数转换网络字节序二进制值返回标准的ASCII以点分十进制的地址值

该函数返回值指向保存点分十进制的字符串地址的指针,该字符串的空间为静态分配 的,所以在第二次调用这个函数时,意味着上一次调用并保存的结果将会被覆盖(重写)

#include <arpa/inet.h>extern char *inet_ntoa (struct in_addr __in);

4. 代码示例:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
int main(int argc, char *argv[])
{char ip1[] = "192.168.0.74";char ip2[] = "211.100.21.179";struct in_addr addr1, addr2;long l1, l2;l1 = inet_addr(ip1);   //IP字符串——》网络字节l2 = inet_addr(ip2);printf("IP1: %s\nIP2: %s\n", ip1, ip2);printf("Addr1: %ld\nAddr2: %ld\n", l1, l2);memcpy(&addr1, &l1, 4); //复制4个字节大小memcpy(&addr2, &l2, 4);printf("%s <--> %s\n", inet_ntoa(addr1), inet_ntoa(addr2)); //注意:printf函数自右向左求值、覆盖printf("%s\n", inet_ntoa(addr1)); //网络字节 ——》IP字符串printf("%s\n", inet_ntoa(addr2));return 0;
}
IP1: 192.168.0.74
IP2: 211.100.21.179
Addr1: 1241557184
Addr2: 3004523731
192.168.0.74 <--> 192.168.0.74
192.168.0.74
211.100.21.179

功能相似的两个函数同时支持IPv4和IPv6,p代表presentation表达,n代表numeric数值

1. int inet_pton(int domain, const char *str, void *addr)将标准的ASCII以点分十进制的地址值转化为网络传输的二进制数值格式

返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1

#include <arpa/inet.h>int inet_pton(int family, const char *strptr, void *addrptr)
{
//这两个函数的family参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。
//如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.if (family == AF_INET) {struct in_addr  in_val;if (inet_aton(strptr, &in_val)) {memcpy(addrptr, &in_val, sizeof(in_val));return (1);}}errno = EAFNOSUPPOPT;return (-1);
}

2. const char *inet_ntop(int domain, const void *addr, char *str, socklen_t size)将网络传输的二进制数值转化标准的ASCII以点分十进制的地址值格式

inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,返回值:若成功则为指向结构的指针,若出错则为NULL

#include <arpa/inet.h>const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len)
{const u_char *p = (const u_char*)addrptr;if (family == AF_INET) {char temp[INET_ADDRSTRLEN];snprintf(temp, sizeof(temp), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);if (strlen(temp) >= len) {errno = ENOSPC;rturn (NULL);}strcpy(strptr, temp);return (strptr);}errno = EAFNOSUPPOPT;return (NULL);
}

3. 代码示例:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>int main()
{char ip[] = "192.168.0.74"; struct in_addr addr;int ret = inet_pton(AF_INET, ip, (void *)&addr);   //IP字符串 ——》网络字节流if(0 == ret){printf("inet_pton error, return 0\n");return -1;}else{printf("inet_pton ip: %ld\n", addr.s_addr);printf("inet_pton ip: 0x%x\n", addr.s_addr);}const char *pstr = inet_ntop(AF_INET, (void *)&addr, ip, 128);  //网络字节流 ——》IP字符串if(NULL == pstr){printf("inet_ntop error, return NULL\n");return -1;}else{printf("inet_ntop ip: %s\n", ip);}return 0;
}
inet_pton ip: 1241557184
inet_pton ip: 0x4a00a8c0
inet_ntop ip: 192.168.0.74

TCP通信相关函数

1. int socket(family, type, protocol):创建套接字

/*** #include <sys/types.h>* #include <sys/socket.h>* @brief Socket  创建一个套接字用于通信* @param family*      AF_INET             IPv4地址协议AF_INET6            IPv6地址协议AF_LOCAL            UNIX域协议AF_ROUTE            路由套接字AF_KEY              密钥套接字* @param type  指定socket类型,SOCK_STREAM         流式套接字SOCKDGRAM           数据报套接字SOCK_SEQPACKET      有序分组套接字SOCKRAW             原始套接字,提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)* @param protocol 协议类型 If PROTOCOL 为0,内核将会自动进行选择,可以默认填0IPPROTO_TCP         TCP传输协议IPPROTO_UDP         UDP传输协议IPPROTO_SCTP        SCTP传输协议* @return 成功返回非负整数套接字描述符;失败返回-1*/
int socket(int family,int type,int protocol);

2. int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen):绑定套接字

#include <sys/types.h>
#include <sys/socket.h>
/*** @brief bind* @param fd  绑定套接子* @param addr  要绑定的地址* @param addrlen  地址长度* @return 成功返回 0  失败返回 -1*/
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

3. int listen(int fd,int backlog):设置同时通信的最大套接字数量

#include <sys/types.h>
#include <sys/sock.h>/*** @brief listen*   (1)一般来说,listen函数应该在调用socket和bind函数之后,调用accept函数之前调用*   (2)对于给定的监听套接字接口,内核要维护两个队列*          <1>已由客户发送并到达服务器,服务器正在等待完成对应的TCP三次握手过程*          <2>已经完成连接的队列* @param fd  socket函数返回的套接字* @param backlog  规定内核为此套接字排队的最大的连接个数* @return 成功返回 0  失败返回 -1*/
int listen(int fd,int backlog);

4. int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)阻塞式监听客户端连接

#include <sys/types.h>
#include <sys/scoket.h>/*** @brief accept  从已经完成连接队列返回第一个连接,如果已经完成连接队列为空,则阻* @param sockfd  服务器套接字* @param addr    将返回对等待的套接字地址* @param addrlen 返回对等方的套接字地址长度* @return  成功返回非负整数:对应和客户点连接的新套接字 ,失败返回-1*/
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

5. int connect(int sockfd, const strcut sockaddr *addr, socklen_t addrlen)用来客户端和tcp服务器建立连接

#include <sys/types.h>
#include <sys/socket.h>/*** @brief connect  用于建立与指定socket的连接* @param sockfd   标识一个未连接的socket* @param addr    指定要连接套接字的sockaddr结构体的指针* @param addrlen sockaddr结构体的字节长度* @return  0 on success, -1 for errors*/
int connect(int sockfd, const strcut sockaddr *addr, socklen_t addrlen);

5. ssize_t read(int fd,void *ptr,size_t nbytes)一次读取指定字节长度数据

#include <unistd.h>/*** @brief read* @param fd   将要读取数据的文件描述符* @param ptr  所读取到的数据的内存缓冲* @param nbytes   需要读取的数据量* @return  成功执行时,返回所读取的数据量;*      如果返回0, 表示已到达文件尾或是无可读取的数据*      失败返回-1,errno被设为以下的某个值EAGAIN:打开文件时设定了O_NONBLOCK标志,并且当前没有数据可读取EBADF:文件描述词无效,或者文件不可读EFAULT:参数buf指向的空间不可访问EINTR:数据读取前,操作被信号中断EINVAL:一个或者多个参数无效EIO:读写出错EISDIR:参数fd索引的时目录*/
ssize_t read(int fd,void *ptr,size_t nbytes);

6. ssize_t write(int fd,const void *ptr,size_t nbytes)一次写入指定字节长度数据

/*** @brief write* @param fd   将要写入数据的文件描述符* @param ptr  所写入到的数据的内存缓冲* @param nbytes   需要写入的数据量* @return 成功执行时,返回所写入的数据量。失败返回-1,错误代码存入errno中*/
ssize_t write(int fd,const void *ptr,size_t nbytes);

7. ssize_t Readn(int fd,void *vptr,size_t n);循环读取n个字节数据

/*** @brief Readn 从描述符fd中读取n个字节,存入vptr指针的位置1. 当剩余长度大于0的时候就一直读啊读2. 当read的返回值小于0的时候,做异常检测3. 当read的返回值等于0的时候,退出循环4. 当read的返回值大于0的时候,拿剩余长度减read的返回值,拿到新的剩余长度,读的入口指针加上read的返回值,进入步15. 返回参数n减去剩余长度,即实际读取的总长度* @param fd* @param vptr* @param n* @return*/
/* Read "n" bytes from a descriptor. */
ssize_t Readn(int fd, void *vptr, size_t n)
{size_t nleft;ssize_t   nread;char  *ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ( (nread = read(fd, ptr, nleft)) < 0) {if (errno == EINTR)nread = 0;        /* and call read() again */elsereturn(-1);} else if (nread == 0)break;                /* EOF */nleft -= nread;ptr   += nread;}return(n - nleft);       /* return >= 0 */
}
/* end readn */

8. ssize_t Writen(int fd,const void*vptr,size_t n)循环写入n个字节数据

/*** @brief Writen  向描述符fd中写入n个字节,从vptr位置开始写1. 当要写入的剩余长度大于0的时候就一直写啊写2. 当write的返回值小于0的时候,做异常检测3. 当write的返回值等于0的时候,出错退出程序4. 当write的返回值大于0的时候,拿剩余长度减去write的返回值,拿到新的剩余长度,写的入口指针加上write的返回值,进入步骤15. 返回参数n的值,即期望写入的总长度* @param fd* @param vptr* @param n* @return */
/* Write "n" bytes to a descriptor. */
ssize_t Writen(int fd, const void *vptr, size_t n)
{size_t     nleft;ssize_t       nwritten;const char *ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ( (nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR)nwritten = 0;        /* and call write() again */elsereturn(-1);         /* error */}nleft -= nwritten;ptr   += nwritten;}return(n);
}
/* end writen */

9. ssize_t Readline(int fd, void *vptr, size_t maxlen) 读到’\n’或者读满缓冲区才返回

static ssize_t readch(int fd, char *ptr)
{static int read_cnt;static char *read_ptr;static char read_buf[100];if(read_cnt <= 0){
again:if((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0){if(errno == EINTR){goto again;}else{return -1;}}else if(read_cnt == 0){return 0;}read_ptr = read_buf;}read_cnt--;*ptr = *read_ptr++;return 1;
}ssize_t Readline(int fd, void *vptr, size_t maxlen)
{ssize_t n, rc;char c, *ptr;ptr = vptr;for(n = 1; n < maxlen; n++){if((rc = readch(fd, &c)) == 1){*ptr++ = c;if(c == '\n'){break;}}else if(rc == 0){*ptr = 0;return n - 1;}else{return (n - 1);}}*ptr = 0;return n;
}

10. int close(int fd) 关闭套接字

close函数会关闭套接字ID,如果有其他的进程共享着这个套接字,那么它仍然是打开的,这个连接仍然可以用来读和写,并且有时候这是非常重要的 ,特别是对于多进程并发服务器来说

//一般不会立即关闭而经历TIME_WAIT的过程
#include<unistd.h>
int close(int sockfd);     //返回成功为0,出错为-1

11. shutdown()函数切断进程共享的套接字的所有连接

#include<sys/socket.h>  /*** @brief shutdown shutdown会切断进程共享的套接字的所有连接,不管这个套接字的引用计数是否为零,*                  那些试图读得进程将会接收到EOF标识,那些试图写的进程将会检测到SIGPIPE信号,*                  同时可利用shutdown的第二个参数选择断连的方式* @param sockfd 文件描述符* @param howto1.SHUT_RD:值为0,关闭连接的读这一半。2.SHUT_WR:值为1,关闭连接的写这一半。3.SHUT_RDWR:值为2,连接的读和写都关闭。* @return 成功为0,出错为-1.*/
int shutdown(int sockfd,int howto);

12. recv()和send()函数

int recv(int sockfd,void *buf,int len,int flags);
int send(int sockfd,void *buf,int len,int flags);
flags 含义
0 相当于read和write函数
MSG_DONTROUTE 不查找表
MSG_OOB 接受或者发送带外数据
MSG_PEEK 查看数据,并不从系统缓冲区移走数据
MSG_WAITALL 等待所有数据
  • MSG_DONTROUTE:是send函数使用的标志。这个标志告诉IP,目的主机在本地网络上面,没有必要查找表。这个标志一般用网络诊断和路由程序里面。

  • MSG_OOB:表示可以接收和发送带外的数据。关于带外数据我们以后会解释的。

  • MSG_PEEK:是recv函数的使用标志。表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容,这样下次读的时候仍然是一样的内容。一般在有多个进程读写数据时可以使用这个标志。

  • MSG_WAITALL:是recv函数的使用标志。表示等到所有的信息到达时才返回。使用这个标志的时候recv会一直阻塞,直到指定的条件满足或者是发生了错误。

1)当读到了指定的字节时,函数正常返回。返回值等于len
2)当读到了文件的结尾时,函数正常返回。返回值小于len
3)当操作发生错误时返回-1,且设置错误为相应的错误号(errno)

UDP通信相关函数

1. recvfrom()函数

/*** @brief recvfrom* @param sockfd    套接字* @param buf       UDP数据报缓存区(包含所接收的数据* @param nbytes    缓冲区长度* @param flags     调用操作方式(一般设置为0)* @param from      指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换)* @param fromlen   指针,指向from结构体长度值* @return  成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno 中*/int recvfrom(int sockfd, const void *buf, size_t nbytes,int flags,struct sockaddr *from, int *fromlen);

2. sendto()函数

sendto函数专用与UDP连接

/*** @brief sendto* @param sockfd    套接字* @param buf       带发送数据存储缓冲区* @param nbytes    要发送数据的字节数* @param flags     可选标志* @param destaddr  (目标地址)数据接收方* @param destlen   目标地址结构长度* @return */ssize_t sendto(int sockfd,const void * buf,size_t nbytes,int flags,const struct sockaddr_in * destaddr,socklen_t destlen );

UDP组播通信相关函数

组播组可以是永久的也可以是临时的。组播组地址中,有一部分由官方分配的,称为永久组播组。永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。永久组播组中成员的数量都可以是任意的,甚至可以为零。那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。

  1. 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用;
  2. 224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;
  3. 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效;
  4. 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效。
getsockopt()/setsockopt()的选项 含义
IP_MULTICAST_TTL 设置多播组数据的TTL值
IP_ADD_MEMBERSHIP 在指定接口上加入组播组
IP_DROP_MEMBERSHIP 退出组播组
IP_MULTICAST_IF 获取默认接口或设置接口
IP_MULTICAST_LOOP 禁止组播数据回送
    // IPv4 multicast request. struct ip_mreq{// 多播组的IP地址 IP multicast address of group. struct in_addr imr_multiaddr;//加入的客户端主机IP地址 Local IP address of interface. struct in_addr imr_interface;};//加入组播组ip_mreq multiCast;multiCast.imr_interface.s_addr=htonl(INADDR_ANY);     //本地某一网络设备接口的IP地址。multiCast.imr_multiaddr.s_addr=inet_addr("234.2.2.2"); //组播组的IP地址。setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,(char*)&multiCast,sizeof(multiCast));

常见的错误码

  1. 网络通信中 TCP 产生 RST 的三个条件分析

参考文献

  1. LInux Tcp 延迟确认问题

UNIX网络套接字相关总结相关推荐

  1. 【Android 应用开发】Android 网络编程 API笔记 - java.net 包 权限 地址 套接字 相关类 简介

    Android 网络编程相关的包 : 9 包, 20 接口, 103 类, 6 枚举, 14异常; -- Java包 : java.net 包 (6接口, 34类, 2枚举, 12异常); -- An ...

  2. Linux网络编程——Unix本地套接字

    概述 今天给大家讲解网络编程中的一个内容--Unix 本地套接字. 发现很多人不知道或者不太了解 Unix 本地套接字这个概念,这也难怪,socket API 原本就是为多台主机之间网络通信设计的,并 ...

  3. linux篇【12】:网络套接字<前序>—网络基础+udp套接字

    目录 一.网络基础 1.认识 "协议" 举例: 2.协议分层 (1)软件分层 (2)协议分层 3.OSI七层模型 4.TCP/IP五层(或四层)模型 5.网络和操作系统之间的关系 ...

  4. 网络套接字编程之IO模型详解

    网络套接字编程之IO模型详解 本文主要参考自<UNIX网络编程>(第1卷)(套接口API第3版) Unix下可用的五种I/O模型有: 阻塞式I/O 非阻塞式I/O I/O复用(select ...

  5. 【Linux】网络基础+UDP网络套接字编程

    只做自己喜欢做的事情,不被社会和时代裹挟着前进,是一件很奢侈的事. 文章目录 一. 网络基础 1.局域网和广域网 2.协议初识和网络协议分层(TCP/IP四层模型) 3.MAC地址和IP地址(子网掩码 ...

  6. 什么是网络套接字(Socket)?

    什么是网络套接字(Socket)?一时还真不好回答,而且网络上也有各种解释,莫衷一是.下文将以本人所查阅到的资料来说明一下什么是Socket. Socket定义 Socket在维基百科的定义: A n ...

  7. 网络套接字(Network socket)

    网络套接字(英语:Network socket:又译网络套接字.网络接口.网络插槽)在计算机科学中是电脑网络中进程间资料流的端点.使用以网际协议(Internet Protocol)为通信基础的网络套 ...

  8. 【Linux】网络套接字编程

    前言 在掌握一定的网络基础,我们便可以先从代码入手,利用UDP协议/TCP协议进行编写套接字程序,明白网络中服务器端与客户端之间如何进行连接并且通信的. 目录 一.了解源目的IP.端口.网络字节序.套 ...

  9. Linux套接字编程之sockaddr与sockaddr_in网络套接字,sockaddr_un进程间通信本地套接字

    sockaddr struct sockaddr { unsigned  short  sa_family;     /* address family, AF_xxx */ char  sa_dat ...

最新文章

  1. 姚班代有才人出:清华本科生用“最简单的形式”,大幅提高少样本学习性能...
  2. Kafka使用遇到的坑
  3. 【天池赛事】零基础入门语义分割-地表建筑物识别 Task1:赛题理解与 baseline
  4. 《LoadRunner 12七天速成宝典》—第2章2.1节开始
  5. WiresShark之抓http包如何倒出图片格式的对象
  6. Windows 8 JavaScript Metro应用程序--入门(上)
  7. 【项目管理】CMM能力成熟度模型
  8. iphonex如何关机_历时一个月,跨越一千里,我找回了在澳门被偷的iphoneX
  9. 11dayC语言指针-指针变量
  10. 互联网搜索的哪些环节 机器学习_什么是机器学习?有哪些分类?到底有什么用?终于有人讲明白了...
  11. .net中的设计模式---单例模式
  12. 服务器本地打开asp文件路径,服务器本地打开asp文件
  13. 用python分析拼多多_python:拼多多订单接口api
  14. 计算机学院实验室安全管理办法,计算机科学学院实验室安全管理制度
  15. kettle使用命令行来运行ktr和kjb
  16. 案例教程:一步步教你ps制作二寸照片
  17. view-ui中select全选实现
  18. 视频教程-区块链基础:共识算法-区块链
  19. Qt编写地图综合应用49-地图类型(街道图、卫星图)
  20. 阿里、京东、乐语们纷纷下注商超,到底为什么?

热门文章

  1. Java 8中Collectors.groupingBy方法空指针异常源码分析
  2. 跟班学习JavaScript第一天——运算符、数据类型、ECMAScript
  3. rtos中的喂狗思路——freertos
  4. verilog always语法_Verilog 最全经验总结(建议收藏)
  5. Groovy on Grails(Java笨狗)系列---前言(二)
  6. 动图制作工具---LICEcap
  7. 详解生物地理学优化(BBO)算法(一)
  8. 今天一天学习的MYSQL语句,立此存照
  9. python 图像清晰度_图像清晰度评价指标(Python)
  10. jws webservice 跳过https认证_【大连学为贵5周年庆典】多邻国考试不能认证是怎么回事?这些雷区不要踩!...