1. 程式人生 > 實用技巧 >C# Socket網路程式設計精華篇

C# Socket網路程式設計精華篇

我們在講解Socket程式設計前,先看幾個和Socket程式設計緊密相關的概念:

  1. TCP/IP層次模型

當然這裡我們只討論重要的四層

01,應用層(Application):應用層是個很廣泛的概念,有一些基本相同的系統級TCP/IP應用以及應用協議,也有許多的企業應用和網際網路應用。http協議在應用層執行。

02,傳輸層(Tanspot):傳輸層包括UDP和TCP,UDP幾乎不對報文進行檢查,而TCP

提供傳輸保證。

03,網路層(Netwok):網路層協議由一系列協議組成,包括ICMP、IGMP、RIP、OSPF、IP(v4,v6)等。

04,鏈路層(Link):又稱為物理資料網路介面層,負責報文傳輸。

然後我們來看下tcp層次模型圖

從上圖中可以看出,應用程式在應用層執行,在傳輸層,在資料前加上了TCP頭,在

網路層加上的IP頭,在資料鏈路層加上了幀。

2,埠

埠號範圍:0-65535,總共能表示65536個數。

按埠號可分為3大類

  (1)公認埠(WellKnownPorts):從0到1023,它們緊密繫結(binding)於一些服務。通常這些埠的通訊明確表明了某種服務的協議。例如:80埠實際上總是HTTP通訊。

  (2)註冊埠(RegisteredPorts):從1024到49151。它們鬆散地綁定於一些服務。也就是說有許多服務綁定於這些埠,這些埠同樣用於許多其它目的。例如:許多系統處理動態埠從1024左右開始。

  (3)動態和/或私有埠(Dynamicand/orPrivatePorts):從49152到65535。理論上,不應為服務分配這些埠。實際上,機器通常從1024起分配動態埠。

3.TCP和UDP報文

下面一起來看下TCP和UDP的報文圖

從圖中我們可以看出TCP和UDP中都有校驗和,但是在UDP報文中,一般不使用校驗和,這樣可以加快資料傳輸的速度,但是資料的準確性可能會受到影響。換句話說,Tcp協議都有校驗和,為了保證傳輸資料的準確性。

3.Socket

Socket包括Ip地址和埠號兩部分,程式通過Socket來通訊,Socket相當於作業系統的一個元件。Socket作為程序之間通訊機制,通常也稱作”套接字”,用於描述IP地址和埠號,是一個通訊鏈的控制代碼。說白了,就是兩個程式通訊用的。

生活案例對比:

Socket之間的通訊可以類比生活中打電話的案例。任何使用者在通話之前,首先要佔有一部電話機,相當於申請一個Socket,同時要知道對方的號碼,相當於對方有一個固定的Socket,然後向對方撥號呼叫,相當於發出連線請求。假如對方在場並空閒,拿起 電話話筒,雙方就可以進行通話了。雙方的通話過程,是一方向電話機發出訊號和對方從電話機接收訊號的過程,相當於向socket傳送資料和從socket接收資料。通話結束後,一方掛起電話機,相當於關閉socket,撤銷連線。

注意:Socket不僅可以在兩臺電腦之間通訊,還可以在同一臺電腦上的兩個程式間通訊。

4,埠進階(深入)

通過IP地址確定了網路中的一臺電腦後,該電腦上可能提供很多提供服務的應用,每一個應用都對應一個埠。

在Internet上有很多這樣的主機,這些主機一般運行了多個服務軟體 ,同時提供幾種服務,每種服務都開啟一個Socket,並繫結到一個埠上,不同的埠對應於不同的服務(應用程式)

例如:http 使用80埠, ftp使用21埠 smtp使用25埠

5.Socket分類

Socket主要有兩種型別:

  1. 流式Socket

是一種面向連線的Socket,針對於面向連線的TCP服務應用,安全,但是效率低

2,資料報式Socket

是一種無連線的Socket,對應於無連線的UDP服務應用,不安全,但效率高

6. Socket一般應用模式(伺服器端和客戶端)

伺服器端的Socket(至少需要兩個)

01.一個負責接收客戶端連線請求(但不負責與客戶端通訊)

02.每成功接收到客戶端的連線便在伺服器端產生一個對應的複雜通訊的Socket

021.在接收到客戶端連線時建立

022. 為每個連線成功的客戶端請求在伺服器端都建立一個對應的Socket(負責和客戶端通訊)

客戶端的Socket

  1. 必須指定要連線的伺服器地址和埠
  2. 通過建立一個Socket物件來初始化一個到伺服器端的TCP連線

通過上圖,我們可以看出,首先伺服器會建立一個負責監聽的socket,然後客戶端通過socket連線到伺服器指定埠,最後伺服器端負責監聽的socket,監聽到客戶端有連線過來了,就建立一個負責和客戶端通訊的socket。

下面我們來看下Socket更具體的通訊過程:

Socket的通訊過程

伺服器端:

01,申請一個socket

02,繫結到一個IP地址和一個埠上

03,開啟偵聽,等待接收連線

客戶端:

01,申請一個socket

02,連線伺服器(指明IP地址和埠號)

伺服器端接收到連線請求後,產生一個新的socket(埠大於1024)與客戶端建立連線並進行通訊,原監聽socket繼續監聽。

注意:負責通訊的Socket不能無限建立,建立的數量和作業系統有關。

7.Socket的建構函式

Public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolTYpe)

AddressFamily:指定Socket用來解析地址的定址方案。例如:InterNetWork指示當Socket使用一個IP版本4地址連線

SocketType:定義要開啟的Socket的型別

Socket類使用ProtocolType列舉向Windows Sockets API通知所請求的協議

注意:

1,埠號必須在 1 和 65535之間,最好在1024以後。

2,要連線的遠端主機必須正在監聽指定埠,也就是說你無法隨意連線遠端主機。

如:

IPAddress addr = IPAddress.Parse("127.0.0.1");

IPEndPoint endp = new IPEndPoint(addr,,9000);

服務端先繫結:serverWelcomeSocket.Bind(endp)

客戶端再連線:clientSocket.Connect(endp)

3,一個Socket一次只能連線一臺主機

4,Socket關閉後無法再次使用

5,每個Socket物件只能與一臺遠端主機連線。如果你想連線到多臺遠端主機,你必須建立多個Socket物件。

8.Socket常用類和方法

相關類:

IPAddress:包含了一個IP地址

IPEndPoint:包含了一對IP地址和埠號

方法:

Socket():建立一個Socket

Bind():繫結一個本地的IP和埠號(IPEndPoint)

Listen():讓Socket偵聽傳入的連線吃那個病,並指定偵聽佇列容量

Connect():初始化與另一個Socket的連線

Accept():接收連線並返回一個新的Socket

Send():輸出資料到Socket

Receive():從Socket中讀取資料

Close():關閉Socket,銷燬連線

接下來,我們同一個簡單的伺服器和客戶端通訊的案例,來看下Sokcet的具體用法,效果圖如下:


關鍵程式碼:

伺服器端程式碼:

  1 private void Form1_Load(object sender, EventArgs e)
  2 
  3         {
  4 
  5             Control.CheckForIllegalCrossThreadCalls = false;
  6 
  7         }
  8 
  9  
 10 
 11         private void btnListen_Click(object sender, EventArgs e)
 12 
 13         {
 14 
 15             //ip地址
 16 
 17             IPAddress ip = IPAddress.Parse(txtIP.Text);
 18 
 19            // IPAddress ip = IPAddress.Any;
 20 
 21             //埠號
 22 
 23             IPEndPoint point=new IPEndPoint(ip,int.Parse(txtPort.Text));
 24 
 25             //建立監聽用的Socket
 26 
 27             /*
 28 
 29              * AddressFamily.InterNetWork:使用 IP4地址。
 30 
 31 SocketType.Stream:支援可靠、雙向、基於連線的位元組流,而不重複資料。此型別的 Socket 與單個對方主機進行通訊,並且在通訊開始之前需要遠端主機連線。Stream 使用傳輸控制協議 (Tcp) ProtocolType 和 InterNetworkAddressFamily。
 32 
 33 ProtocolType.Tcp:使用傳輸控制協議。
 34 
 35              */
 36 
 37             //使用IPv4地址,流式socket方式,tcp協議傳遞資料
 38 
 39             Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
 40 
 41             //建立好socket後,必須告訴socket繫結的IP地址和埠號。
 42 
 43             //讓socket監聽point
 44 
 45             try
 46 
 47             {
 48 
 49                 //socket監聽哪個埠
 50 
 51                 socket.Bind(point);
 52 
 53                 //同一個時間點過來10個客戶端,排隊
 54 
 55                 socket.Listen(10);
 56 
 57                 ShowMsg("伺服器開始監聽");
 58 
 59                 Thread thread = new Thread(AcceptInfo);
 60 
 61                 thread.IsBackground = true;
 62 
 63                 thread.Start(socket);
 64 
 65             }
 66 
 67             catch (Exception ex)
 68 
 69             {
 70 
 71                
 72 
 73                ShowMsg(ex.Message);
 74 
 75             }
 76 
 77         }
 78 
 79         //記錄通訊用的Socket
 80 
 81         Dictionary<string,Socket> dic=new Dictionary<string, Socket>();
 82 
 83        // private Socket client;
 84 
 85         void AcceptInfo(object o)
 86 
 87         {
 88 
 89             Socket socket = o as Socket;
 90 
 91             while (true)
 92 
 93             {
 94 
 95                 //通訊用socket
 96 
 97                 try
 98 
 99                 {
100 
101                     //建立通訊用的Socket
102 
103                   Socket  tSocket = socket.Accept();
104 
105                   string point = tSocket.RemoteEndPoint.ToString();
106 
107                     //IPEndPoint endPoint = (IPEndPoint)client.RemoteEndPoint;
108 
109                     //string me = Dns.GetHostName();//得到本機名稱
110 
111                     //MessageBox.Show(me);
112 
113                  ShowMsg(point + "連線成功!");
114 
115                  cboIpPort.Items.Add(point);
116 
117                  dic.Add(point, tSocket);
118 
119                     //接收訊息
120 
121                     Thread th = new Thread(ReceiveMsg);
122 
123                     th.IsBackground = true;
124 
125                     th.Start(tSocket);
126 
127                 }
128 
129                 catch (Exception ex)
130 
131                 {
132 
133                     ShowMsg(ex.Message);
134 
135                     break;
136 
137                 }
138 
139             }
140 
141         }
142 
143         //接收訊息
144 
145         void ReceiveMsg(object o)
146 
147         {
148 
149             Socket client = o as Socket;
150 
151             while (true)
152 
153             {
154 
155                 //接收客戶端傳送過來的資料
156 
157                 try
158 
159                 {
160 
161                     //定義byte陣列存放從客戶端接收過來的資料
162 
163                     byte[] buffer = new byte[1024 * 1024];
164 
165                     //將接收過來的資料放到buffer中,並返回實際接受資料的長度
166 
167                     int n = client.Receive(buffer);
168 
169                     //將位元組轉換成字串
170 
171                     string words = Encoding.UTF8.GetString(buffer, 0, n);
172 
173                   
174 
175                     ShowMsg(client.RemoteEndPoint.ToString() + ":" + words);
176 
177                 }
178 
179                 catch (Exception ex)
180 
181                 {
182 
183                    ShowMsg(ex.Message);
184 
185                     break;
186 
187                 }
188 
189             }
190 
191         }
192 
193  
194 
195         void ShowMsg(string msg)
196 
197         {
198 
199             txtLog.AppendText(msg+"\r\n");
200 
201         }
202 
203  
204 
205         private void Form1_FormClosing(object sender, FormClosingEventArgs e)
206 
207         {
208 
209             //主窗體關閉時關閉子執行緒
210 
211           
212 
213         }
214 
215         //給客戶端傳送訊息
216 
217         private void btnSend_Click(object sender, EventArgs e)
218 
219         {
220 
221             try
222 
223             {
224 
225                 ShowMsg(txtMsg.Text);
226 
227                 string ip = cboIpPort.Text;
228 
229                 byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
230 
231                 dic[ip].Send(buffer);
232 
233                 // client.Send(buffer);
234 
235             }
236 
237             catch (Exception ex)
238 
239             {
240 
241                ShowMsg(ex.Message);
242 
243             }
244 
245  
246 
247         }

客戶端程式碼:

  1 Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  2 
  3         private void btnConnection_Click(object sender, EventArgs e)
  4 
  5         {
  6 
  7             //連線到的目標IP
  8 
  9             IPAddress ip = IPAddress.Parse(txtIP.Text);
 10 
 11             //IPAddress ip = IPAddress.Any;
 12 
 13             //連線到目標IP的哪個應用(埠號!)
 14 
 15             IPEndPoint point=new IPEndPoint(ip,int.Parse(txtPort.Text));
 16 
 17             try
 18 
 19             {
 20 
 21                 //連線到伺服器
 22 
 23                 client.Connect(point);
 24 
 25                 ShowMsg("連線成功");
 26 
 27                 ShowMsg("伺服器" + client.RemoteEndPoint.ToString());
 28 
 29                 ShowMsg("客戶端:" + client.LocalEndPoint.ToString());
 30 
 31                 //連線成功後,就可以接收伺服器傳送的資訊了
 32 
 33                 Thread th=new Thread(ReceiveMsg);
 34 
 35                 th.IsBackground = true;
 36 
 37                 th.Start();
 38 
 39             }
 40 
 41             catch (Exception ex)
 42 
 43             {
 44 
 45                 ShowMsg(ex.Message);
 46 
 47             }
 48 
 49         }
 50 
 51         //接收伺服器的訊息
 52 
 53         void ReceiveMsg()
 54 
 55         {
 56 
 57             while (true)
 58 
 59             {
 60 
 61                 try
 62 
 63                 {
 64 
 65                     byte[] buffer = new byte[1024 * 1024];
 66 
 67                     int n = client.Receive(buffer);
 68 
 69                     string s = Encoding.UTF8.GetString(buffer, 0, n);
 70 
 71                     ShowMsg(client.RemoteEndPoint.ToString() + ":" + s);
 72 
 73                 }
 74 
 75                 catch (Exception ex)
 76 
 77                 {
 78 
 79                     ShowMsg(ex.Message);
 80 
 81                     break;
 82 
 83                 }
 84 
 85             }
 86 
 87           
 88 
 89         }
 90 
 91  
 92 
 93         void ShowMsg(string msg)
 94 
 95         {
 96 
 97             txtInfo.AppendText(msg+"\r\n");
 98 
 99         }
100 
101  
102 
103         private void btnSend_Click(object sender, EventArgs e)
104 
105         {
106 
107             //客戶端給伺服器發訊息
108 
109             if (client!=null)
110 
111             {
112 
113                 try
114 
115                 {
116 
117                    ShowMsg(txtMsg.Text);
118 
119                     byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);
120 
121                     client.Send(buffer);
122 
123                 }
124 
125                 catch (Exception ex)
126 
127                 {
128 
129                    ShowMsg(ex.Message);
130 
131                 }
132 
133             }
134 
135            
136 
137         }
138 
139  
140 
141         private void ClientForm_Load(object sender, EventArgs e)
142 
143         {
144 
145             Control.CheckForIllegalCrossThreadCalls = false;
146 
147         }
https://www.cnblogs.com/asdyzh/p/9839775.html