1. 服務端
用c#建立一個控制台的新項目 建立方法StartServerSync()實現與用戶端的同步服務,但是,這個缺陷很明顯,有兩個點需要等待
/// <summary> /// 同步式伺服器 /// </summary> static void StartServerSync() { Socket serveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//參數一表示使用ip4,參數二表示使用流傳輸,參數三表示使用tcp協議 IPAddress ipAddress = IPAddress.Parse("127.0.0.1");//本機ip地址 IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8088); serveSocket.Bind(ipEndPoint);//綁定連接埠號碼 serveSocket.Listen(10);//開始監聽連接埠號碼,10表示一個隊列,這個隊列最多讓十個要進行串連的用戶端排隊,如果設定為0則可以讓無限個要串連的用戶端排隊讓服務端進行處理 Socket clientSocket = serveSocket.Accept();//使用serveSocket.Accept()使伺服器串連一個用戶端,此方法會暫停程式直到接收到一個用戶端,返回被串連的socket //向用戶端發送一條資訊 string msg = "welcome !你好"; byte[] data = Encoding.UTF8.GetBytes(msg);//將字串轉成位元 clientSocket.Send(data); //接受用戶端的一條資訊 byte[] dataBuffer = new byte[1024]; int count = clientSocket.Receive(dataBuffer);//clientSocket.Receive(dataBuffer)接受一條byte資料存入dataBuffer,傳回值是接受到的byte的個數,此方法會暫停程式直到接收完資料 string msgReceive = Encoding.UTF8.GetString(dataBuffer, 0, count);//擷取dataBuffer[0]至dataBuffer[count]的byte轉化為字串返回 Console.WriteLine(msgReceive); Console.ReadKey(); clientSocket.Close();//關閉跟用戶端的串連 serveSocket.Close();//關閉服務端的串連 }
接下來實現服務端與用戶端的非同步串連,可實現多個用戶端串連服務端,這裡我們用Socket.BeginAccept(AsyncCallback,Object)方法使伺服器開始串連一個用戶端,其參數AsyncCallback可以是我們自己定義的一個方法,這裡我們用自己寫的AcceptCallBack(IAsyncResult)方法,第二個參數就是為了給我們的方法傳遞參數的. 先定義一個全域變數
static byte[] dataBuffer = new byte[1024];//用來儲存傳 收的資料
/// <summary> /// 伺服器(非同步) /// </summary> static void StartServerAsync() { Socket serveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//參數一表示使用ip4,參數二表示使用流傳輸,參數三表示使用tcp協議 IPAddress ipAddress = IPAddress.Parse("127.0.0.1");//本機ip地址 IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8088); serveSocket.Bind(ipEndPoint);//綁定連接埠號碼 serveSocket.Listen(10);//開始監聽連接埠號碼,10表示一個隊列,這個隊列最多讓十個要進行串連的用戶端排隊,如果設定為0則可以讓無限個要串連的用戶端排隊讓服務端進行處理 serveSocket.BeginAccept(AcceptCallBack, serveSocket);//調用函數AcceptCallBack()開始非同步串連用戶端 }
serveSocket.BeginAccept(AcceptCallBack, serveSocket);第二個參數其實就是傳遞給AcceptCallBack()方法的,相當於AcceptCallBack(serveSocket);當我們使用serveSocket.BeginAccept(AcceptCallBack, serveSocket)時程式是不會暫停,會繼續執行下去,只是我們在這裡並沒有需要執行的代碼.這隻是開始接收一個用戶端,並沒有接受一個用戶端.我們把伺服器socket傳遞過去方便在方法中實現與用戶端的串連
/// <summary> /// 串連用戶端的回呼函數 /// </summary> /// <param name="ar"></param> static void AcceptCallBack(IAsyncResult ar) { Socket serveSocket = ar.AsyncState as Socket; Socket clientSocket = serveSocket.EndAccept(ar);//完成一個用戶端的串連 //向用戶端發送一條資訊 string msg = "welcome !你好"; byte[] data = Encoding.UTF8.GetBytes(msg);//將字串轉成位元 clientSocket.Send(data); //開始監聽用戶端資料傳遞//開始接收資料,第四個參數SocketFlags這裡不需要,所以.none,ReceiveCallBack()是一個自訂的方法,這裡表示我們接收完資料後調用該方法,最後一個參數就是ReceiveCallBack()方法中的參數的傳遞,這個參數是object類型 clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket); serveSocket.BeginAccept(AcceptCallBack, serveSocket);//繼續處理下一個用戶端串連 }
Socket clientSocket = serveSocket.EndAccept(ar);到這裡才是完成了一個用戶端socket的串連.函數的最後從新回調,實現多個用戶端的串連.同樣的,我們使用clientSocket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientSocket);開始接受用戶端發過來的資料,存到dataBuffer,並切調用ReceiveCallBack()函數,此函數也是個回掉函數,以此來實現每個用戶端的多次資料發送.使用clientScoket.EndReceive(ar)完成用戶端資料的傳遞,此時資料已經存到dataBuffer裡,如果用戶端非正常關閉,此行代碼會出異常,因為clientSocket會為空白.如果用戶端使用close()方法正常關閉,那麼伺服器會接受到很多條空資料,以此可以區分用戶端關閉方式以便做不同處理.
/// <summary> /// 接收資料的回呼函數 /// </summary> /// <param name="ar"></param> static void ReceiveCallBack(IAsyncResult ar) { Socket clientScoket = ar.AsyncState as Socket;//將參數強制轉換為socket try { int count = clientScoket.EndReceive(ar);//完成用戶端資料的傳遞,返回位元個數 //用戶端正常關閉伺服器就會接受到空資料,但是不會報錯,所以這裡處理正常關閉的情況 if(count==0) { clientScoket.Close(); return; } string msg = Encoding.UTF8.GetString(dataBuffer, 0, count); Console.WriteLine("客戶說:" + msg); //函數回調,這樣就能接收多條資料 clientScoket.BeginReceive(dataBuffer, 0, 1024, SocketFlags.None, ReceiveCallBack, clientScoket); } catch (Exception e) { Console.WriteLine(e); //處理非正常關閉的情況 if(clientScoket==null) { clientScoket.Close(); } } }
2. 用戶端
相比之下用戶端代碼就相當容易了
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net.Sockets;using System.Net;namespace FirstSocketClient{ class Program { static void Main(string[] args) { Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); clientSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8088));//串連服務端 //接受資訊 byte[] data =new byte[1024]; int count = clientSocket.Receive(data); string msg = Encoding.UTF8.GetString(data); Console.WriteLine(msg); //向服務端發送資訊 while (true) { string s = Console.ReadLine(); if (s.Equals("//c")) { clientSocket.Close();return; } clientSocket.Send(Encoding.UTF8.GetBytes(s)); } } }}