发表于

网络相关知识

UDP是数据报文协议,非常的简单,有边界且无状态;边界是通过数据包中给出该数据包长度来实现的。 TCP是传输控制协议,提供如下功能:

  • 连接:三次握手和四次挥手,通过SYN,ACK,FIN等比特位来实现
  • 排序:所有的TCP包都是有顺序的,保证了数据包丢失后重传,乱序到达后内核重新组织等功能
  • 流量控制:定义了一个通告窗口,该窗口确定了数据容量,当满了的时候通知客户端暂停发送数据包。
  • 全双工:双向通信,也可以调整问单向通信的

SCTP不仅包括了TCP和UDP的特点,还能够实现多宿功能,从而提升了健壮性,也就是网络故障对数据传输的影响更小。

TCP选项

  • MSS选项,也就是最大分节大小,也就是一个数据包的最大字节数,可以通过设置TCP_MAXSEG来设置
  • WS选项,就是窗口大小,通过SO_RCVBUF和SO_SNDBUF来控制

TIME_WAIT存在的目的:

在四次挥手阶段,会有个TIME_WAIT状态;因为数据包在网络中是有生存期的,利用这个生存期时间来解决一些问题:

  • 主动发起FIN的那一段会等待被动关闭那一段发来的ACK,这个ACK可能是重传的
  • 保证老的连接中迷失的数据包不会干扰新的连接,因为经过了2MSL时间后数据包必定消失

套接字

利用套接字对来分析,监听套接字为(:21,:),已连接套接字需要将都填满。

五种套接字

  • sockaddr_in
  • sockaddr_in6
  • sockaddr_un
  • sockaddr_dl
  • sockaddr_storage

套接字函数的形参是struct sockaddr*,所以实参是引用的形式;且套接字函数分为从内核传递到用户和用户传递到内核两种类别。

  • 用户到内核:bind connect sento
  • 内核到用户:accept recvfrom getsockname getpeername

名字和地址转换方式

#include <netdb.h>
int getaddrinfo(const char* hostname, const char* service,
    const struct addrinfo* hints, struct addrinfo **result) //根据主机名和端口来得到对应套接字相关信息

void freeaddrinfo(struct addrinfo *ai) //释放堆内存中的addrinfo链表

const char* gai_strerror(int error) //getaddrinfo的返回值对应的错误信息

POSIX标准定义了inet_pton和inet_ntop来进行字符串的地址和数值地址的转换

五种IO模型

unp最重要的就是介绍了五种IO模型,理解他们的本质在于:

  • 内核维护IO,用户和内核都维护一段缓冲用于存储IO相关数据
  • 用户只是从内核的IO缓冲中拷贝数据
  • 同步和异步的区别在于是否会消耗用户态时间

我们一般写的都是用户态的程序,所以一定要考虑到程序的运行是要和内核搭配的。比如socket套接字,其实是内核维护了了TCP/IP协议栈,用户维护了应用层协议,通过socket来实现用户到内核的交互。

  • 同步阻塞
  • 同步非阻塞
  • 信号驱动IO
  • select/poll
  • 异步IO

需要注意的是,内核维护的描述符结构体中,存在引用计数,所以close并不一定回关闭对应IO,当且仅当引用计数为0时才会关闭。所以fork时,内核维护的描述符结构体中的引用计数会加1,需要在子进程中根据需要调用close来减一。

select 和 poll

#include <sys/select.h>
#include <sys/time>

int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *exceptset,
const struct timeval *timeout)

其中fd_set操作函数包括

  • void FD_ZERO(fd_set *fdset)
  • void FD_SET(int fd, fd_set *fdset)
  • void FD_CLR(int fd, fd_set *fdset)
  • int FD_ISSET(int fd, fd_set *fdset)

其中maxfdp1最大值是FD_SETSIZE,也就是1024。当有带外数据的时候,就会设置exceptset的比特位。

非常值得注意的一点是:select无法感知fd的缓冲,导致的问题是对于一个IO设备,还没有在缓冲区写入所有数据时,select已经执行且只能读取到部分数据。解决方法是利用read或write的返回值来确定是否重新调用select