TCP/IP协议族
-
数据链路层、网络层、传输层协议是在Linux内核中实现的。因此操作系统需要实现一组系统调用,使得应用程序能够访问这些协议提供的服务。实现这组系统调用的API就是Socket。
-
Socket提供两种功能:
- 将应用程序数据从用户缓冲区中复制到 TCP/UDP内核发送缓冲区,以交付内核来发送数据;或者是从内核 TCP/UDP接收缓冲区中复制数据到用户缓冲区,以读取数据。
- 应用程序以通过它们来修改内核中各层协议的某些头部信息或其他数据结构,从而精细地控制底层通信的行为
-
route命令可以查看路由表。
-
TCP是基于字节流:写操作次数和读操作次数之间没有关系。数据的发送与接受是没有边界的。
-
TCP状态转移的图得吃透:
-
TIME_WAIT状态,出现于客户端返回最后一个确认之后。主要原因有两个:
- 可靠地终止TCP连接,害怕这个ACK数据包丢失,服务端未关闭。
- 让这次连接中的迟到的报文段,有足够的时间被丢弃,而不会发送至下一个连接。
-
在例子的抓包数据中,好像有fin和ack合并的情况,这样就变成了三次握手??
-
cookie: HTTP是无状态的协议,但是如果后续HTTP请求需要用到前文的数据的话,那么就需要用Cookie保存这些信息。Cookie是服务器发送给客户端的特殊信息,客户端每次向服务器发送请求的时候,带上Cookie。
Linux API
-
主机字节序和网络字节序:大小端字节序。一般主机都是小端,而网络传输是大端,也叫网络字节序。
- htonl(),htons(),ntohl(),ntohs()四个函数用来转换(n代表net网络,h代表host主机)(l为long,s为short)。
-
TCP/IP的Socket地址
- 表示为:
- 地址结构与可读性良好的字符串的转化:
-
inet_aton函数从字符串转为地址。
-
inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的ipv4地址。
-
- 表示为:
-
Socket是一个文件描述符:
- 创建:
- 命名,(bind, 为啥不叫绑定):将地址分配给sockfd描述符:
- 监听(创建监听队列以存放待处理的客户连接)(指定最大长度):
- 接受连接:执行过listen的描述符,用来接受客户端发送的连接。成功后返回一个新的socket,可执行读写。
- 创建:
-
数据读写:
- 对文件的通用的read,write。
- socket API 提供的针对TCP协议的recv, send:flag提供了额外的控制选项
- 针对UDP协议的 recvfrom/sendto,因为无连接,所以需要加上地址。
-
获取地址信息函数:getsockname 获取 sockfd对应的本端 socket地址,getpeername获取远端的地址。
-
socket选项设置:
- 一些选项
- 强制使用time_wait的端口; 设置接收/发送缓冲区的大小; 关闭TCP连接的行为。
-
网络信息API:
- gethostbyname函数根据主机名称获取主机的完整信息,gethostbyaddr函数根据IP地址获取主机的完整信息。
- getservbyname函数根据名称取某个服务的完整信息, getservbyport函数根据口号获取某个服务的完整信息。
高级I/O函数
-
pipe()函数用于创建管道,实现进程间通信。
创建管道的两端。一端写,一端读,不能双向。默认是阻塞的。fd[0]读取,fd[1]写入。 -
socketpair()函数创建双向管道,即可读又可写。
-
dup()函数复制一个新的文件描述符,该新的文件描述符和原有的文件描述符指向相同的文件/管道或者网络连接。并且返回的文件描述符重视取系统当前最小的整数值。dup2则是返回一个不下于规定值的整数值。
比如我们要将标准输出输出到socket文件描述符,由于标准输出的描述符是1。所以我们先关闭标准输出,然后dup(connfd),那么返回1,所以connfd是1,变成了标准输出的文件描述符,那么标准输出则会输出到这里,则返回客户端。 -
readv() 和 writev() 函数:将文件符读到分散的内存块中,或将多块分散的内存的内容写入文件符。
-
sendfile()函数: sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。
out是读出,in是写入。out必须是一个socket,而in不能是socket或者pipe,只能是一个支持类似mmap函数的文件,指向真实的文件。函数设计为在网络上传输文件。 -
splice()函数
- 用于在两个文件描述符之间移动数据,也是零拷贝操作。其中一个描述符一定是管道。
- 用于在两个文件描述符之间移动数据,也是零拷贝操作。其中一个描述符一定是管道。
-
mmap()函数:
- 在<<深入理解计算机系统>>这本书中,mmap定义为:Linux通过将一个虚拟内存区域与一个磁盘上的对象(object)关联起来,以初始化这个虚拟内存区域的内容,这个过程称为内存映射(memory mapping)。
- 在mmap之后,只是在虚拟内存中分配了空间,并没有加载文件内容到物理内存,在你读的时候,这个虚拟内存页没有与物理页映射,那么产生一个“缺页”中断,然后操作系统再去加载到物理页并映射。
- 在写操作时,对于mmap和直接write,mmap在小文件的写入有点优势,在每次写入大小达到一个页的大小时,几乎没差别。
- 而在读操作时,mmap的优势明显,根据网上的测试,能在两个数量级的差距。因为mmap读取直接读取mmap的虚拟内容空间,而read则是要从内核空间拷贝到用户空间的buffer,多了一次拷贝。
-
fcntl()函数
- fcnt函数,正如其名字(file control)描述的那样,提供了对文件描述符的各种控制操作。可以写入设置或者读取设置。
-
其他Linux服务器程序用到的API:
- 日志信息:使用rsyslogd处理日志。
- 用户信息:API用于获取和设置当前进程的真实用户ID(UID),有效用户ID(EUID)GID,EGID等。
- 进程关系:进程组ID,会话设置等。
- 系统资源限制:获取资源限额API。
- 改变工作目录和根目录。
- 以守护进程在后台运行。