专注于嵌入式和图像处理
分类: C/C++
2013-09-13 10:24:24
整个过程可简述为如下五步:
① 服务器端,通过调用socket、bind、listen等待客户端的连接。
② 客户端调用socket后,调用connect主动连接,它向服务器发送一个连接请 求报文段,该报文段中TCP首部中的同步位SYN(Synchronize)=1,同时选择 一个初始序列号seq=J,一般来说,SYN连接请求报文段不携带数据,只包 含一个IP头部、TCP头部及可能有的TCP选项信息。但是,它要消耗一个 序列号。然后客户端进程进入SYN_SENT(同步已发送)状态。
③ 服务器端进程接收到客户端的SYN连接请求报文段后,它向客户端发送一 个确认报文段,该报文段中TCP首部中的同步位SYN和确认位ACK都为1, 确认号ack=J+1(表示J报文段已经收到,下次要接收的报文段序号为J+1), 同时它也要选择一个初始序列号seq=K,同样该报文段一般也不携带数据, 要消耗一个序列号。此时服务器端进程进入SYN_RECV(同步收到)状态。
④ 客户端收到服务端确认报文段后,它再想客户端发送一个确认报文段,该报 文段中ACK位为1,确认号ack=K+1,该报文段序列号seq=J+1。TCP标准 规定,该报文段可以携带数据,但若不携带数据则不消耗序列号,即此时, 下一个报文段序列号还是J+1,这是客户端进入ESTABLISHLED(已建立连 接)状态。
⑤ 服务器端收到客户端的确认后,也进入ESTABLISHLED状态。
1111111
好了,对于上面的分析我们告一段落,现在再来讨论一个问题,也是面试的时候,经常会被问到的.
问题1:假如第三次客户端向服务器端发送的ACK的丢失,会出现什么情况?
问题分析:第三次客户端向服务器端发送的ACK丢失,此时客户端处于ESTABLISHLED状态,但服务器端处于SYN_RECV状态。此时可能会出现两种情况:
(1) 因为客户端处于ESTABLISHLED状态,所以它认为连接已经建立了可以发送数据了,此时如果它发送数据给服务端,但是由于服务端没有接收到ACK,它还处于SYN_RECV状态,这个时候服务器端接收到客户端发来的数据,将回应RST报文,告之Server端错误。
(2) 对于Server端,它在一段时间内没有接收到ACK报文,它将重新发送SYN+ACK报文段,以便Client端再次发送ACK报文。
问题2:TCP建立连接的时候,为什么非要用三次握手,而只用两次可以吗?
问题分析:问题意思就是说,不用第三次客户端发给服务端的ACK确认报文,即当服务器端接收到客户端的SYN报文时,服务器端就进入ESTABLISHLED状态,然后向客户端发送SYN+ACK报文,当客户端接收到该报文时,客户端也进入ESTABLISHLED状态。然后两种就可以发送数据了。咋一看,这样似乎没有问题,但是假如服务端向客户端发送的SYN+ACK报文丢失,会出现什么情况?此时Server端进入ESTABLISHLED状态,值得注意的是,进入这个状态,代表该端认为连接建立成功,它就会分配资源,以为接下来数据传输做准备。对于Client端,由于它没有接收到ACK,它会以为上次发送的SYN报文丢失了,会再次重发SYN报文,此时,Server端又会分配资源,造成资源的浪费。但是我并不认为这个理由能站住脚,因为我觉得Server端在再次接收相同的Client端的SYN时,它完全可以根据报文的头部知道该SYN为上次同一Client端发送过来的,它可以不重新分配资源呀,它就会想,我这边对你的连接已经建立成功了呀,你为什么还发SYN来呢,那肯定是因为我刚刚发给你的SYN+ACK报文丢失了,那我重新给你发一次吧,由于它知道是同一个Client端发来的,它完全可以不重新分配资源呀。我看网上还有一种解释,如下:
具体可参考链接:http://blog.chinaunix.net/uid-20665047-id-3137792.html
void Sift(A[],int n,int k) { int i = k; int j = 2*i; while(j