TCP/IP網絡模型
最上層的是應用層,也就是我們日常可以接觸到的,它會給數據添加對應的頭部,并傳輸給傳輸層,應用層是我們日常會接觸到的,比如HTTP,FTP,Telnet,DNS,SMTP。HTTPS默認端口是443.
傳輸層有兩個協議:TCP和UDP,TCP常用于網絡請求等可靠性要求高的場景下,UDP的特點是速度快,但可靠性較低,常用于VPN,域名查詢的場景下。
TCP建立連接需要三次握手,客戶端發送連接請求,服務器回應請求,客戶端再次發送確認連接的請求。
客戶端 ------SYN------>服務器(SYN=1,ACK=0,此時客戶端還未連接,正發起連接請求)
客戶端 <---SYN+ACK---服務器(SYN=1,ACK=1,此時服務器收到了連接請求,同意建立連接)
客戶端 ------ACK------>服務器(SYN=0,ACK=1,連接已建立無需發送SYN去建立,只需發送ACK進行確認)
TCP斷開連接需要四次揮手,客戶端發送斷開連接的請求,服務器回應請求表示可以,之后服務器再次發送斷開連接的確認請求,客戶端確認該請求。
客戶端------FIN------>服務器(FIN=1,ACK=0,客戶端發起斷開連接的請求)
客戶端<------ACK------服務器(FIN=0,ACK=1,服務器收到請求,向客戶端確認請求,但還有部分數據未傳完暫不斷開)
客戶端<------FIN------服務器(FIN=1,ACK=1,此時服務器的數據全部傳輸完成,可以斷開連接)
客戶端------ACK------>服務器(FIN=0,ACK=1,客戶端確認收到了服務器的確認信息)
數據報文
數據報文是一次網絡傳輸的基本單位,它包含多個部分:序列號,確認號,窗口,保留位,校驗和。序列號用來打標記保證順序是正確的,因為一次請求會被拆分為多個報文,確認號是告知接收方回應的標記,做到一一對應,保留字段包括剛剛提到的ACK,FIN,SYN,校驗和是用來確認數據是否被篡改,如何無法通過校驗會被丟棄,窗口是數據傳輸的吞吐量,受限于發送方和接收方的管道大小。
什么是擁塞窗口?
這是一種網絡傳輸的過程,在初始階段通信窗口的數量是成指數性增長,當達到臨界值后,進行入擁塞避免階段開始線性增長,當增長到出現丟包的情形時傳輸數量減半,并繼續線性增長,如果接收到3次ACK請求,傳輸窗口的數量減半并加3,再進入線性增長。
網絡層最常使用的是IP協議,這一層的職責是接收傳輸層的報文,將它封裝為IP數據包,添加IP頭。
兩臺電腦通過IP地址和端口號就可以建立Socket連接,一臺電腦最多可以擁有2 ^ 16個端口。
通過在控制臺中輸入netstat -nao獲取電腦的TCP和UDP連接,已經對應的PID。
Socket通信
Socket位于哪一層?
在 OSI 模型中,Socket 通常被認為是傳輸層的一部分,因為它直接與傳輸層協議(如 TCP 或 UDP)交互。
但從功能上看,Socket 更像是傳輸層和應用層之間的橋梁,因為它為應用層提供了訪問傳輸層服務的接口。
如何用C#實現Socket通信?
這里需要用到一個第三方的類庫TouchSocket,它比微軟原生的Socket通信庫多了很多功能,像是斷點重連, 健康活性檢驗等等。
1、首先在Nuget上下載最新版的TouchSocket 3.1.1
2、創建一個TcpClient服務,進行事件綁定,配置插件,在Received事件中可以獲取到服務器的反饋,發送數據通過Send方法
// TCP客戶端var _tcpClient = new TcpClient();#region 事件綁定_tcpClient.Connected = (client, e) =>{this.Invoke(new Action(() =>{this.richTextBoxMsgLog.AppendText($"連接到服務器".StringFormatLog() + Environment.NewLine);}));// 改變UI狀態this._isOpen = true;this.Invoke(new Action(() => {this.btnOpen.Text = "斷開連接";this.SwitchCheckBoxForSetting(false);}));return EasyTask.CompletedTask;};_tcpClient.Closed = (client, e) =>{this.Invoke(new Action(() =>{this.richTextBoxMsgLog.AppendText($"斷開與服務器的連接".StringFormatLog() + Environment.NewLine);}));// 斷開網絡this._tcpClient.Close();this._isOpen = false;this.Invoke(new Action(() => {this.btnOpen.Text = "連接";this.SwitchCheckBoxForSetting(true);}));return EasyTask.CompletedTask;};_tcpClient.Received = (client, e) =>{string mes = e.ByteBlock.Span.ToString(Encoding.UTF8);this.Invoke(new Action(() =>{this.richTextBoxMsgLog.AppendText($"從服務器收到信息:{mes}".StringFormatLog() + Environment.NewLine);}));return EasyTask.CompletedTask;};#endregion// 配置斷開重連機制var config = new TouchSocketConfig();config.ConfigurePlugins(plugins =>{// 自動重連plugins.UseTcpReconnection().UsePolling(TimeSpan.FromSeconds(1));});// 連接服務器await this._tcpClient.SetupAsync(config);await _tcpClient.ConnectAsync($"{ip}:{port}");
3、創建一個TcpServer服務,用于接收客戶端發送的消息,代碼和客戶端的差不多。
TcpService _service = new TcpService();// 創建一個TCP服務器_service.Connecting = (client, e) =>{return EasyTask.CompletedTask;};_service.Connected = (client, e) =>{this.Invoke(new Action(() =>{this.richTextBoxMsgLog.AppendText($"有客戶端連接:{client.IP}:{client.Port}".StringFormatLog() + Environment.NewLine);}));return EasyTask.CompletedTask;};_service.Closing = (client, e) =>{return EasyTask.CompletedTask;};_service.Closed = (client, e) =>{this.Invoke(new Action(() =>{this.richTextBoxMsgLog.AppendText($"有客戶端斷開連接:{client.IP}:{client.Port}".StringFormatLog() + Environment.NewLine);}));return EasyTask.CompletedTask;};_service.Received = (client, e) =>{//從客戶端收到信息var mes = e.ByteBlock.Span.ToString(Encoding.UTF8);this.Invoke(new Action(() =>{this.richTextBoxMsgLog.AppendText($"客戶端:{client.IP}:{client.Port},發送消息:{mes}".StringFormatLog() + Environment.NewLine);}));return EasyTask.CompletedTask;};var config = new TouchSocketConfig();config.ConfigurePlugins(plugins =>{// 健康活性檢驗插件(CheckClearPlugin)用于檢測當前連接是否有正常的數據交流,如果沒有,則主動斷開連接。plugins.UseCheckClear().SetCheckClearType(CheckClearType.All).SetTick(TimeSpan.FromSeconds(60)).SetOnClose(async (c, t) =>{await c.ShutdownAsync(System.Net.Sockets.SocketShutdown.Both);await c.CloseAsync("超時無數據");});});await this._service.SetupAsync(config);await this._service.StartAsync($"{ip}:{port}");