《linux高性能服务器编程》读书笔记 - Go语言中文社区

《linux高性能服务器编程》读书笔记


linux高性能服务器编程


从高到底的协议有:

应用层:ping(使用ICMP)、telnet(使用tcp)、OSPF(使用IP)、DNS(使用UDP

传输层:TCPUDP

网络层:ICMPIP

数据链路层:ARPRARP




tcpip链接建立断开的状态图:



tcp的头部:

16位的端口号:唯一标志一台主机上的进程

32位的序号:一次tcp通信过程中某一个传输方向上的字节流的每个字节的编号

32位的确认号:用作对另一方发送来的tcp报文段的响应。其值是收到的tcp报文段的序号值+1

4位头部长度:最大表示15,单位是4字节,头部最长是15*4=60字节

6位标志位:

URG标志:表示紧急指针是否有效

ACK标志:表示确认好是否有效。称携带ACK标志的tcp报文段为确认报文

PSH标志:提示接收端应用进程应该立即从tcp接受缓冲区中读走数据

RST标志:表示要求对方重新建立链接。我们称携带RST标志的tcp报文为复位报文段

SYN标志:表示请求建立一个链接。称携带SYN标志的报文段为同步报文段

FIN标志:表示通知对方本端要关闭了(即不再发送数据了,但是可以接受数据),称携带FIN标志的报文为结束报文段

16位窗口大小:是tcp流量控制的一个手段。这里说的窗口,指的是接收通告窗口(RWND)。它告诉对方本端的tcp接受缓冲区还能容纳多少字节数据,这样对方就可以控制发送数据的速度

16位校验和:校验tcp头部和tcp数据

16位紧急指针:是一个正的偏移量。它和序号字段的值相加表示最后一个紧急数据的下一个字节的序号。即,这个字段是紧急指针相对于当前序号的偏移。



tcp建立链接,三次握手:

客户端请求发起链接:SYN

服务器确认:SYNACK

客户端确认:ACK



tcp关闭链接,4次握手:

客户端请求关闭(不再发送数据,但是仍然可以接受数据):FIN

服务器确认(可以继续向客户端发送数据):ACK

进入半关闭状态(shutdown函数提供了半关闭状态的支持)

服务器端关闭(不再发送数据):FIN

客户端确认:ACK



对于链接超时,一般的情况下客户端会自动发起重连,如果重连若干次(一般是5次)仍然失败,那么通知应用程序超时



tcp状态转移总结:

服务器端:

1、服务器通过listen系统调用,进入LISTEN状态

2、服务器受到客户链接请求(同步报文段),旧把该链接放入内核等待队列,并向客户发送携带SYN标志的确认报文,此时该链接进入SYN_RCVD状态。如果此时服务器接收到客户端发来的确认报文,那么该链接就进入ESTABLISHED状态。ESTABLISHED状态是链接双方能够进行双向数据传输的状态。

3、当客户主动关闭链接时(通过closeshutdown发送结束报文),服务器返回确认报文,链接进入CLOSE_WAIT状态。这个状态的含义就是等待服务器进程关闭链接,等服务器发送数据完毕,服务器向客户端发送结束报文(FIN),链接进入LAST_ACK状态,等待客户端对结束报文的最后一次确认。接收到确认之后链接进入CLOSED状态。


客户端:

1、客户端通过connect调用向服务器发起链接请求,发送一个SYN报文,链接进入SYN_SENT状态

2、如果connect链接的目标端口不存在,或者未被监听、或者端口仍然处于TIME_WAIT状态的链接占用,则服务器给客户端发送一个复位报文,connect失败;端口存在,但是超时,connect失败。connect失败,链接进入CLOSED状态。

3、如果connect链接成功,链接进入ESTABLISHED状态(双方开始发送数据)

4、如果客户端汉族动关闭链接,向服务器发送一个FIN报文,链接进入FIN_WAIT_1状态,如果客户端接受到服务器发送过来的FIN确认报文,那么链接进入FIN_WAIT_2状态,当客户端处于FIN_WAIT_2状态时,服务器处于CLOSE_WAIT状态,这个状态就是可能发生半关闭的状态。

5、服务器向客户端发送FIN报文,客户端接受之后向服务器发送确认,链接进入TIME_WAIT状态。此时服务器的状态是LAST_ACK,直到接收到确认。客户端等待2MSL的时间之后进入CLOSED状态。



重点讨论的TIME_WAIT状态:

客户端接受到服务器的FIN报文之后并不直接进入CLOSED状态。而是进入TIME_WAIT状态,等待2MSL的时间,才进入CLOSED状态。MSL的大小是2min

TIME_WAIT状态存在的原因是:

1、可靠的终止TCP链接:如果客户端确认服务器结束报文(FIN)的报文段丢失,那么服务器就会重发FIN报文,此时客户端要停留在某个状态以处理重复受到的结束报文段(这种处理就是向服务器发送确认报文段)。如果不进入TIME_WAIT而是直接进入CLOSED状态,那么客户端受到重复的FIN报文时,会向服务器发送复位报文段,服务器会认为这是一个错误,这不是它期望的。所以要有TIME_WAIT状态。

2、让迟到的tcp报文段有足够的时间被识别并被丢弃。linux系统上,一个端口不能被打开多次,当一个链接处于TIME_WAIT状态时,我们无法立即使用该端口来建立一个新的链接,这个新的、和原来相似的链接称为原来的链接的化身。新链接可能收到属于旧链接的报文段(例如迟到的数据报文或者重复的FIN报文),这显然不是新链接的的数据,是不应该发生的。

3、另外,因为TCP报文段的最大生存时间是MSL,所以坚持3MSL的时间的TIME_WAIT状态能够确保网络上两个传输方向上尚未被接收到、迟到的报文都已经消失。所以,一个新的链接可以在2MSL之后安全的建立,而绝不会收到属于原来链接的数据。


可以通过socket选项SO_REUSEADDR来强制进程立即使用处于TIME_WAIT状态的端口。


Nagle算法:

它要求tcp链接的通信双方在任意时刻都最多只能发送一个未被确认的tcp报文段,在该报文段的确认未到达之前不能发送其他的tcp报文;另一方面,发送方在等待确认的同时收集本端需要发送的微量数据,并在确认到来时以一个tcp报文将它们全部发出去,这样就能够大大减少网络上微小的tcp报文段的数量。

该算法的另一个优点是其适应性:确认到达越快,数据就发送的越快


带外数据(OOB):

OOB比普通数据有更高的优先级别。一般tcp中使用紧急指针实现紧急数据传送,OOB的大小只有一个字节,如果你写入了多个字节,那么只有最后一个字节被当作带外数据,其他的数据被当作普通数据。

接受到带外数据的一端必须迅速将带外数据取出,否则会被后来的数据覆盖。接收到带外数据的一段会把带外数据取出存放到一个特殊的缓冲区,如果设置了SO_OOBINLINE选项,那么带外数据将会和普通数据一起存放,此时需要通过紧急指针来识别带外数据




拥塞控制:

包含了四个部分:慢启动、拥塞避免、快速重传、快速恢复

一些概念:SWND发送窗口、RWND接收通告窗口、CWND拥塞窗口,实际的SWNDCWNDRWND中较小的一个


慢启动通常和拥塞避免一起使用:

先成倍的增加窗口大小,到达阈值之后,开始慢慢增长(慢启动)


快速重传和快速恢复通常一起使用:

如果发送端连续收到三个重复的确认报文,就认为拥塞发生了,然后启动快速重传(即立即重传丢失的报文段),然后重新设置阈值大小,从阈值开始慢慢增长窗口(即快速恢复)












网络字节顺序:大端字节顺序

主机字节顺序:目前pc很多都是小端字节顺序


主机字节和网络字节的转换函数:

htol——主机转网络long

htos——主机转网络short

ntol——网络转主机long

ntos——网络转主机short


通用地址结构

soakaddr



协议族和地址族中两者的值完全相同,所以两者常混用



还有一个通用的地址结构:sockaddr_storage,不过用的不是很多


三种地址结构(包括地址和端口):

unix本地地址:sockaddr_un

ipv4地址:sockaddr_in

ipv6地址:sockaddr_in6



ipv4地址结构中还包含了ipv4的地址:in_addr

ipv6地址结构还包含了ip6的地址:in6_addr



ip字符串和地址之间的转换:

1、字符串转地址:

inet_addr

inet_aton

inet_pton(建议使用这个函数)


2、地址转字符串:

inet_ntoa(不可重入)

inet_ntop(建议使用这个)



地址信息函数:

getsockname(获取本端socket地址)

getpeername(获取远端socket地址)



socket函数中:domainPF_INETPF_INET6PF_UNIX

type是:SOCK_STREAMSOCK_DGRAM

protocol是:0

该函数失败返回-1,并设置errno



bind函数:

成功返回0,失败返回-1并设置erron,常见的erron是:EACCES表示没有权限访问该端口;EADDRINUSE表示地址正在被使用


listen函数:

成功返回0,失败返回-1并设置erron值。这个才是真正被动接受客户链接的函数


accpet函数:

失败返回-1,并设置erron值。注意accpet函数只是从监听队列中取出链接,而不论链接处于何种状态,也不关心网络的变化


connect函数:

成功返回0,失败返回-1并设置erron。常见的erron有:ECONNREFUSED,链接被拒绝,一般是端口不存在;ETIMEDOUT,链接超时


shutdown函数:

3howto的可选值:SHUT_RDSHUT_WRSHUT_RDWR


sendrecv函数的flags选项:



flags只对当前sendrecv调用有效,如果想一直有效,可以设置套接字选项



recvmsgsendmsg适用于tcpudp

recvsend适用于tcp

recvfromsendto适用于udp



sockatmark函数判断套接字是否处于带外标记,如果处于表示有带外数据到来



套接选项:


网络信息api

gethostbyname根据主机名返回主机的完整信息(返回hostent主机信息结构)

gethostbyaddr根据ip返回主机的完整信息(返回hostent主机信息结构体)



getservbyname根据名称获取某个服务的完整信息(返回servent主机信息结构)

getservbyport根据端口号获取某个服务的完整信息(返回servent主机信息结构)


getaddrinfo既能通过主机名获取ip地址又可以通过服务名获取端口号(返回addrinfo结构)

调用之后需要调用freeaddrinfo函数来释放内存


getnameinfo根据socket地址同时获取字符串表示的主机名和服务名



linux的错误码转换成字符串:strerrorgai_strerror



高级io函数


包括:

1、用于创建文件描述符的函数:pipe/socketpairdup/dup2函数

2、用于读写的函数:redv/writevsendfilemmap/munmapsplicetee函数

3、用于控制io行为和属性的函数:fcntl


pipe函数,用于创建一个管道,函数成功返回0,并得到一对文件描述符;失败返回-1.

父进程从fd0中读取数据,将数据写到fd1中。


sockaetpair函数能够方便的创建双向管道


dup函数用于复制文件描述符,复制得到的文件描述符和原来的文件描述符指向相同的文件,但是文件描述符的值不同


dup2dup类似,不过它指定将原文件描述符复制为指定的(未被使用)的文件描述符


readv从文件描述符中将数据读取到分散的内存块中,即分散读


writev将多块分散的内存数据一并写到文件描述符中,即集中写


sendfile函数在两个文件描述符之间直接传输数据(完全在内核中操作),效率很高


mmap/mnumap函数用于创建共享内存,将文件映射到内存中,就如同内存数据一样操作文件


splice函数用于两个文件描述符之间移动数据,也是0拷贝操作,效率很高


tee函数在两个管道之间复制数据,0拷贝操作,效率高


fctnl函数对文件描述符提供了各种操作



linux服务器程序规范


一些规范:

1、服务器程序一般以后台进程(也叫守护进程)运行。

2、服务器程序一般有一套日志系统

3、服务器程序一般以某个专门的非root身份运行

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/NB_vol_1/article/details/49682847
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

  • 发表于 2020-03-01 23:25:03
  • 阅读 ( 674 )
  • 分类:Linux

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢