网络编程的一些知识点

##socket的缓冲区
每个套接字都有一个发送缓冲区和一个接收缓冲区.

接收缓冲区被TCP,UDP和SCTCP用来保存接收到的数据,直到由应用进程读取。对于TCP来说,套接字接收缓冲区可用空间的大小限制了TCP通告对端的窗口大小。TCP套接字接收缓冲区不可以溢出,因为不允许对端发出超过本端所通告窗口大小的数据,这就是TCP的流量控制。如果对端无视窗口大小而发出了超过窗口大小的数据,本端TCP将丢弃它们。然而对于UDP来说,当接收到的数据报装不进套接字接收缓冲区时,该数据报就被丢弃。回顾一下,UDP是没有流量控制的:较快的发送端可以很容易的淹没较慢的接收端,导致接收端的UDP丢弃数据报。

##socket 在什么情况下可读?

  1. 正常有数据输入的时候;
  2. 有异常发生的时候,read 返回 -1,errno 置为错误编号;
  3. 对端关闭的时候,read 返回 0, 表示读文件结束;
  4. 当这个socket 是一个监听 socket ,且连接数不为0,又有 SYN 进来时。

##listen不是一个阻塞函数
listen的作用是将一个套接字设置为监听状态,并且设置监听数目

他不是一个阻塞函数,设置完套接字后它就没有作用了,以后就是套接字在等待来自客户端的连接

##tcp三次握手的过程,accept发生在三次握手哪个阶段

当客户发起请求时,内核将为每个新到的客户完成3次握手,直到达到相应套接字上listen调用的backlog数为止,然后在服务器调用accept时把这些已完成的连接传递给它。

所以说,accept是在三次握手完成之后执行的

  • 第一次握手:客户端发送syn包(syn=j)到服务器。
  • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个ASK包(ask=k)。
  • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)。

三次握手完成后,客户端和服务器就建立了tcp连接。这时可以调用accept函数获得此连接。

accept会阻塞直到3次握手成功为止

##为什么客户端不需要调用bind
客户端在调用函数connect之前不必非得调用bind函数(当然也可以调用),因为如果需要的话,内核会确定源ip地址,并选择一个临时端口作为源端口。

服务在listen之前也可以不调用bind函数,这样内核同样会为套接字选一个ip和临时端口。让内核来选择临时端口对tcp客户端来说是常见的。但是对于tcp服务器来说却即为罕见,因为服务器是通过众所周知端口被大家认识的。

同样,调用bind可以指定ip地址或端口,可以都指定,也可以都不指定,也可以只指定其中一个。

##traceroute的实现原理
每个IP数据报都有TTL字段,每经过一个路由器,该字段就被减1,抵达路由器的数据报的TTL为1或0时,就会被丢弃,同时会发送一个ICMP“传输超时”错误报文给原始发送者。traceroute利用了这个特性,它首先向目的地发送一个TTL设置为1的UDP数据报。当这个UDP数据报抵达第一跳时,路由器注意到TTL为1,就会将其丢弃,并发回一个ICMP报文,从这个ICMP报文的源地址中,可以得到第一跳的IP地址,然后traceroute用gethostbyaddr来查询它的名字。为获得下一跳的标识,traceroute重复这个过程,只是把TTL设置为2,当这个数据报到达第一跳时,TTL被减为1并转发往下一跳,在那里发现TTL为1,丢弃它,并返回ICMP报文,通过不断增加TTL来重复这个过程,traceroute就可以获得从源到目的地之间的路径消息。

##TCP 的头有多少个字节?有哪些字段?
TCP 头有 20个字节,选项12个字节.

  • 源端口号16位
  • 目的端口号16位
  • SEQ32位(序列号)
  • ACK32位(确认序列号)
  • 包头长度4位
  • 保留字段6位
  • 包类型6位
  • 窗口大小16位
  • 校验和16位
  • 紧急指针16位
  • 可选项32位

之后是数据

##keepalive 是什么?起什么作用?起作用的过程是什么?
keepalive 是 TCP 协议内的心跳机制,用来维护链接的状态,默认不开启。服务器端每隔一段事件会向空闲两小时以上的链接的对端发送一个 keepalive 包,有以下情形:

  1. 对端回复 ACK,链接保活,再次空闲两小时后重新探测;
  2. 对端的应用程序已经退出,TCP 回复一个 RST包,链接关闭;
  3. 对端的应用程序卡死/无反应,TCP 回复一个 FIN包,来终止链接;
  4. 对端机器无任何反应,服务器端将持续发送 keepalive 包,超时则关闭链接。时间范围半小时到两小时。

##UDP的recvfrom函数可以返回0

写一个长度为0的数据报是可行的。在UDP情况下,这会形成一个只包含一个IP首部(对于IPv4通常为20字节,对于IPv6通常为40字节)和一个8字节UDP首部而没有数据的IP数据报。这也意味着对于数据报协议,recvfrom返回0值是可以接受的:它不像TCP套接字上read返回0值那样表示对端关闭连接。既然UDP是无连接的,因此也就没有诸如关闭一个UDP连接之类的事情

##UDP 的 connect 有什么作用?
UDP 的 connect 和 TCP 的 connect 不同之处在于,UDP connect 的时候只会进行对端的记录,以及显而易见的错误的检查(比如 IP 压根儿填错了啊),而没有 TCP 的三次握手操作;这时,当前的 UDP socket 也被限定于只与 connect 指定的对端通信,当前的 socket 发生的异常也只会通知到 当前进程;同时,UDP 的 connect 是可以进行重复操作进行对端更新的,即 connect 完 A 之后,再调用 connect B 也是可以的,这与 TCP 不同。

UDP 链接的两种模式:

  1. socket —> sendto —> recvfrom
  2. socket —> connect —> send —> recv

第二种模式也是可以使用 sendto 和 recvfrom 的,但是 参数内的 地址信息需要置成 NULL。

##EAGAIN错误的含义

在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。

从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。

又例如,当一个系统调用(比如fork)因为没有足够的资源(比如虚拟内存)而执行失败,返回EAGAIN提示其再调用一次(也许下次就能成功)。

Linux - 非阻塞socket编程处理EAGAIN错误
 在linux进行非阻塞的socket接收数据时经常出现Resource temporarily unavailable,errno代码为11(EAGAIN),这是什么意思?

这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏socket的同步,不用管它,下次循环接着recv就可以。对非阻塞socket而言,EAGAIN不是一种错误。在VxWorks和Windows上,EAGAIN的名字叫做EWOULDBLOCK

另外,如果出现EINTR即errno为4,错误描述Interrupted system call,操作也应该继续。

最后,如果recv的返回值为0,那表明连接已经断开,我们的接收操作也应该结束。