TCP
TCP是面向對象的連接,是安全可靠的,是基於連接的協議,也就是說,在正式收發數據前,必須和對方建立可靠的連接。一個TCP連接必須要經過三次“對話”才能建立起來,其中的過程非常復雜,我 們這里只做簡單、形象的介紹,你只要做到能夠理解這個過程即可。我們來看看這三次對話的簡單過程:主機A向主機B發出連接請求數據包:“我想給你發數據, 可以嗎?”,這是第一次對話;主機B向主機A發送同意連接和要求同步(同步就是兩台主機一個在發送,一個在接收,協調工作)的數據包:“可以,你什么時候 發?”,這是第二次對話;主機A再發出一個數據包確認主機B的要求同步:“我現在就發,你接着吧!”,這是第三次對話。三次“對話”的目的是使數據包的發 送和接收同步,經過三次“對話”之后,主機A才向主機B正式發送數據。
首先我們來看看用TCP實現聊天程序吧
實現聊天程序我們需要一個服務端一個客戶端來模擬實現,我們首先來建立服務器端,直接貼代碼,如下:

2 Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
3 // 將套接字綁定到本地的IP和端口
4 IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999);
5 // 綁定套接字
6 socketServer.Bind(endPoint);
7 // 輸出語句 服務已經啟動
8 Console.WriteLine( " =====TCP Server Is OK======\r\n ===IP: " + endPoint.Address + " Port: " + endPoint.Port+ " === ");
9 // 開始監聽
10 socketServer.Listen( 10);
11 //接受消息並 返回的新的套接字對象
12 Socket sk = socketServer.Accept();
簡要介紹下個別變量,方法的作用:
socketServer:實例化一個服務端的Socket實例
endPoint:網絡終結點,定義了IP和Port
Bind方法:用於將Socket實例綁定到該網絡終結點上
Listen方法:監聽端口,參數為監聽序列的長度
Accept方法:接受通信,返回一個新的Socket對象,然后之后的通信就交由該新的對象來進行,socketServer就相當於公司的前台,只負責接待,具體的事務是通過它交由其他人來執行(個人理解)
然后是服務器端接受數據的代碼,如下:

2
3 // 新建一個字節數組
4 byte[] recveMsg= new byte[ 1024* 1024];
5 // 使用receive方法接受發送到服務器端的數據
6 int bytes = sk.Receive(recveMsg,SocketFlags.None);
7 // 將數據進行編碼
8 string receive = System.Text.Encoding.UTF8.GetString(recveMsg, 0, bytes);
9 // 將信息打印到控制台
10 Console.WriteLine(receive);
簡要解釋下個別變量的作用:
recveMsg:這是一個字節數組,因為在接受數據時,我們需要將接收到的數據存放到字節數組中,所以我們要首先定義一個字節數組,這里我給了它1024*1024的大小
bytes:這是一個int型變量,作用就是用來接收Socket用Receive接受到數據的實際長度,但為什么我們需要這個變量呢,因為在后一行代碼中,我們需要將字節數組轉換為字符串來進行輸出,如果沒有這個變量來定義大小,我們每次都會把1024*1024的字節長度轉換成字符串,所以往往有時候接收到的長度沒有1024*1024大小,
因此會造成無用的轉換。
然后就是發送數據,貼上代碼:

2
3 // 實例化發送的信息
4 string message= " Hello Clinet,My Name Is HolyKnight_Server ";
5 // 將字符串轉換成字節數組
6 byte[] sendMsg = System.Text.Encoding.UTF8.GetBytes(message);
7 // 發送數據
8 int sendBytes = sk.Send(sendMsg, SocketFlags.None);
9
10 // 關閉套接字
11 socketServer.Close();
12
13 Console.ReadKey();
簡要的解釋下個別變量的作用:
message:這個很顯然,我們用message定義了一個字符串,來模擬要發送的數據
sendMsg:剛上面說了,接受數據時我們會用一個字節數組來接受,然后將數據存到該數組中,同理,發送也一樣,發送的數據也要求是字節數組,所以我們同樣定義一個字節數組來存放要發送的數據。
sendBytes:同樣一個int型的變量,來接受Socket用Send發送數據的實際長度(大小)。
好,這樣呢,我們的服務器端就搭建好了,這時我們需要一個客戶端,所以下面我們來創建客戶端
同樣,首先也要新建一個Socket,並綁定IP和Port,再連接到遠程主機,代碼如下:

2 Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
3 // 設置與遠程主機連接的網絡節點
4 IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse( " 127.0.0.1 "), 9999);
5 // 與遠程主機建立連接
6 socketClient.Connect(endPoint);
簡要解釋下個別變量,方法的作用:
socketClient:實例化一個客戶端的Socket對象
endPoint:網絡終結點,這里用了127.0.0.1這個IP地址(回環地址),端口號必須和服務器端的相同
Connect方法:已經定義好了Socket對象和網絡終結點了,這里就用Connect方法來實現和遠程主機建立連接了
同樣客戶端也要進行發送和接受數據,由於兩個方法和服務器端端的收發數據方法一致,這里就不再重復贅述了,直接貼上代碼:
發送數據:

2 string sendMsg = " Hello Server,My Name Is HolyKnight_Client ";
3 byte[] sendBytes = System.Text.Encoding.UTF8.GetBytes(sendMsg);
4 int bytes = socketClient.Send(sendBytes, SocketFlags.None);

2 byte[] receiveMsg = new byte[ 1024 * 1024];
3 int receiveBytes = socketClient.Receive(receiveMsg, SocketFlags.None);
4 string Message = System.Text.Encoding.UTF8.GetString(receiveMsg, 0, receiveBytes);
5 Console.WriteLine(Message);
接受數據:

2 byte[] receiveMsg = new byte[ 1024 * 1024];
3 int receiveBytes = socketClient.Receive(receiveMsg, SocketFlags.None);
4 string Message = System.Text.Encoding.UTF8.GetString(receiveMsg, 0, receiveBytes);
5 Console.WriteLine(Message);
這樣,我們的客戶端也就搭建好了,所以至此,我們的小聊天程序的客戶端和服務器端都搭建完畢,可以來運行看效果了,運行時,我們必須首先運行服務器端,然后再開啟客戶端進行遠程連接,首先開啟服務器端:顯示【TCP Server Is OK】並顯示了IP和Port,表示服務器端服務已成功開啟,圖如下:
接下來運行客戶端程序,客戶端已開啟就會連上服務器端,並接受到服務器發送過來的數據,運行效果如圖:
此時,服務器端也應該接受到了來自客戶端的數據,查看,果然收到了數據,如圖:
至此,一個基於TCP的Socket的簡單通信就完成了,,,,下面我們要開看看,用UDP同樣來實現這個效果。。。
UDP
UDP 是User Datagram Protocol的簡稱, 中文名是用戶數據報協議,是 一種無連接的傳輸層協議,提供面向事務的簡單不可靠信息傳送服務,與TCP不同的是,UDP是面向無連接的,它沒有TCP傳輸前的“三次握手”的機制,是一種不可靠的傳輸機制。。
我們再來看看用UDP實現小聊天程序吧,,,
同TCP一樣,UDP同樣需要一個服務器端和一個客戶端來模擬對話,我們就用兩個控制台應用程序實現,首先我們來搭建服務器端,貼上代碼:

Socket socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
// 第二步 設置一個網絡節點
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 8888);
// 將socketServer綁定到網絡節點上
socketServer.Bind(endPoint);
// 【Tcp的時候需要監聽 但DUP不需要監聽】
// socketServer.Listen(10);
// 輸出一句話 提示服務已開啟
Console.WriteLine( " ===UDP Server Is OK===\r\n IP: " + endPoint.Address + " Port: " + endPoint.Port);
【解釋】:與TCP一樣,實例化一個Socket對象,然后新建一個網絡終結點,並指定IP和Port,然后將Socket對象綁定到該終結點上
【不同】:由於UDP是面向無連接的,所以在UDP中並不需要監聽機制
大家還記得上面TCP接受到通信時是怎樣處理的么??是的,Accept方法返回了一個新的Socket對象,之后由該對象進行通信,那UDP呢?先看代碼吧。。

2 IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
3 // 建立一個網絡地址
4 EndPoint Remote = (EndPoint)sender;
原來在UDP中,我們首先會實例化一個新的網絡地址(該地址的IP為任意IP,端口號為0,表示由系統分配端口),在收發數據時,將該網絡地址引用到主機的網絡地址上,之后我們就可以通過這個網絡地址來收發數據了
接下來開始收發數據,首先接受數據的代碼:

2 byte[] data= new byte[ 1024* 1024];
3 // 接受數據報文到緩沖區 並存儲終結點
4 int receive = socketServer.ReceiveFrom(data, ref Remote);
5 // 打印到控制台
6 Console.WriteLine( " Message Received From {0} ", Remote.ToString());
7 Console.WriteLine(System.Text.Encoding.UTF8.GetString(data, 0, receive));
【解釋】:同樣,我們在接受數據的時候要將數據存放到一個字節數組中,所以我們仍然會定義一個字節數組data
【不同】:1).我們除了存放接收到的數據,我們還要將遠程主機的通信節點信息存儲下來,注意參數類型ref ,為引用類型,也就是將該類型用指針指向了主機節點類型的地址。
2).接受數據的方法,TCP中為Receive方法,UDP中為ReceiveFrom方法
接下來看看發送數據的代碼,如下:

2 string sendMsg = " Hello Client,My Name is HolyKnight_UdpServer ";
3 // 將數據轉換成字節數組
4 data = System.Text.Encoding.UTF8.GetBytes(sendMsg);
5 // 將數據發送到指定的網絡地址
6 socketServer.SendTo(data, SocketFlags.None, Remote);
【解釋】:我們發送數據時,定義了一個字符串作為模擬發送數據,當然,它發送數據也是要求為字節數組,同樣的我們做了轉換
【不同】:1).在TCP中我們發送數據,直接將字節數組作為參數發送就可以了,但在UDP中,我們多了一個參數,就是我們之前指定的新的終結點,由於在前面的接收數據代碼中,我們已經用
該參數保存了遠程主機通信節點的信息了,所以我們在這里發送數據的時候,直接將數據發送到該網絡節點就可以了
2).TCP中的發送方法為Send,而在UDP中為SendTo。
同時我們還實現循環收發數據,思路很簡單,就是把收發數據放在了一個死循環中,代碼如下:

2 try
3 {
4 // 循環收發數據
5 while ( true)
6 {
7 // 接受數據
8 data = new byte[ 1024 * 1024];
9 // 從端點接受數據
10 receive = socketServer.ReceiveFrom(data, ref Remote);
11 // 打印到控制台
12 Console.WriteLine( " The Client Say: " + System.Text.Encoding.UTF8.GetString(data, 0, receive));
13 count++;
14
15 // 發送數據
16 string reply = " 這是服務器的第 " + count.ToString() + " 次回復 ";
17 // 數據轉換
18 data = System.Text.Encoding.UTF8.GetBytes(reply);
19 // 發送數據到指定端點
20 socketServer.SendTo(data, Remote);
21 }
22 }
23 finally
24 {
25 // 關閉套接字
26 socketServer.Close();
27 }
好,,這樣我們UDP的服務器端也搭建完成了,,接下來同理,搭建客戶端。。
實現原理和服務器端一樣,所以就不解釋了,直接上代碼吧:

2 Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
3
4 // 第二步 通過套接字收發報文
5 Console.WriteLine( " 按任意鍵 開始向服務器發送數據 ");
6 Console.ReadKey();
7
8 byte[] data = new byte[ 1024 * 1024];
9 string input, stringData;
10
11 IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse( " 127.0.0.1 "), 8888);
12 data = Encoding.UTF8.GetBytes( " Hello Server,My Name Is HolyKnight_UdpClient ");
13
14 // 將數據發送到服務器的終結點
15 socketClient.SendTo(data, endPoint);
16
17 // 定義一個發送終結點,沒有具體的IP和Port
18 IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
19
20 // 定義一個網絡地址
21 EndPoint Remote = (EndPoint)sender;
22
23 // 重新實例化一個字節數組 用於存放接受到的數據
24 data = new byte[ 1024 * 1024];
25 // 接受數據 將數據保存到data數據 將遠程主機的節點保存到Remote終端中【注意ref引用】
26 int receive = socketClient.ReceiveFrom(data, ref Remote);
27
28 Console.WriteLine( " Message Receive From {0} ", Remote.ToString());
29 Console.WriteLine(Encoding.UTF8.GetString(data, 0, receive));
30
31 // 循環收發數據
32 while ( true)
33 {
34 // 從鍵盤讀取數據
35 input = Console.ReadLine();
36 if (input == " exit ")
37 {
38 break;
39 }
40 // 同樣的發送數據到Remote節點
41 socketClient.SendTo(Encoding.UTF8.GetBytes(input),Remote);
42 data = new byte[ 1024 * 1024];
43
44 // 同樣的接受數據 並再次更新存儲終結點
45 receive = socketClient.ReceiveFrom(data, ref Remote);
46 stringData = Encoding.UTF8.GetString(data, 0, receive);
47
48 Console.WriteLine( " 服務器說: " + stringData);
49 }
50
51 // 關閉套接字
52 Console.WriteLine( " Stopping Client ");
53 socketClient.Close();
54 Console.ReadKey();
55 }
好了,,這樣服務器端和客戶端都搭建完畢,接下來就是看看運行效果了,直接上圖
首先開啟服務器端:
我們看到【UDP Server Is Ok】和IP,Port,證明此時UDP服務器端已成功開啟了,,,
接下來開啟客戶端:
客戶端開啟成功,提示按任意鍵開始發送數據。。
接下來我們在客戶端連續給服務器發送三條消息
客戶端:
服務器端:
接下來我們在客戶端輸入“exit”請求停止通信,效果:
輸入"exit“ 之后,客戶端就提示 "Stopping Client”,這樣就停止通信了,,,
哈哈,,就這樣,通過TCP和UDP實現通信聊天程序就完成了,,當然這個是最基礎的通信例子,,在下一篇博客中將用窗體程序和多線程來實現聊天程序,盡請關注,,,,
這里附上小Demo的源代碼:
/Files/holyknight-zld/SocketDemo/SocketDemo.rar