首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 網路程式設計:非同步傳輸字串 (5) — 伺服器/客戶端非同步完整實作

     編輯:關於C語言

伺服器端:主程式極度精簡

得益於 RemoteClient 建構函式已完成初始化,主程式僅需建立 TcpListener、進入迴圈接受連線、新增包裝物件即可:

class Server {
    static void Main(string[] args) {
        Console.WriteLine("Server is running ... ");
        IPAddress ip = new IPAddress(new byte[] { 127, 0, 0, 1 });
        TcpListener listener = new TcpListener(ip, 8500);

        listener.Start();
        Console.WriteLine("Start Listening ...");

        while (true) {
            TcpClient client = listener.AcceptTcpClient(); // 同步阻塞取得連線
            RemoteClient wrapper = new RemoteClient(client);
        }
    }
}

客戶端實作:ServerClient 包裝類別

為方便使用,將 TcpClient 包裝為 ServerClient,內含緩衝區、資料流、非同步讀取回調等完整邏輯:

public class ServerClient {
    private const int BufferSize = 8192;
    private byte[] buffer;
    private TcpClient client;
    private NetworkStream streamToServer;
    private string msg = "Welcome to TraceFact.Net!";

    public ServerClient() {
        try {
            client = new TcpClient();
            client.Connect("localhost", 8500);
        } catch (Exception ex) {
            Console.WriteLine(ex.Message);
            return;
        }
        buffer = new byte[BufferSize];
        Console.WriteLine("Server Connected!{0} --> {1}",
            client.Client.LocalEndPoint, client.Client.RemoteEndPoint);
        streamToServer = client.GetStream();
    }

    // 發送訊息(含協議長度前綴)
    public void SendMessage(string msg) {
        msg = String.Format("[length={0}]{1}", msg.Length, msg);
        for (int i = 0; i <= 2; i++) {
            byte[] temp = Encoding.Unicode.GetBytes(msg);
            try {
                streamToServer.Write(temp, 0, temp.Length);
                Console.WriteLine("Sent: {0}", msg);
            } catch (Exception ex) {
                Console.WriteLine(ex.Message);
                break;
            }
        }
        // 發送完成後啟動非同步讀取
        lock (streamToServer) {
            AsyncCallback callback = new AsyncCallback(ReadComplete);
            streamToServer.BeginRead(buffer, 0, BufferSize, callback, null);
        }
    }

    public void SendMessage() { SendMessage(this.msg); }

    // 非同步讀取完成回調
    private void ReadComplete(IAsyncResult ar) {
        int bytesRead;
        try {
            lock (streamToServer) {
                bytesRead = streamToServer.EndRead(ar);
            }
            if (bytesRead == 0) throw new Exception("讀取到 0 位元組");

            string msg = Encoding.Unicode.GetString(buffer, 0, bytesRead);
            Console.WriteLine("Received: {0}", msg);
            Array.Clear(buffer, 0, buffer.Length); // 清除髒讀

            // 繼續下一輪讀取
            lock (streamToServer) {
                AsyncCallback callback = new AsyncCallback(ReadComplete);
                streamToServer.BeginRead(buffer, 0, BufferSize, callback, null);
            }
        } catch (Exception ex) {
            if (streamToServer != null) streamToServer.Dispose();
            client.Close();
            Console.WriteLine(ex.Message);
        }
    }
}

重點:SendMessage 中對 streamToServer 加鎖,因 SendFile 為多執行緒存取且會呼叫 SendMessage;SendStatus 類別獨立負責顯示傳送進度,符合單一職責原則。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved