在學習Socket以前,先來學習點網絡相關的知識吧,本身學習過程當中的一些總結,Socket是一門很高深的學問,本文只是Socket一些最基礎的東西,大神請自覺繞路。編程
傳輸協議數組
第一次握手:客戶端嘗試鏈接服務器,向服務器發送syn包(同步序列編號Synchronize Sequence Numbers),syn=j,客戶端進入SYN_SEND狀態等待服務器確認緩存
第二次握手:服務器接收客戶端syn包並確認(ack=j+1),同時向客戶端發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態安全
第三次握手:第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手服務器
TCP協議:就比如兩個電話機 經過電話線進行數據交互的格式約定網絡
HTTP協議:就比如兩我的 經過電話機 說話的語法。異步
(1)公認端口(WellKnownPorts):從0到1023,它們緊密綁定(binding)於一些服務。一般這些端口的通信明確代表了某種服務的協議。例如:80端口實際上老是HTTP通信。socket
(2)註冊端口(RegisteredPorts):從1024到49151。它們鬆散地綁定於一些服務。也就是說有許多服務綁定於這些端口,這些端口一樣用於許多其它目的。例如:許多系統處理動態端口從1024左右開始。異步編程
(3)動態和/或私有端口(Dynamicand/orPrivatePorts):從49152到65535。理論上,不該爲服務分配這些端口。實際上,機器一般從1024起分配動態端口。學習
OSI網絡7層模型
服務器端代碼:
using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Server { using System.Net.Sockets; using System.Net; using System.Threading; public partial class Form1 : Form {
public Form1() { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false; }
//服務端 監聽套接字 Socket socketWatch = null; //服務端 監聽線程 Thread threadWatch = null; //字典集合:保存 通訊套接字 Dictionary<string, Socket> dictCon = new Dictionary<string, Socket>();
private void btnStartListen_Click(object sender, EventArgs e) { try { //1.建立監聽套接字 使用 ip4協議,流式傳輸,TCP鏈接 socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.綁定端口 //2.1獲取網絡節點對象 IPAddress address = IPAddress.Parse(txtIP.Text); IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text)); //2.2綁定端口(其實內部 就向系統的 端口表中 註冊 了一個端口,並指定了當前程序句柄) socketWatch.Bind(endPoint); //2.3設置監聽隊列 socketWatch.Listen(10); //2.4開始監聽,調用監聽線程 執行 監聽套接字的 監聽方法 threadWatch = new Thread(WatchConnecting); threadWatch.IsBackground = true; threadWatch.Start(); ShowMsg("楓伶憶,服務器啓動啦!"); } catch (Exception ex) { MessageBox.Show(ex.Message); throw; } }
void WatchConnecting() { //2.4開始監聽:此方法會阻斷當前線程,直到有 其它程序 鏈接過來,才執行完畢 Socket sokMsg = socketWatch.Accept(); //將當前鏈接成功的 【與客戶端通訊的套接字】 的 標識 保存起來,並顯示到 列表中 //將 遠程客戶端的 ip和端口 字符串 存入 列表 this.lbOnline.Items.Add(sokMsg.RemoteEndPoint.ToString()); //將 服務端的通訊套接字 存入 字典集合 dictCon.Add(sokMsg.RemoteEndPoint.ToString(), sokMsg); ShowMsg("有客戶端鏈接了!"); //2.5建立 通訊線程 Thread thrMsg = new Thread(ReceiveMsg); thrMsg.IsBackground = true; thrMsg.Start(sokMsg); }
void ReceiveMsg(object obj) { try { Socket sokMsg = obj as Socket; //3.通訊套接字 監聽 客戶端的 消息 //3.1建立 消息緩存區 byte[] arrMsg = new byte[1024 * 1024 * 1]; while (isReceive) { //3.2接收客戶端的消息 並存入 緩存區,注意:Receive方法也會阻斷當前的線程 sokMsg.Receive(arrMsg); //3.3將接收到的消息 轉成 字符串 string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg); //3.4將消息 顯示到 文本框 ShowMsg(strMsg); } } catch (Exception ex) { MessageBox.Show(ex.Message); throw; } }
void ShowMsg(string strmsg) { this.txtShow.AppendText(strmsg + "\r\n"); }
private void btnSend_Click_1(object sender, EventArgs e) { string strClient = this.lbOnline.Text; if (string.IsNullOrEmpty(strClient)) { MessageBox.Show("請選擇你要發送消息的客戶端"); return; } if (dictCon.ContainsKey(strClient)) { string strMsg = this.txtInput.Text.Trim(); ShowMsg("\r\n向客戶端【" + strClient + "】說:" + strMsg); //使用 指定的 通訊套接字 將 字符串 發送到 指定的客戶端 byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); dictCon[strClient].Send(arrMsg); } this.txtInput.Text = ""; }
}
}
客戶端代碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Client { using System.Net.Sockets; using System.Net; using System.Threading; public partial class Form1 : Form {
public Form1() { InitializeComponent(); TextBox.CheckForIllegalCrossThreadCalls = false; }
//客戶端 通訊套接字 Socket socketMsg = null; //客戶端 通訊線程 Thread threadMsg = null; bool isRec = true;//標記任務
private void btnConnect_Click(object sender, EventArgs e) { try { //1.建立監聽套接字 使用 ip4協議,流式傳輸,TCP鏈接 socketMsg = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.獲取要鏈接的服務端 節點 //2.1獲取網絡節點對象 IPAddress address = IPAddress.Parse(txtIP.Text); IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txtPort.Text)); //3.向服務端 發送連接請求 socketMsg.Connect(endPoint); ShowMsg("鏈接服務器成功~~!"); //4.開啓通訊線程 threadMsg = new Thread(RecevieMsg); threadMsg.IsBackground = true; threadMsg.Start(); } catch (Exception ex) { MessageBox.Show(ex.Message); throw; } }
void RecevieMsg() { try { //3.1建立 消息緩存區 byte[] arrMsg = new byte[1024 * 1024 * 1]; while (isRec) { socketMsg.Receive(arrMsg); string strMsg = System.Text.Encoding.UTF8.GetString(arrMsg); ShowMsg("\r\n服務器說:" + strMsg); } } catch (Exception ex) { MessageBox.Show(ex.Message); throw; } }
private void btnSend_Click_1(object sender, EventArgs e) { string strMsg = this.txtInput.Text.Trim(); byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg); socketMsg.Send(arrMsg); this.txtInput.Text = ""; }
void ShowMsg(string strmsg) { this.txtShow.AppendText(strmsg + "\r\n"); }
}
}
最終的效果圖以下:
至少要定義一個要鏈接的遠程主機的IP和端口號。
服務端先綁定:serverWelcomeSocket.Bind(endp)
客戶端再鏈接:clientSocket.Connect(endp)
好比Socket的分包,黏包問題,異步編程在後續的文章繼續討論