基於C#的socket編程的TCP同步實現
該部落格源著地址//www.cnblogs.com/sunev/archive/2012/08/05/2604189.html
一、摘要
總結一下基於C#的TCP傳輸協議的涉及到的常用方法及同步實現。
二、實驗平台
Visual Studio 2010
三、socket編程的一些常用方法(同步實現)
3.1 命名空間
需要添加的命名空間
using System.Net; using System.Net.Socket;
3.2 構造新的socket對象
socket原型:
public socket (AddressFamily addressFamily,SocketType sockettype,ProtocolType protocolType)
(1) AddressFamily 用來指定socket解析地址的定址方案,Inte.Network標示需要ip版本4的地址,Inte.NetworkV6需要ip版本6的地址;
(2) SocketType 參數指定socket類型,Raw支援基礎傳輸協議訪問,Stream支援可靠,雙向,基於連接的數據流;
(3) ProtocolType 表示socket支援的網路協議,如常用的TCP和UDP協議。
3.3 定義主機對象
(1) IPEndPoint類
原型:
a)
public IPEndPoint(IPAddress address,int port)
參數address可以直接填寫主機的IP,如”192.168.2.1″;
b)
public IPEndPoint(long address,int port)
參數address整型int64如123456,參數port埠int32,如6655。
(2) 利用DNS伺服器解析主機,使用Dns.Resolve方法
原型:
public static IPHostEntry Resolve(string hostname)
參數:待解析的主機名稱,返回IPHostEntry類值,IPHostEntry為Inte.Net主機地址資訊提供容器,該容器提供存有IP地址列表,主機名稱等。
(3) Dns.GetHostByName獲取本地主機名稱
原型:
public static IPHostEntry GetHostByName(string hostname)
(4) GetHostByAddress
原型:
a)
public static IPHostEntry GetHostByAddress(IPAddress address)
參數:IP地址。
b)
public static IPHostEntry GetHostByAddress(string address)
參數:IP地址格式化字元串。
3.4 埠綁定和監聽
同步套接字伺服器主機的綁定和埠監聽,Socket類的Bind(綁定主機),Listen(監聽埠),Accept(接收客戶端的連接請求)。
(1) Bind
原型:
public void Bind(EndPoint LocalEP)
參數為主機對象 IPEndPoint
(2) Listen
原型:
public void Listen(int backlog)
參數整型數值,掛起隊列最大值
(3) accept
原型:
public socket accept()
返回為套接字對象
3.5 socket的發送和接收方法
(1) 發送數據
a)socket類的send方法
原型一:
public int Send(byte[] buffer)
參數:待發送的位元組數組;
原型二:
public int Send(byte[],SocketFlags)
SocketFlags成員列表:
DontRoute不使用路由表發送,
MaxIOVectorLength為發送和接收數據的wsabuf結構數量提供標準值,
None 不對次調用使用標誌,
OutOfBand消息的部分發送或接收,
Partial消息的部分發送或接收,
Peek查看傳入的消息。
原型三:
public int Send(byte[],int,SocketFlags)
參數二要發送的位元組數
原型四:
public int Send(byte[],int,int,SocketFlags)
參數二為Byte[]中開始發送的位置
b) NetWordStream類的Write方法
原型:
public override void write(byte[] buffer,int offset,int size)
參數分別為:位元組數組,開始位元組位置,總位元組數。
(2) 接收數據
a) Socket類Receive方法
原型一:
public int Receive(byte[] buffer)
原型二:
public int Receive(byte[],SocketFlags)
原型三:
public int Receive(byte[],int,SocketFlags)
原型四:
public int Receive(byte[],int,int,SocketFlags)
Socket類Receive方法的相關參數可參看Socket類Send方法中的參數。
b) NetworkStream類的Read方法
public override int Read(int byte[] buffer,int offset,int size)
參數可參看NetworkStream類的Write方法。
四、TCP傳輸協議的同步實現
4.1 伺服器端編程的步驟:
(1) 創建套接字;
(2) 綁定套接字到一個IP地址和一個埠上(bind());
(3)將套接字設置為監聽模式等待連接請求(listen());
(4)請求到來後,接受連接請求,返回一個新的對應於此次連接的套接字(accept());
(5)用返回的套接字和客戶端進行通訊(send()/recv());
(6)返回,等待另一連接請求;
(7)關閉套接字。
伺服器端程式碼:
using System; using System.Net; using System.Net.Sockets; using System.Collections.Generic; using System.Text; namespace net { class Program { static void Main(string[] args) { //定義接收數據長度變數 int recv; //定義接收數據的快取 byte[] data = new byte[1024]; //定義偵聽埠 IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, 5566); //定義套接字類型 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //連接 socket.Bind(ipEnd); //開始偵聽 socket.Listen(10); //控制台輸出偵聽狀態 Console.Write("Waiting for a client"); //一旦接受連接,創建一個客戶端 Socket client = socket.Accept(); //獲取客戶端的IP和埠 IPEndPoint ipEndClient = (IPEndPoint)client.RemoteEndPoint; //輸出客戶端的IP和埠 Console.Write("Connect with {0} at port {1}", ipEndClient.Address, ipEndClient.Port); //定義待發送字元 string welcome = "Welcome to my server"; //數據類型轉換 data = Encoding.ASCII.GetBytes(welcome); //發送 client.Send(data, data.Length, SocketFlags.None); while (true) { //對data清零 data = new byte[1024]; //獲取收到的數據的長度 recv = client.Receive(data); //如果收到的數據長度為0,則退出 if (recv == 0) break; //輸出接收到的數據 Console.Write(Encoding.ASCII.GetString(data, 0, recv)); //將接收到的數據再發送出去 client.Send(data, recv, SocketFlags.None); } Console.Write("Disconnect form{0}", ipEndClient.Address); client.Close(); socket.Close(); } } }
4.2 客戶端編程的步驟:
(1) 創建套接字;
(2) 向伺服器發出連接請求(connect());
(3) 和伺服器端進行通訊(send()/recv());
(4) 關閉套接字。
客戶端程式碼:
using System; using System.Net; using System.Net.Sockets; using System.Collections.Generic; using System.Text; namespace client { class Program { static void Main(string[] args) { //定義發送數據快取 byte[] data = new byte[1024]; //定義字元串,用於控制台輸出或輸入 string input, stringData; //定義主機的IP及埠 IPAddress ip = IPAddress.Parse("127.0.0.1"); IPEndPoint ipEnd = new IPEndPoint(ip, 5566); //定義套接字類型 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //嘗試連接 try { socket.Connect(ipEnd); } //異常處理 catch (SocketException e) { Console.Write("Fail to connect server"); Console.Write(e.ToString()); return; } //定義接收數據的長度 int recv = socket.Receive(data); //將接收的數據轉換成字元串 stringData = Encoding.ASCII.GetString(data, 0, recv); //控制台輸出接收到的數據 Console.Write(stringData); //定義從鍵盤接收到的字元串 input = Console.ReadLine(); //將從鍵盤獲取的字元串轉換成整型數據並存儲在數組中 data = Encoding.ASCII.GetBytes(input); //發送該數組 socket.Send(data, data.Length, SocketFlags.None); while (true) { // //如果字元串是"exit",退出while循環 if (input == "exit") { break; } //對data清零 data = new byte[1024]; //定義接收到的數據的長度 recv = socket.Receive(data); //將接收到的數據轉換為字元串 stringData = Encoding.ASCII.GetString(data, 0, recv); //控制台輸出字元串 Console.Write(stringData); //發送收到的數據 socket.Send(data, recv, 0); } Console.Write("disconnect from server"); socket.Shutdown(SocketShutdown.Both); socket.Close(); } } }
上述程式碼實現了,當連接建立之後,客戶端向伺服器端發送鍵盤輸入的字元,伺服器端收到字元後,顯示在控制台並發送給客戶端,客戶端收到字元後,顯示在控制台並再次發送給伺服器端,如此循環。
五、實驗結果
先後運行伺服器端程式和客戶端程式,控制台介面如下:
圖1 伺服器端控制台
當連接建立後,伺服器端控制台顯示等待客戶端的狀態”Waiting for a client”,並列印出連接資訊。
圖2 客戶端控制台
當連接建立後,客戶端收到來自伺服器端發送的字元串”Welcome to my server”。
之後,客戶端通過鍵盤發送數據,二者循環接收並發送,控制台分別如下:
圖3 伺服器控制台
圖4 客戶端控制台
六、幾點說明
6.1 傳輸速度
(1) 增大發送和接收的數組可提升傳輸速度,即增加一次實際發送數據的數量可以提高傳輸速度,但數組中數據的個數也不能一味的增大。需要說明的,由於地層MIT的限制,底層具體實現的時候每次發送的數據仍是不超過1510個的。
(2) 將控制台介面最小化後,速度也會有翻倍的提升。
6.2 MFC的轉換
為了使傳輸協議更有可觀性和使用性,通常做成MFC的樣式,具體的使用已在”基於TCP協議的網路攝影機的設計與實現“應用。