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

Windows 8 / UWP 應用開發:StreamSocket 實現與桌面端 TCP 通訊完整實戰

     編輯:關於.NET

前言

Windows 8 (WinRT) 與 UWP 應用通過 Windows.Networking.Sockets.StreamSocket 進行 TCP 通訊。本文提供一個完整的「WinForms 桌面服務端 + UWP 客戶端」示例,重點解決 DataReader 讀取長度前綴消息的常見陷阱,實現可靠的字串/二進制數據雙向傳輸。

服務端:WinForms TCP Listener

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestServerApp
{
    public partial class Form1 : Form
    {
        private TcpListener _listener;
        private TcpClient _client;

        public Form1()
        {
            InitializeComponent();
            btnStart.Enabled = true;
            btnStop.Enabled = false;
        }

        private void SendMessage(TcpClient client)
        {
            using (var stream = client.GetStream())
            {
                string msg = "奔,不停地奔,奔向傳說中的荒原;飛,不停地飛,飛向天空的那一端。";
                byte[] buffer = Encoding.UTF8.GetBytes(msg);
                uint len = (uint)buffer.Length;
                // 先發送 4 字節長度 (大端序/網絡序)
                stream.Write(BitConverter.GetBytes(len), 0, sizeof(uint));
                // 再發送數據
                stream.Write(buffer, 0, buffer.Length);
            }
        }

        private async void btnStart_Click(object sender, EventArgs e)
        {
            if (_listener == null)
                _listener = new TcpListener(IPAddress.Parse(txtAddr.Text), (int)udPort.Value);

            _listener.Start();
            lblMsg.Text = "監聽已開始。";
            btnStart.Enabled = false;
            btnStop.Enabled = true;

            try
            {
                _client = await _listener.AcceptTcpClientAsync();
                SendMessage(_client);
            }
            catch (SocketException se) { lblMsg.Text = se.Message; }
            catch (Exception ex) { lblMsg.Text = ex.Message; }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            _listener?.Stop();
            lblMsg.Text = "監聽已停止。";
            btnStart.Enabled = true;
            btnStop.Enabled = false;
        }
    }
}

客戶端:UWP StreamSocket

1. Package.appxmanifest 勾選網絡能力

雙擊 `Package.appxmanifest` → **功能** 選項卡 → 勾選:

  • Internet (Client & Server) (出站/入站)
  • Private Networks (Client & Server) (本地網絡)

網絡能力勾選

2. MainPage.xaml 介面

<Page
    x:Class="WCleintApp.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <Grid Grid.Row="0" Margin="3">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="服務器:"/>
            <TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Text="端口:"/>
            <TextBox x:Name="txtServer" Grid.Row="0" Grid.Column="1" Margin="2,3"/>
            <TextBox x:Name="txtPort" Grid.Row="1" Grid.Column="1" Margin="2,3"/>
        </Grid>

        <StackPanel Grid.Row="1" Margin="5">
            <Button Content="連接" Click="onConnClick" Margin="3,8,0,12" Padding="4,2.5"/>
            <TextBox x:Name="txtRec" Margin="5,7,0,0" Height="200" IsReadOnly="True"/>
        </StackPanel>
    </Grid>
</Page>

3. MainPage.xaml.cs 核心邏輯

using System;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace WCleintApp
{
    public sealed partial class MainPage : Page
    {
        private StreamSocket _socket;
        private DataReader _reader;

        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void onConnClick(object sender, RoutedEventArgs e)
        {
            if (_socket != null)
            {
                _socket.Dispose();
                _reader?.DetachStream();
            }

            _socket = new StreamSocket();
            _reader = new DataReader(_socket.InputStream);
            // 關鍵:設定 Unicode 編碼 (UTF-8) 與位元組序 (小端序/大端序需與發送端一致)
            _reader.UnicodeEncoding = UnicodeEncoding.Utf8;
            _reader.ByteOrder = ByteOrder.BigEndian; // 網絡序為大端序

            try
            {
                HostName host = new HostName(txtServer.Text);
                string port = txtPort.Text;
                await _socket.ConnectAsync(host, port);
                txtRec.Text = "連接成功,正在接收數據...";

                // 1. 讀取長度前綴 (4 字節 uint)
                uint len = await _reader.LoadAsync(sizeof(uint));
                if (len != sizeof(uint)) { txtRec.Text = "讀取長度失敗"; return; }
                uint dataLength = _reader.ReadUInt32();

                // 2. 讀取實際數據
                uint loaded = await _reader.LoadAsync(dataLength);
                if (loaded != dataLength) { txtRec.Text = "數據不完整"; return; }
                string received = _reader.ReadString(dataLength);

                txtRec.Text = received;
            }
            catch (Exception ex)
            {
                txtRec.Text = "錯誤: " + ex.Message;
            }
        }
    }
}

關鍵陷阱與解決方案

問題原因解決
DataReader 讀不到數據 / 讀到亂碼未正確設定 UnicodeEncoding 與 ByteOrder,或未先 LoadAsync 再 ReadXxx設定 _reader.UnicodeEncoding = UnicodeEncoding.Utf8; _reader.ByteOrder = ByteOrder.BigEndian; (與發送端 BitConverter.GetBytes 預設小端序不同,需雙方協商一致),嚴格遵循 await LoadAsync(n) 後再 ReadXxx
連接超時 / 拒絕防火牆阻擋、IP/端口錯誤、服務端未啟動檢查 Windows 防火牆入站規則、確認服務端 IP (本機用 127.0.0.1)、端口一致
發送中文亂碼編碼不一致雙方統一 UTF-8:服務端 Encoding.UTF8.GetBytes,客戶端 UnicodeEncoding.Utf8
大數據傳輸卡死未分片發送/接收,緩衝區滿應用層協議設計:長度前綴 + 分片循環 LoadAsync/ReadBytes,或使用 StreamSocketListener 服務端模式

擴展:雙向通訊與心跳

  • 客戶端發送:DataWriter writer = new DataWriter(_socket.OutputStream); writer.WriteUInt32((uint)buffer.Length); writer.WriteBytes(buffer); await writer.StoreAsync(); writer.DetachStream();
  • 服務端接收:同樣用 NetworkStream 循環 Read 長度再讀數據。
  • 心跳:定時發送固定長度極小包 (如 "PING"),超時未收到回應視為斷線重連。

總結

WinRT/UWP StreamSocket + DataReader/DataWriter 提供了現代異步 TCP 程式設計模型,關鍵在於:**協議設計 (長度前綴)、編碼統一 (UTF-8)、位元組序協商 (BigEndian)、嚴格的 LoadAsync/Read 配對**。掌握這些細節,即可構建穩定高效的跨平台 (桌面/手機/IoT) 網絡應用。

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