Socket網絡編程(TCP/IP/端口/類)和實例

原文:C# Socket網絡編程精華篇

轉自:微冷的雨

  咱們在講解Socket編程前,先看幾個和Socket編程緊密相關的概念:

  1. TCP/IP層次模型

    固然這裏咱們只討論重要的四層html

       01,應用層(Application):應用層是個很普遍的概念,有一些基本相同的系統級TCP/IP應用以及應用協議,編程

也有許多的企業應用和互聯網應用。http協議在應用層運行。數組

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

提供傳輸保證。服務器

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

      04,鏈路層(Link):又稱爲物理數據網絡接口層,負責報文傳輸。socket

   而後咱們來看下tcp層次模型圖tcp

 

     從上圖中能夠看出,應用程序在應用層運行,在傳輸層,在數據前加上了TCP頭,在函數

網絡層加上的IP頭,在數據鏈路層加上了幀。post

   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         }
複製代碼
複製代碼
相關文章
相關標籤/搜索