CS144-Lab4-TCPConnection

lab 地址 :lab4-doc
代碼實現:lab4-code
完整目錄:
0. ByteStream
1. StreamReassembler
2. TCPReceiver
3. TCPSender
4. TCPConnection
5. ARP
6. IP-Router

1. 目標

lab4 的目標是結合 TCPSenderTCPReceiver,實現 TCPConnection,完成了 TCPConnection 後 TCP 部分的內容就基本完成了。

image

TCPSenderTCPReceiver 已經完成了基本的收發包操作,支持了定時重傳,亂序包重組等特性,接下來需要支持 TCPConnection 的 連接發起/關閉 等功能,根據 TCPConnection 的不同狀態,在發包時調整 TCPSegment 的 header flag(ackno,win,rst 等)

這裏需要注意的是,lab4 需要在官方提供的 VM 實驗環境下運行,否則 ipv4 部分會 timeout ,無法執行通過

2. 實現

這裏將 TCPConnection 的幾個實現重點劃分爲如下:

  • 發起連接
  • 寫入數據,發送數據包
  • 接收數據包
  • 關閉連接
  • 豐富超時重傳機制

2.1 發起連接

發起連接這個功能 TCPSender 基本已經實現了,只要主動調用 TCPSender::fill_window ,就可以填充一個 SYN 包。

void TCPConnection::connect() {
	_sender.fill_window();
	flush_send(); // 將 sender 的 segment push 到 connection 的發送隊列中
}

2.2 發包

寫入數據,發包的流程也相對比較簡單,就是將數據寫入 TCPSenderByteStream 中,然後填充窗口發送即可。

size_t TCPConnection::write(const string &data) {
	size_t write_size = _sender.stream_in().write(data);
	_sender.fill_window();
	flush_send(); // 將 sender 的 segment push 到 connection 的發送隊列中
	return write_size;
}

2.3 接收數據包

接收數據包的流程主要如下:

  • 調用 TCPReceiver::segment_received 接收數據包
  • 如果是 ack 包,調用 TCPSender::ack_received 更新對端窗口 size 和 對端確認情況。
  • 如果數據包有數據(沒數據不需要回復,防止無限 ack),需要回復一個 ack 包
  • 檢查連接關閉情況(2.4 展開)
void TCPConnection::segment_received(const TCPSegment &seg) {
	_time = 0;
	// if the rst (reset) flag is set, sets both the inbound and outbound streams to the error state and kills the connection permanently
	if (seg.header().rst) {
		unclean_shutdown();
		return;
	}
	
	_receiver.segment_received(seg);
	if (seg.header().ack) {
		_sender.ack_received(seg.header().ackno, seg.header().win);
	}
	
	// make sure ack
	if (seg.length_in_sequence_space() > 0) {
		_sender.fill_window();
		if (_sender.segments_out().empty()){
			_sender.send_empty_segment();
		}
	}

	flush_send();
	clean_shutdown();
}

2.4 連接關閉

連接關閉的情況比較複雜,主要分爲 clean shutdown 和 unclean shutdown。

unclean shutdown

unclean shutdown 的情況比較簡單,當收到 TCPSegmentheader.rst 標記爲 true 的情況下,此時可以直接關閉連接,同時需要設置 inbound stream 和 outbound stream 爲 error 狀態。這種方式關閉 TCPConnection 就稱爲 unclean shutdown

inbound stream 指的是 receiver 的 ByteStream
outbound stream 指的是 sender 的 ByteStream

clean shutdown

clean shutdown 有 3 個前提條件:

  1. 本地 inbound stream 的 unassembled_bytes == 0 (所有字符串都組裝完畢)並且已經 end_input ,也就是收到了 Fin 報文。
  2. 本地 outbound stream 已經完全發送完畢(eof == true
  3. 對端成功收到所有 outbound stream 發送的數據包對應的 ack (注意是對端)
    當滿足 條件 1 後,根據條件 3 是否百分百成立,會出現兩種情況:
  • lingering after both streams end
    條件 2 滿足,本地發送的數據已經全部收到 ack,並且已經成功接收到對端的所有數據,此時還需要等待一段時間,因爲接受到數據後發送給對端的 ack 可能會丟失,如果直接關閉的話,對方可能會一直嘗試重發數據包。
  • passive close
    條件 2 未滿足,而條件 1 成立的時候,條件 3 也就確定成立了,這樣等到條件 2 滿足時,就可以直接關閉連接。
    爲什麼條件 2 未滿足,而條件 1 成立的時候,條件 3 也就確定成立了?這裏是結合了 TCPSegment 的可靠性來實現的。由於普通的 ack 包不會重傳,那麼只要讓它帶上數據就可以了。當條件 2 未滿足的情況下,說明還有數據需要傳輸(最起碼有個 Fin 包),也就是說後續的包都是可靠的,且會帶上 ackno,那麼對端在收到包的時候,就能確定自己發送的包都被成功接收了。
    那麼此時條件 3 也就滿足了,當條件 2 也滿足的時候,就能直接關閉了。

3. 測試

測試過程中犯的幾個錯誤:

  • 沒有按照對端窗口大小發包,發了額外的數據包,對端丟棄了,導致需要重傳,進而導致 test case 的執行時間超時了。
  • 超時時間等配置需要讀取 TCPConnection::cfg 的配置變量,而不是直接讀取 TCPConfig 中的靜態常量,test case 中會動態調整 cfg 的配置,不使用 cfg 在某些情況下會導致超時(配置的重傳時間比 TCPConfig 中的短)

image

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章