基於C#的socket編程的TCP同步實現


一、摘要

  總結一下基於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協議的網絡攝像頭的設計與實現"應用。

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM