http://www.cnblogs.com/weilengdeyu/archive/2013/03/08/2949101.html
我們在講解Socket編程前,先看幾個和Socket編程緊密相關的概念:
- 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主要有兩種類型:
- 流式Socket
是一種面向連接的Socket,針對於面向連接的TCP服務應用,安全,但是效率低
2,數據報式Socket
是一種無連接的Socket,對應於無連接的UDP服務應用,不安全,但效率高
6. Socket一般應用模式(服務器端和客戶端)
服務器端的Socket(至少需要兩個)
01.一個負責接收客戶端連接請求(但不負責與客戶端通信)
02.每成功接收到客戶端的連接便在服務器端產生一個對應的復雜通信的Socket
021.在接收到客戶端連接時創建
022. 為每個連接成功的客戶端請求在服務器端都創建一個對應的Socket(負責和客戶端通信)
客戶端的Socket
- 必須指定要連接的服務器地址和端口
- 通過創建一個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 }
好了,到這里我們對Socket的討論就告一個段落了。

