社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
linux高性能服务器编程
从高到底的协议有:
应用层:ping(使用ICMP)、telnet(使用tcp)、OSPF(使用IP)、DNS(使用UDP)
传输层:TCP、UDP
网络层:ICMP、IP
数据链路层:ARP、RARP
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
服务器确认:SYN,ACK
客户端确认:ACK
tcp关闭链接,4次握手:
客户端请求关闭(不再发送数据,但是仍然可以接受数据):FIN
服务器确认(可以继续向客户端发送数据):ACK
进入半关闭状态(shutdown函数提供了半关闭状态的支持)
服务器端关闭(不再发送数据):FIN
客户端确认:ACK
对于链接超时,一般的情况下客户端会自动发起重连,如果重连若干次(一般是5次)仍然失败,那么通知应用程序超时
tcp状态转移总结:
服务器端:
1、服务器通过listen系统调用,进入LISTEN状态
2、服务器受到客户链接请求(同步报文段),旧把该链接放入内核等待队列,并向客户发送携带SYN标志的确认报文,此时该链接进入SYN_RCVD状态。如果此时服务器接收到客户端发来的确认报文,那么该链接就进入ESTABLISHED状态。ESTABLISHED状态是链接双方能够进行双向数据传输的状态。
3、当客户主动关闭链接时(通过close、shutdown发送结束报文),服务器返回确认报文,链接进入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拥塞窗口,实际的SWND是CWND和RWND中较小的一个
慢启动通常和拥塞避免一起使用:
先成倍的增加窗口大小,到达阈值之后,开始慢慢增长(慢启动)
快速重传和快速恢复通常一起使用:
如果发送端连续收到三个重复的确认报文,就认为拥塞发生了,然后启动快速重传(即立即重传丢失的报文段),然后重新设置阈值大小,从阈值开始慢慢增长窗口(即快速恢复)
网络字节顺序:大端字节顺序
主机字节顺序:目前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函数中:domain是PF_INET、PF_INET6、PF_UNIX
type是:SOCK_STREAM、SOCK_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函数:
有3中howto的可选值:SHUT_RD、SHUT_WR、SHUT_RDWR
send和recv函数的flags选项:
flags只对当前send、recv调用有效,如果想一直有效,可以设置套接字选项
recvmsg和sendmsg适用于tcp和udp
recv和send适用于tcp
recvfrom和sendto适用于udp
sockatmark函数判断套接字是否处于带外标记,如果处于表示有带外数据到来
套接选项:
网络信息api:
gethostbyname根据主机名返回主机的完整信息(返回hostent主机信息结构)
gethostbyaddr根据ip返回主机的完整信息(返回hostent主机信息结构体)
getservbyname根据名称获取某个服务的完整信息(返回servent主机信息结构)
getservbyport根据端口号获取某个服务的完整信息(返回servent主机信息结构)
getaddrinfo既能通过主机名获取ip地址又可以通过服务名获取端口号(返回addrinfo结构)
调用之后需要调用freeaddrinfo函数来释放内存
getnameinfo根据socket地址同时获取字符串表示的主机名和服务名
linux的错误码转换成字符串:strerror和gai_strerror
高级io函数
包括:
1、用于创建文件描述符的函数:pipe/socketpair、dup/dup2函数
2、用于读写的函数:redv/writev,sendfile、mmap/munmap、splice和tee函数
3、用于控制io行为和属性的函数:fcntl
pipe函数,用于创建一个管道,函数成功返回0,并得到一对文件描述符;失败返回-1.
父进程从fd0中读取数据,将数据写到fd1中。
sockaetpair函数能够方便的创建双向管道
dup函数用于复制文件描述符,复制得到的文件描述符和原来的文件描述符指向相同的文件,但是文件描述符的值不同
dup2和dup类似,不过它指定将原文件描述符复制为指定的(未被使用)的文件描述符
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
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!