日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

C#網(wǎng)絡(luò)編程(一)基礎(chǔ)篇

 hzsxcn 2024-09-08 發(fā)布于陜西

簡(jiǎn)介:

    C#網(wǎng)絡(luò)編程API包含在System.Net和System.Net.Sockets命名空間下,大部分網(wǎng)絡(luò)操作都可以在其中找到相應(yīng)的類來實(shí)現(xiàn);包括Socket的創(chuàng)建和連接,網(wǎng)絡(luò)流收發(fā)方法的封裝,而且還封裝了服務(wù)端類和客戶端類,提供創(chuàng)建服務(wù)端和客戶端的快速通道;

(一)Socket類

    Socket類在System.Net.Sockets命名空間下,是最基本的網(wǎng)絡(luò)操作類,其中封裝了網(wǎng)絡(luò)連接的創(chuàng)建和關(guān)閉,數(shù)據(jù)的收發(fā),以及網(wǎng)絡(luò)狀態(tài)監(jiān)控等一系列有用的功能;

示例(TCP):

復(fù)制代碼
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

class Test_Tcp
{
private Socket socket;
private void Server() { var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 綁定服務(wù)端IP和端口,客戶端通過這個(gè)地址連入 server.Bind(new IPEndPoint(IPAddress.Parse('127.0.0.1'), 9356)); // 開啟服務(wù)監(jiān)聽,參數(shù)為最大掛起隊(duì)列的連接數(shù)(并發(fā)),已連接的不計(jì) server.Listen(1); // 這是一個(gè)阻塞方法,接收客戶端接入,返回客戶端連接Socket socket = server.Accept(); Console.WriteLine('Local : {0}\nRemote : {1}', socket.LocalEndPoint.ToString(), socket.RemoteEndPoint.ToString()); // 數(shù)據(jù)接收:這是一個(gè)異步過程 ReceiveAsync(); // 數(shù)據(jù)發(fā)送:這是一個(gè)阻塞方法 Write(); // 關(guān)閉客戶端連接Socket和服務(wù)Socket socket.Close(); server.Close(); } }
復(fù)制代碼

    Server()是一個(gè)簡(jiǎn)易的基于TCP連接的服務(wù)端開啟方法,使用這個(gè)方法需要用到System.Net.Sockets和System.Net兩個(gè)命名空間;

    服務(wù)端開啟分為4步:創(chuàng)建Socket、綁定IP和端口Bind()、開啟監(jiān)聽Listen()、接入客戶端Accept();

    其中還有兩個(gè)自定義方法,ReceiveAsync()和Write(),它們分別是異步數(shù)據(jù)接收、數(shù)據(jù)發(fā)送,它們的定義在后面可以看到。

    另外,Socket的Accept()接入客戶端連接方法也有異步版本BeginAccept(),它的用法類似于后面的BeginReceive(),多客戶端系統(tǒng)一般都是用這種異步接入方式,在BeginAccept方法的回調(diào)方法中,維護(hù)一個(gè)客戶端連接容器;

復(fù)制代碼
class Test_Tcp
{
    private void Client()
    {
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        socket.Connect('127.0.0.1', 9356);
        Console.WriteLine('{0}\n{1}', socket.LocalEndPoint.ToString(), socket.RemoteEndPoint.ToString());
        ReceiveAsync();
        Write();
        socket.Close();
    }
}
復(fù)制代碼

     Client()方法是相應(yīng)的客戶端開啟方法,相對(duì)服務(wù)端來說代碼更簡(jiǎn)單明了,而且不需綁定IP,端口也是自動(dòng)分配,不過需注意Connect()方法中的IP和端口是服務(wù)端綁定的服務(wù)地址;ReceiveAysnc()、Write()和服務(wù)端中的兩個(gè)方法是一樣的;

復(fù)制代碼
using System.Text;

class Test_Tcp
{
    private const int BuffSize = 1024;
    private void ReceiveAsync()
    {
        if (socket == null || !socket.Connected) return;
        var Buff = new byte[BuffSize];
        socket.BeginReceive(Buff, 0, BuffSize, SocketFlags.None, OnReceived, Buff);
    }

    private void OnReceived(IAsyncResult result)
    {
        if (socket == null || !socket.Connected) return;
        byte[] data = (byte[])result.AsyncState;

        int recLength = socket.EndReceive(result);
        Console.WriteLine('Length = {0}', recLength);

        if (recLength <= 0) return;
        ReceiveAsync();

        string msg = Encoding.Default.GetString(data, 0, recLength);
        Console.WriteLine('Receive : {0}\n', msg);
    }

    private void Write()
    {
        while (socket != null && socket.Connected)
        {
            string msg = Console.ReadLine();
            if (msg == 'exit') break;
            byte[] buff = Encoding.Default.GetBytes(msg);
            socket.Send(buff);
        }
    }
}
復(fù)制代碼

    這3個(gè)方法是數(shù)據(jù)異步接收、數(shù)據(jù)接收回調(diào)、和聊天數(shù)據(jù)發(fā)送的方法,這里的字符串 - 字節(jié)序列轉(zhuǎn)換,需要用到System.Text命名空間;

    ReceiveAysnc()是開啟異步數(shù)據(jù)接收方法,其中Socket.BeginReceive方法是Socket類中的異步接收方法,它需要一個(gè)字節(jié)緩沖區(qū)和一個(gè)回調(diào)方法作為參數(shù);

    OnRecieve()數(shù)據(jù)接收回調(diào)方法,在這個(gè)方法中,主要工作是開啟新的異步接收方法RecieveAsync(),以及數(shù)據(jù)處理;這里的數(shù)據(jù)處理只是簡(jiǎn)單地轉(zhuǎn)換為字符串,并打印到控制臺(tái),而一般在實(shí)際應(yīng)用中,這里就收的數(shù)據(jù)data會(huì)用一個(gè)容器儲(chǔ)存起來(一般是隊(duì)列Queue),然后在其它地方從容器中取出數(shù)據(jù),并進(jìn)行復(fù)雜的處理;

    Write()方法:在網(wǎng)絡(luò)連接可用狀態(tài)下,不斷從控制臺(tái)等待讀取一行字符串,并將其轉(zhuǎn)換為字節(jié)序列發(fā)送到socket,服務(wù)器在異步接收線程中會(huì)接收到數(shù)據(jù),并觸發(fā)回調(diào)方法OnRecieve(),控制臺(tái)會(huì)看到打印的字符串;輸入exit終結(jié)循環(huán),Write()方法返回,接著關(guān)閉socket;

    Socket.Send發(fā)送數(shù)據(jù)方法是一個(gè)阻塞方法,它同樣也有異步版本BeginSend(),用法和BeginReceive()類似;

    在實(shí)際項(xiàng)目應(yīng)用中,數(shù)據(jù)的發(fā)送和接收會(huì)分別維護(hù)一個(gè)發(fā)送隊(duì)列和接收隊(duì)列,這樣,應(yīng)用層在調(diào)用數(shù)據(jù)發(fā)送方法時(shí),只是把數(shù)據(jù)加入到發(fā)送隊(duì)列,而不用等待發(fā)送完成,特別是數(shù)據(jù)量大的時(shí)候,等待發(fā)送的時(shí)間會(huì)影響到應(yīng)用層性能;真正的數(shù)據(jù)發(fā)送用一個(gè)專門的線程不斷從發(fā)送隊(duì)列里面取數(shù)據(jù)并發(fā)送;數(shù)據(jù)接收也是類似維護(hù)一個(gè)接收隊(duì)列。

 程序入口:

復(fù)制代碼
class Test_Tcp
{
    public void Run()
    {
        Console.WriteLine('Input 'c' or 's' :');
        var key = Console.ReadKey();
        Console.WriteLine();
        if (key.Key == ConsoleKey.C)
        {
            Client();
        }
        else if (key.Key == ConsoleKey.S)
        {
            Server();
        }
    }
}

class Program
{
    static int Main(string[] args)
    {
        new Test_Tcp().Run();
        return 0;
    }
}
復(fù)制代碼

(二)NetworkStream網(wǎng)絡(luò)流

    完全限定名System.Net.Sockets.NetworkStream網(wǎng)絡(luò)流類,繼承于Stream類,簡(jiǎn)單地理解就是對(duì)Socket讀寫網(wǎng)絡(luò)數(shù)據(jù)的封裝,用NetworkStream網(wǎng)絡(luò)流封裝Socket,可以簡(jiǎn)化數(shù)據(jù)的接收和發(fā)送;

    NetworkStream的構(gòu)造方法需要傳入一個(gè)可用的網(wǎng)絡(luò)Socket實(shí)例,然后就可以用流的方式替代Socket進(jìn)行數(shù)據(jù)的讀寫;

    var netStream = new NetworkStream(socket);

    這里的socket必須網(wǎng)絡(luò)連接成功(服務(wù)端連入客戶端,客戶端連接遠(yuǎn)程成功),才能用NetworkStream進(jìn)行數(shù)據(jù)流的讀寫;

(三)UDP和心跳包

    相比于TCP的可靠傳輸,UDP是一種非連接、不可靠的數(shù)據(jù)傳輸協(xié)議,它不需要建立連接,也就是說,服務(wù)端不需要監(jiān)聽Listen、接受連入Accept,而且Socket.Connected也不能用;Udp發(fā)送數(shù)據(jù)不需確定對(duì)方是否存在,網(wǎng)路是否可通,當(dāng)然也無法確定對(duì)方是否收到(但可以手動(dòng)發(fā)送返回包來通知對(duì)方),但是Udp相對(duì)Tcp的消耗也??;

    Socket的構(gòu)造方法第二、三個(gè)參數(shù)分別要設(shè)置為SocketType.Dgram、ProtocolType.Udp;

    相比于TCP連接,UDP的客戶端差別不大,但是在UDP服務(wù)端,由于沒有客戶端連接,數(shù)據(jù)的發(fā)送應(yīng)該使用SendTo,這個(gè)方法要求傳入一個(gè)客戶端地址結(jié)構(gòu)EndPoint表示目標(biāo)終端,那么,在接收數(shù)據(jù)時(shí),就應(yīng)該保存好數(shù)據(jù)的來源地址;

    那么,接收數(shù)據(jù)也應(yīng)該用另一個(gè)版本ReceiveFrom,這個(gè)方法可以得到一個(gè)數(shù)據(jù)來源地址EndPoint,這時(shí)就可以保存要用到的EndPoint,這個(gè)EndPoint可以用一個(gè)容器來維護(hù);

    這兩個(gè)方法在可靠連接Tcp中也可以用(但一般不這么用),另外它們都也有各自的異步版本,Begin開頭的便是;

    心跳包,顧名思義是一種在通信雙方,定時(shí)發(fā)送一個(gè)特定的數(shù)據(jù)序列,一般3-10s,用來通知對(duì)方網(wǎng)絡(luò)通信是正常的,一般這個(gè)數(shù)據(jù)序列短小、固定的;心跳包在Udp非連接協(xié)議中非常地必要,因?yàn)閁dp沒辦法在沒有數(shù)據(jù)接收的情況下確定網(wǎng)絡(luò)狀態(tài);在Tcp中心跳包不是必要的,但是要求較高的項(xiàng)目中依然會(huì)應(yīng)用心跳包;心跳超時(shí)(一般大于2倍的間隔時(shí)間),就表示網(wǎng)絡(luò)通信失聯(lián);

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多