分析服务器客户端通信过程
通过上篇我们大体上学习到了服务器与客户端编程的步骤。
现在我们来具体分析一下他们之间进行通信时都发生了什么?
服务器 | 客户端 |
---|---|
服务器创建套接字 | 客户端创建套接字 |
创建服务器地址信息结构体绑定IP地址端口号等 | 创建服务器地址信息结构体绑定IP地址端口号等 |
服务器绑定套接字信息,指定地址信息结构体 | |
服务器创建监听队列 | |
客户端请求与服务器连接 | |
服务器接受到客户端请求连接信息并与之成功连接 | |
发送信息 | |
接收信息 | |
发送给客户端表示自己接受信息成功 | |
接受信息 |
前面的创建套接字,创建地址信息结构体就不说了。
从服务器创建监听队列开始说,监听队列是什么,首先监听队列分为未完成三次握手队列和已完成三次握手队列。
那么什么叫三次握手,三次握手发生在哪个阶段,三次握手具体又做了什么?
因为TCP是一种面向连接的 可靠的 流式服务,所以需要通过三次握手来建立连接,在谢希仁的《《计算机网络》》中,对于三次握手的目的式这样描述的:是为了防止已失效的连接请求报文段突然又传送到了服务端而造成错误。
三次握手过程
服务器 <<<-- (SYN seq=i)--------------- 客户端
服务器 —(SYN seq=j ack=i+1)–>>> 客户端
服务器 <<<----(ack=j+1)------------------ 客户端
1.首先客户端发送一个SYN段,并指明自己的初始序列号,即ISN©.
2.服务器发送自己的SYN作为应答,并且同样指明自己的初始序列号ISN。为了确认客户端的SYN,服务器就将客户端的ISN+1作为ACK的值,这样每发送一个SYN,序列号就会加1.而如果有丢失的情况,就会重传。
3.为了确认服务器的SYN,客户端就将服务器的ISN+1作为返回的ACK值。
清楚了三次握手原理后,我们继续分析服务器客户端网络通信。
客户端发送一个连接请求connect后,也就意味着完成了三次握手,就会被写入到服务器listen中的完成三次握手的监听队列中。
而服务器中的accept是等待listen建立的完成三次握手的队列的,队列中没有就阻塞住,有就与相应的客户端连接。
服务器中accept完成后会阻塞到recv,直到客户端发送数据给服务器,服务器接收到数据后,可以send发送一个确认信息给客户端。
而在recv,send中,实际上只是接收到或者发送到缓冲区中:
客户端写入数据到自己的发送缓冲区,然后通过套接字发送到服务器的接受缓冲区中。
服务器接收数据到自己的接受缓冲区,回复确认消息时,也是先写到自己的发送缓冲区,然后再由套接字发送到客户端的接收缓冲区。
因此我们就可能存在几种问题:
1.假设在服务器与客户端循环发送接受条件下,如果我们设置服务器recv每次只接受一个数据,那么当客户端发送一序列数据的时候,服务器只读一个后,发送一个的确认信息客户端,客户端接收到确认信息后阻塞到下一次输入数据,而服务器因为接受缓冲区中还有数据,就会循环接受数据,并且每次给客户端一个确认信息,但是此时客户端阻塞在下次输入数据,因此所有由服务器发送的确认信息都会被写入到客户端的接收缓冲区中,就会导致下一次输入数据时,会从接收缓冲区读到上一次数据的确认信息,而本次的确认信息也会在下一次才能读到。
2.TCP的粘包问题
什么叫粘包呢?就是假设服务器直接连着send发送了三次数据,而写入到发送缓冲区后可能会写入到一个包中,客户端在读取的时候就会当成时一个数据来读。
那么服务器第二个第三个的数据的recv就会阻塞住,而客户端还在等待服务器的确认信息就也会阻塞住,导致客户端与服务器都阻塞。
send (hello) recv (helloabctest)
send (abc) recv 阻塞
send (test) recv 阻塞
recv(阻塞) send
那么如何解决这样的问题呢?很简单。
1.我们只需要保证每次只send发送一次,在接受到确认信息后再进行下一次发送。
2.给信息加上特有的标志,那么即使 粘包问题发生之后,我们仍然可以让客户端根据这些标志来区别信息。
知道这些后我们再谈谈TCP协议的可靠性。
1.应答确认
2.超时重传
3.滑动窗口
首先,TCP时面向连接的,有应答确认,那么当客户端发送数据之后,如果中途数据丢失了,那么确认信息就不能再一定时间内返回给客户端,那么客户端就会启动超时重传机制,重新在发送一次,即使是数据中途没有丢失,是因为服务器收到了数据,但没有及时返回,超出了时间,那么客户端仍然会重新发送一次数据,而服务器收到后,有相同的数据,那么就会自动删除一个以保证数据的准确性。滑动窗口:比如我们设置一次发送5个数据,那么后续的数据就要阻塞等待著,那么这5个数据被发送后,客户端可能先收到了一个数据,而其他的数据还在路上,则此时服务器就可以不必等待,可以再发一个数据,以此来提高速率。