网络编程/tcp协议分析

时间:2021-07-23 10:25:22

一、链接的建立与终止

 TCP Server和Client模型:

网络编程/tcp协议分析

1、bind函数相关

1 #include <sys/types.h>          /* See NOTES */
2 #include <sys/socket.h>
3
4 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

作用是绑定addr中的<IP:PORT>, 对于tcp server一般port是固定的。当系统有多个IP(多网卡)时,把ip设置为INADDR_ANY, 内核会自动分配ip。如果只有单一的IP,选择

机器IP和INADDR_ANY效果是一样的。

客户和服务器通过调用函数bind时可以指定IP地址或端口号,可以都指定,也可以都不指定:

网络编程/tcp协议分析


l 客户端
1. TCP客户端:

1) 当TCP客户未绑定IP地址,当它调用connect时内核会根据外出接口给它绑定一个IP地址和一个临时端口号。并且TCP服务器在接到这个连接后会以这个IP地址作为回应数据报的目的IP地址。

2) 当TCP客户绑定了IP地址,它就为发出的数据连接指定了一个源IP地址,并且TCP服务器在接到这个连接后会以这个IP地址作为回应数据报的目的IP地址。

3) TCP客户只能根据四元组(原端口号,原IP地址,目的端口号,目的IP地址)接受数据报。

2. UDP客户端:

1) 当UDP客户未绑定IP地址,当它调用sendto时内核会根据外出接口给它绑定一个IP地址和一个临时端口号。(UDP客户可以接收到达它绑定的临时端口的任何UDP数据报)。

2) 当UDP客户绑定了IP地址,它就为发出的数据报指定了一个源IP地址,并且UDP服务器在接到这个数据报后会以这个IP地址作为回应数据报的目的IP地址。(UDP客户只能接收到达它绑定的临时端口并且目的地址为它绑定的IP地址的UDP数据报)。

3) 当UDP客户调用connect,内核记录下对方的IP地址和端口号,它们包含在传递给connect的套接口地址结构中,并为UDP客户绑定了一个临时端口号和IP地址。(UDP客户只能接收目的IP地址为它绑定的IP地址和端口号并且源IP地址为它指定对方的IP地址和端口号的数据报)。
关于 UDP中客户端调用connect的好处,还有一个作用是能够捕获错误
当UPD客户端像服务器发送一个不存在的端口请求,服务器会像客户端发送ICMP端口不可达消息。不调用connect时,对于返回的ICMP响应,协议栈不知道该传递给上层的哪个应用,所以客户端进程中捕获不到相应的错误。
如果 UDP客户端connect UDP服务器<IP:PORT>,在调用时其实没有向外发包,只是在内核协议栈中记录了该状态。发生错误时本机收到ICMP消息,回来的ICMP不可达的响应能够被协议栈处理,通知客户端进程(因为内核已经知道该PORT的消息对应该服务);当客户端再次对该fd进行操作时,比如读数据时,read等调用会返回一个错误。
而在两种情况下,write或者sendto操作都是把数据放到协议栈的发送队列之后就返回成功,而相应的ICMP回应则要等数据到达对端后才能返回,所以通常这种情况叫做“异步错误”。


l 服务器端
1. TCP服务器:

1) 当TCP服务器绑定通配IP地址,套接口会接收到达它绑定端口的任何TCP连接。并以接收的目的IP地址作为它的源IP地址(用以确定四源组),以接收的源IP地址作为它的目的IP地址发回应答。

2) 当TCP服务器绑定本地IP地址,这就限制了套接口只接收到达它绑定端口并且目的地址为此IP地址的客户连接。以绑定的目的IP地址作为源IP地址(当然,绑定的IP地址肯定与接收连接的目的IP地址相同,否则它不会接收),并以接收的源IP地址作为它的目的IP地址发回应答。
一般只有在本机有多个网卡(多IP)才有效。如服务器SERVER有IP地址IPA和IPB,服务bind了IPA,那么发送给IPB的数据SERVER不会接收。如果是bind IPADDR_ANY, 则发到该服务器上的请求都会接受。

2. UDP服务器:

1) 当UDP服务器绑定通配IP地址,套接口会接收到达它绑定端口的任何UDP数据报。并以数据报的外出接口的主IP地址为源IP地址,以接收到的源IP地址作为它的目的IP地址发回应答。

2) 当UDP服务器绑定本机IP地址,这就限制了套接口只接收到达它绑定端口并且目的地址为此IP地址的UDP数据报。并以绑定的IP地址作为源IP地址,以接收的源IP地址作为它的目的IP地址发回应答。

3) 当UDP服务器调用connect,内核记录下对方的IP地址和端口号,它们包含在传递给connect的套接口地址结构中,并为UDP服务器绑定了一个临时端口号和IP地址。(UDP服务器只能接收目的IP地址为它绑定的IP地址和端口号并且源IP地址为它指定对方的IP地址和端口号的数据报)。

 2、connect函数

1  #include <sys/types.h>          /* See NOTES */
2 #include <sys/socket.h>
3
4 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三次握手,而这个连接的过程是由内核完成,不是这个函数完成的,这个函数的作用仅仅是通知 Linux 内核,让 linux 内核自动完成 TCP 三次握手连接,最后把连接的结果返回给这个函数的返回值(成功连接为0, 失败为-1)。

通常的情况,客户端的 connect() 函数默认会一直阻塞,直到三次握手成功或超时失败才返回(正常的情况,这个过程很快完成)。

关于UDP中客户端调用connect的好处,还有一个作用是 能够捕获错误
当UPD客户端像服务器发送一个不存在的端口请求,服务器会像客户端发送ICMP端口不可达消息。不调用connect时,对于返回的ICMP响应,协议栈不知道该传递给上层的哪个应用,所以客户端进程中捕获不到相应的错误。
如果UDP客户端connect UDP服务器<IP:PORT>,在调用时其实没有向外发包,只是在内核协议栈中记录了该状态。发生错误时本机收到ICMP消息,回来的ICMP不可达的响应能够被协议栈处理,通知客户端进程(因为内核已经知道该PORT的消息对应该服务);当客户端再次对该fd进行操作时,比如读数据时,read等调用会返回一个错误。
而在两种情况下,write或者sendto操作都是把数据放到协议栈的发送队列之后就返回成功,而相应的ICMP回应则要等数据到达对端后才能返回,所以通常这种情况叫做“异步错误”。

3、

 

listen() 函数的主要作用就是将套接字( sockfd )变成被动的连接监听套接字(被动等待客户端的连接), listen()函数不会阻塞,它主要做的事情为,将该套接字和套接字对应的连接队列长度告诉 Linux 内核,然后,listen()函数就结束。

这样的话,当有一个客户端主动连接(connect()),Linux 内核就自动完成TCP 三次握手,将建立好的链接自动存储到队列中,如此重复。

所以,只要 TCP 服务器调用了 listen(),客户端就可以通过 connect() 和服务器建立连接,而这个连接的过程是由内核完成

关于backlog,Linux内核中维护两个队列:SYN_queue(SYN报文接收)和 ACCEPT queue(Established, Server收到ACK,三次握手完成)

a) When a connection request arrives (i.e., the SYN segment), the system-wide parameter net.ipv4.tcp_max_syn_backlog is checked (default 1000).

If the number of connections in the SYN_RCVD state would exceed this threshold, the incoming connection is rejected 

客户端connect发送SYN请求,服务端内核先检查是否超过了SYN,没有超过则加入到SYN queue,服务器的 SYN 响应,其中稍带对客户 SYN 的 ACK(即SYN+ACK)

b)Each listening endpoint has a fixed-length queue of connections that have been completely accepted by TCP (i.e., the three-way handshake is complete) but not yet accepted by the application 

Accept queue中的tcp连接是内核建立完三次握手,但是应用程序还没有调用accept取出链接。这个队列的大小就是backlog

The application specifies a limit to this queue, commonly called the backlog. This backlog must be between 0 and a system-specific maximum called net.core.somaxconn,

inclusive (default 128)

backlog has no effect whatsoever on the maximum number of established connections allowed by the system, or on the number of clients that a concurrent server can handle concurrently. 

accept queue和最大tcp连接数无关

网络编程/tcp协议分析

c)If there is room on this listening endpoint’s queue for this new connection, the TCP module ACKs the SYN and completes the connection. The server application with the listening endpoint does not see this new connection until the third segment of the three-way handshake is received.

如果SYN队列未满,TCP接收SYN请求并返回SYN的ACK,服务器并不感知这一过程。

Also, the client may think the server is ready to receive data when the client’s active open completes successfully, before the server application has been notified of the new connection. If this happens, the server’s TCP just queues the incoming data. 

客户端在Connect返回成功后(三次握手完成,链接已经进入服务端的Accept队列),就认为服务端准备好接收数据。此时服务端可能没有调用Accept函数把链接从Accept队列取出。此时服务端tcp内核只是把数据存入缓冲区。

d) If there is not enough room on the queue for the new connection, the TCP delays responding to the SYN, to give the application a chance to catch up .it persists in not ignoring incoming connections if it possibly can .

如果SYN队列满,TCP延迟响应客户端SYN请求。此时客户端会超时重试。

If the net.ipv4.tcp_abort_on_overflow system control variable is set, new incoming connections arereset with a reset segment. 

如果服务端开启net.ipv4.tcp_abort_on_overflow,该SYN请求会被拒绝,服务端发送RST报文。

 

二、select/epoll区别