完全是基礎,新手可以隨意看看,大牛可以關閉瀏覽頁了,哈哈。
TCP/IP協議
TCP/IP是一系列網絡通信協議的統稱,其中最核心的兩個協議是TCP和IP。TCP稱為傳輸控制協議,IP稱為互聯網絡協議。
網絡分層除了OSI模型分層,還有TCP/IP模型分層,將網絡划分為四層,應用層、傳輸層、網際層、網絡接口層。TCP/IP模型分層是OSI模型分層的濃縮版
OSI模型和TCP/IP網絡分層的對應結構及TCP/IP部分協議族,
TCP三次握手
第一次握手:建立連接。客戶端發送連接請求報文段,將SYN位置為1,Sequence Number為x;然后,客戶端進入SYN_SEND狀態,等待服務器的確認;
第二次握手:服務器收到SYN報文段。服務器收到客戶端的SYN報文段,需要對這個SYN報文段進行確認,設置Acknowledgment Number為x+1(Sequence Number+1);同時,自己自己還要發送SYN請求信息,將SYN位置為1,Sequence Number為y;服務器端將上述所有信息放到一個報文段(即SYN+ACK報文段)中,一並發送給客戶端,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK報文段。然后將Acknowledgment Number設置為y+1,向服務器發送ACK報文段,這個報文段發送完畢以后,客戶端和服務器端都進入ESTABLISHED狀態,完成TCP三次握手。
三次握手圖解
在此基礎上,socket連接過程:
服務器監聽:服務器端socket並不定位具體的客戶端socket,而是處於等待監聽狀態,實時監控網絡狀態。
客戶端請求:客戶端clientSocket發送連接請求,目標是服務器的serverSocket。為此,clientSocket必須知道serverSocket的地址和端口號,進行掃描發出連接請求。
連接確認:當服務器socket監聽到或者是受到客戶端socket的連接請求時,服務器就響應客戶端的請求,建議一個新的socket,把服務器socket發送給客戶端,一旦客戶端確認連接,則連接建立。
注:在連接確認階段:服務器socket即使在和一個客戶端socket建立連接后,還在處於監聽狀態,仍然可以接收到其他客戶端的連接請求,這也是一對多產生的原因。
socket連接原理知道了,此處編寫最基本最簡單的socket通信:
客戶端代碼:
/// <summary> /// 客戶端發送與服務端的連接請求,創建與之通信用的Socket /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnLink_Click(object sender, EventArgs e) { try { //定義一個用於請求連接服務器的IP和端口號 IPEndPoint ip = new IPEndPoint(IPAddress.Parse(txtIp.Text), Convert.ToInt32(txtPort.Text));//txtIP:IP地址;txtPort:端口號 //定義一個套接字用於請求連接服務器,包含三個參數(IP4尋址協議,流式連接,Tcp協議) socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //請求連接服務端 clientSocket.Connect(ip); //創建一個通信線程,避免阻塞主線程 Thread th = new Thread(CRecive); //設置為后台線程,隨着主線程退出而退出 th.IsBackground = true; //啟動線程 th.Start(clientSocket); //顯示與服務端連接情況 ShowMsg($"{clientSocket.RemoteEndPoint.ToString()}:已連接"); } catch (Exception ex) { //提示套接字監聽異常 ShowMsg($"網絡錯誤"); } } /// <summary> /// 客戶端不停的接收服務端發送過來的消息 /// </summary> /// <param name="o"></param> void CRecive(object o) { Socket socketSend = o as Socket; while (true) { try { //創建一個內存緩沖區,其大小為1024*1024字節 即1M byte[] buffer = new byte[1024 * 1024]; //將接收到的信息存入到內存緩沖區,並返回其字節數組的長度 int length = socketSend.Receive(buffer); if (length == 0) { ShowMsg($"{socketSend.RemoteEndPoint.ToString()}:已斷開連接"); break; } //將機器接受到的字節數組轉換為人可以讀懂的字符串 string str = Encoding.UTF8.GetString(buffer, 0, length); //將發送的字符串信息附加到ShowMsg上 ShowMsg($"Ta({socketSend.RemoteEndPoint.ToString()}):{str}"); } catch (Exception ex) { } } }
客戶端向服務端發送數據:
clientSocket.Send(Encoding.UTF8.GetBytes($"{txtWrite.Text}"));
服務端代碼:
/// <summary> /// 開啟監聽,等待客戶端連接 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnMonitor_Click(object sender, EventArgs e) { try { //定義一個套接字用於監聽客戶端發來的消息,包含三個參數(IP4尋址協議,流式連接,Tcp協議) socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //服務端發送信息需要一個IP地址和端口號 IPAddress ip = IPAddress.Parse(txtIp.Text);//IPAddress.Any; //將IP地址和端口號綁定到網絡節點point上 IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(txtPort.Text)); //監聽綁定的網絡節點 socketWatch.Bind(point); ShowMsg("開始監聽。。。"); //將套接字的監聽隊列長度限制為10 socketWatch.Listen(10); //負責監聽客戶端的線程:創建一個監聽線程 Thread th = new Thread(SListen); //將窗體線程設置為與后台同步,隨着主線程結束而結束 th.IsBackground = true; //啟動線程 th.Start(socketWatch); } catch (Exception ex) { } } /// <summary> /// 等待客戶端的連接 並且創建與之通信用的Socket /// </summary> /// <param name="o"></param> void SListen(object o) { Socket socketWatch = o as Socket; while (true) { try { //等待客戶端的連接,並且創建與之通信用的Socket Socket serverSocket = socketWatch.Accept(); //獲取客戶端網絡結點號 string socketIP = serverSocket.RemoteEndPoint.ToString(); ShowMsg($"{socketIP}:已連接"); //創建一個通信線程,避免阻塞主線程 Thread th = new Thread(SRecive); th.IsBackground = true; th.Start(serverSocket); } catch { } } } /// <summary> /// 服務端不停的接收客戶端發送過來的消息 /// </summary> /// <param name="o"></param> void SRecive(object o) { Socket socketSend = o as Socket; while (true) { //將接收到的信息存入到內存緩沖區,並返回其字節數組的長度 try { //創建一個內存緩沖區,其大小為1024*1024字節 即1M byte[] buffer = new byte[1024 * 1024]; //接收客戶端發送過來的消息 int length = socketSend.Receive(buffer); if (length == 0) { ShowMsg($"{socketSend.RemoteEndPoint.ToString()}:已斷開連接"); break; } //將機器接受到的字節數組轉換為人可以讀懂的字符串 string str = Encoding.UTF8.GetString(buffer, 0, length); //將發送的字符串信息附加到ShowMsg上 ShowMsg($"Ta({socketSend.RemoteEndPoint.ToString()}):{str}"); } catch (Exception ex) { } } }
服務端向已連接的客戶端發送數據:
serverSocket.Send(Encoding.UTF8.GetBytes($"{txtWrite.Text}"));
注:c#新手的隨手筆記,方便以后學習和使用,如有不足和雷同,歡迎留言!謝謝!