一個基於TCP/IP的伺服器與客戶端通訊的小專案(超詳細版)
1.目的:實現客戶端向伺服器傳送資料
原理:
2.建立兩個控制檯應用,一個為伺服器,用於接收資料。一個為客戶端,用於傳送資料。
關鍵類與對應方法:
1)類IPEndPoint:
1.是抽象類EndPoint的實現類
2.Socket物件的RemoteEndPoint、 LocalEndPoint都是這個型別
3.屬性Address: 使用IPv4表示的地址
4.屬性Port:使用int表示的埠
2)類Socket:
這個類即可以用於作伺服器端的開發,又可以作客戶端的開發
構造方法:
引數 AddressFamily:指定使用IPv4的地址InterNetwork
引數SocketType:指定使用流式傳輸Stream
引數ProtocolType:指定協議型別Tcp
1.方法Bind()E 繫結IP與埠,這樣就成為了伺服器,可以監聽指定IP的特定埠
2.方法Listen(); 置於監聽狀態,引數是最大的掛起數
3.方法Accept(): 接收客戶端連線,返回Socket物件, 這個方法會阻塞當前執行緒,建議開啟新執行緒執行些方法,結合尾遞迴,這樣就可以接收多個客戶端
4.方法Receive(): 接收客戶端傳送過來的訊息,以位元組為單位進行操作,此方法會阻塞當前執行緒,建議開啟新執行緒執行此方法,結合尾遞迴,就可以持續接收多條資訊
5. 方法Send(): 傳送訊息,以位元組為單位
3.具體實現
其他內容不做過多解釋了,備註做的超詳細,應該只有笨笨的人才寫這麼多備註吧。。
1.伺服器
主函式:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ServerTest { class Program { static void Main(string[] args) { // 呼叫建構函式,使用Start方法 ServerControl server = new ServerControl(); server.Start(); Console.ReadKey(); } } }
ServerControl類:
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ServerTest { public class ServerControl { // 宣告變數(使用Socket需using System.Net.Sockets;) private Socket serverSocket; // 自定義有參構造方法(IP地址,流程傳輸方式,TCP協議) public ServerControl() { serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } // 建立啟動方法(IPEndPoint用於指定地址及埠初始化,需using System.Net;) public void Start() { // 伺服器啟動 // 繫結IP地址(為任意IP)與埠(設定為12345) serverSocket.Bind(new IPEndPoint(IPAddress.Any,12345)); serverSocket.Listen(10); Console.WriteLine("伺服器啟動成功"); // 開啟執行緒:目的實現伺服器和客戶端一對多連線 Thread threadAccept = new Thread(Accept); threadAccept.IsBackground = true; threadAccept.Start(); } // Accept方法測試:接收客戶端連線 private void Accept() { // 接收客戶端方法,會掛起當前執行緒(.RemoteEndPoint表示遠端地址) Socket client = serverSocket.Accept(); IPEndPoint point = client.RemoteEndPoint as IPEndPoint; Console.WriteLine(point.Address + "[" + point.Port + "] 連線成功!"); // 開啟一個新執行緒執行緒,實現訊息多次接收 Thread threadReceive = new Thread(Receive); threadReceive.IsBackground = true; threadReceive.Start(client); // 尾遞迴 Accept(); } // Receive方法的使用測試 // 接收客戶端傳送過來的訊息,以位元組為單位進行操作 // 該方法會阻塞當前執行緒,所以適合開啟新的執行緒使用該方法 // Accept()中將Receive作為執行緒傳遞物件,所以要注意一點,使用執行緒傳遞物件只能是object型別的!! private void Receive(object obj) { // 將object型別強行轉換成socket Socket client = obj as Socket; IPEndPoint point = client.RemoteEndPoint as IPEndPoint; // 此處的異常丟擲主要針對客戶端異常的問題 // 比如,客戶端關閉或者連線中斷 // 程式會停留在int msgLen = client.Receive(msg);這段程式碼,而導致無法繼續往下走 try { byte[] msg = new byte[1024]; // 實際接收到位元組陣列長度,該方法會阻塞當前執行緒,即(client.Receive(msg)開始掛起) // 同時,這裡還是尾遞迴掛起處 int msgLen = client.Receive(msg); // 將msg裝換成字串 Console.WriteLine(point.Address + "[" + point.Port + "]:" + Encoding.UTF8.GetString(msg, 0, msgLen)); // 此處實現伺服器自動向客戶端返回一條訊息 // 因為Send傳送資訊是以位元組為單位傳送的 // 所以下面(Encoding.UTF8.GetString(msg,0,msgLen)+" yes.boy")這一塊是把這一部分均搞成string // 後使用Encoding.UTF8.GetBytes統一轉化成位元組傳遞 // 這裡呢,已經實現伺服器向客戶端傳送訊息了,客戶端只需要receive一下,格式一轉就可視化了 client.Send(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(msg,0,msgLen)+" yes.boy")); // 尾遞迴實現多條訊息的接收;和while同理。 Receive(client); } catch { Console.WriteLine(point.Address + "[" + point.Port + "]積極斷開"); } } } }
2.客戶端:
主函式:
client.Connect("127.0.0.1",12345);
修改IP可實現不同計算機之間的連線。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClientTest { class Program { static void Main(string[] args) { // 呼叫建構函式 ClientControl client = new ClientControl(); // 輸入本機IP與埠號 client.Connect("127.0.0.1",12345); // 提示操作方法 Console.WriteLine("請輸入傳送至伺服器的內容或者輸入quit退出"); // 輸入內容 string msg = Console.ReadLine(); // 非退出情況下操作方式,使用while可以持續不斷的接收使用者輸入 while(msg != "quit") { client.Send(msg); msg = Console.ReadLine(); } Console.ReadKey(); } } }
ClientControl類:
using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ClientTest { public class ClientControl { // 宣告變數 private Socket clientSocket; // 自定義有參構造方法((IP地址,流程傳輸方式,TCP協議)) public ClientControl() { clientSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); } // 建立通過IP與埠號連線的方法 public void Connect(string ip,int port) { clientSocket.Connect(ip, port); Console.WriteLine("連線伺服器成功"); // 客戶端接收伺服器訊息的執行緒 Thread threadReceive = new Thread(Receive); threadReceive.IsBackground = true; threadReceive.Start(); } // 用於測試伺服器向客戶端返回一條訊息 private void Receive() { while(true) { try { // 用於接收伺服器的回覆資訊 byte[] msg = new byte[1024]; int msgLen = clientSocket.Receive(msg); Console.WriteLine("伺服器:"+Encoding.UTF8.GetString(msg,0,msgLen)); } // 異常處理方法 catch { Console.WriteLine("伺服器積極拒絕!!"); // 退出while迴圈 break; } } } // Send方法測試:即傳送訊息,以位元組為單位 public void Send(string msg) { // 將字元創傳化為位元組陣列 clientSocket.Send(Encoding.UTF8.GetBytes(msg)); } } }
4.實現
本地連線:IP為127.0.0.1
遠端連線:因為我只有一臺電腦,所以用騰訊雲伺服器作為我的伺服器,我本地的PC作為客戶端,實現連線。
1.修改客戶端主程式裡面的IP為我的騰訊雲IP
2.使用遠端桌面連線
3.連線成功