【Win 10 應用開發】TCP通信過程


基於TCP協議的通信,估計大伙兒都不陌生的,以前玩.net或玩C++的時候應該玩得很多吧。現在老周簡單介紹一下在RT中如何用。

TCP是基於連接的,所以,肯定有一方是監聽者,通常稱服務端或服務器,它負責接受連接請求,但不負責通信;接受連接后得到一個專用於通信的套接字。

 

1、new一個StreamSocketListener,它用於監聽TCP連接。

2、處理StreamSocketListener實例的ConnectionReceived事件,當有新連接傳入,會發生該事件,並可以獲得用於通信的socket。

3、綁定本地結點。BindEndpointAsync綁定特定本機地址和端口(或服務,如果是藍牙通信,就是服務名,大多數情況下是端口號)。BindServiceNameAsync方法綁定本地端號或服務,該方法不指定地址,即綁定本機所有地址,如果有需要,你可以指定綁定到哪張網卡。如果所指定的端口是空白字符串("",不能為null),就會自動選擇一個隨機端口進行綁定。要是綁定的是本地的隨機端口,你可以從StreamSocketListener.Information的LocalPort屬性中獲取已綁定的端口。

4、在StreamSocketListener.ConnectionReceived事件的處理中,訪問事件參數的Socket屬性得到一個StreamSocket實例,然后你就可以用它來進行通信了。

5、當不需要時調用Dispose方法即可釋放。

 

下面來練習一下。老周發現一個現象,UWP兩個應用程序在同一台機器上不能連接,要用兩台機器來測試,但在同一個應用中就可以本地測試。

不過,后來想想,其實也無妨,畢竟UWP是通用應用,如果服務器一個應用,客戶一個應用,這樣反而不合理了,因為這樣用戶就要安裝兩個應用,在通用平台而言不太好,把服務器和客戶端都放在同一個應用中較好,讓用戶自行選擇是作為服務器端還是客戶端來運行。如果用戶選擇當前應用作為服務器,就開啟監聽;如果用戶選擇作為客戶端運行,就允許其輸入遠程設備的IP和端口進行連接。

 

下面代碼開啟連接監聽並綁定機地端口。

            if (listener != null)
            {
                listener.ConnectionReceived -= OnConnReceived;
                listener.Dispose();
                listener = null;
            }

            listener = new StreamSocketListener();
            listener.ConnectionReceived += OnConnReceived;
            await listener.BindServiceNameAsync("");

調用BindServiceNameAsync時傳遞的是空字符串的參數,表示讓應用程序自動選擇一個隨機端口來監聽。為了讓客戶端知道該連接哪個端口,可以把本地監聽端口顯示在界面上。

  runPort.Text = listener.Information.LocalPort;

 

 

處理ConnectionReceived事件,如果接收到連接請求,就向客戶端發送一條文本消息:“你好,我是你外公,我叫服務器。”。

        private async void OnConnReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
        {
            // 獲取用於通信的socket
            StreamSocket socket = args.Socket;
            // 向客戶端發送字符串:
            //        你好,我是你外公,我叫服務器。
            using (DataWriter writer = new DataWriter(socket.OutputStream))
            {
                string content = "你好,我是你外公,我叫服務器。";
                writer.UnicodeEncoding = UnicodeEncoding.Utf8; //注意
                // 計算長度
                uint len = writer.MeasureString(content);
                // 寫入長度
                writer.WriteUInt32(len);
                // 再寫內容
                writer.WriteString(content);
                // 提交數據
                await writer.StoreAsync();
            }
            // 這個socket不要了,扔掉
            socket.Dispose();
        }


前一文章中,老周給大伙介紹過DataWriter的作用,這時我們用得上,用來把字符串寫入網絡流。注意,應該設置UnicodeEncoding屬性為Utf-8編碼,這個編碼比較通用,就不會出現亂碼。

由於字符串的長度是可變的,客戶端並不知道我們要發送的內容有多大,為了讓接收者能夠准確接收數據,應該先向流中寫入數據長度,然后再寫內容。接收方在讀的時候,可以先讀出長度,再讀內容,因為表示長度的值是uint,它的值大小是固定的4個字節。

 

下面代碼為客戶端發起連接。

            StreamSocket socket = new StreamSocket();
            try
            {
                HostName svname = new HostName(txtIp.Text);

                // 連接
                await socket.ConnectAsync(svname, txtPort.Text);
                // 接收數據
                DataReader reader = new DataReader(socket.InputStream);
                reader.UnicodeEncoding = UnicodeEncoding.Utf8; //注意
                // 長度
                await reader.LoadAsync(sizeof(uint));
                uint len = reader.ReadUInt32();
                // 讀內容
                await reader.LoadAsync(len);
                string msg = reader.ReadString(reader.UnconsumedBufferLength);
                runRecMsg.Text = msg;
                // 釋放
                reader.Dispose();


在讀取接收到的數據時,用的是DataReader類,而且記住要統一編碼utf-8,然后先加載4個字節,讀出內容長度,再加載剩余的字節,最后讀出字符串。

 

好,最后一步就是配置清單文件,打開清單文件,默認用設計器打開,切換到[功能]選項卡,勾選“Internet(客戶端與服務器)”與“專用網絡(客戶端與服務器)”,而“Internet(客戶端)”可以取消。

 

XML代碼如下。

  <Capabilities>
    <Capability Name="internetClientServer" />
    <Capability Name="privateNetworkClientServer" />
  </Capabilities>

 

運行結果請看下面的艷圖。

 

啊,今天的話題就扯到這里吧,改天再扯其他話題。

 

示例源代碼下載

 


免責聲明!

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



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