程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 序列化和反序列化技術在編寫Socket應用程序時的應用

序列化和反序列化技術在編寫Socket應用程序時的應用

編輯:關於.NET

我們在編寫與Socket有關的應用程序時,在發送軟為復雜的數據時,可能我們最常做的是把各個部分的數據轉換為字符串,然後將這些字符串用一個分隔符連接起來進行發送。不過,不知道你有沒有想過這樣做還是有問題的。

比如,我用#來分隔各個字符串,在根據客戶端輸入的內容到服務器端進行查找,然後返回結果,萬一用戶輸入的查找關鍵字中就包含#,那麼就會影響我們對字符串進行分割了。

不知道各位有沒有想過,把序列化和反序列化的技術也用到socket上?先定義一個封裝數據的類,發送前將該類的對象序列化,然後發送;接收時,先接收字節流,然後再反序列化,得到某個對象。

這樣一來是不是比單單發送字符串好多了。

下面我舉的這個例子,服務器端用WPF開發,客戶端是Windows Store App,當然我這裡只是舉例,其實這可以用於所有類型的應用程序,包括Windows Phone應用,原理是不變的。

一、服務器端

首先我們定義一個用於封裝數據的類,這裡就以一個產品信息類做演示,這個類在服務器端和客戶端都要定義一遍,這樣才方便序列化和反序列化,你也可以特立寫到一個類庫中,然後服務器端和客戶端都引用該類庫。

[DataContract(Name = "product")]     
public class Product     
{     
    /// <summary>     
    /// 產品編號     
    /// </summary>     
    [DataMember(Name = "product_no")]     
    public string ProductNo { get; set; }     
    /// <summary>     
    /// 產品名稱     
    /// </summary>     
    [DataMember(Name = "product_name")]     
    public string ProductName { get; set; }     
    /// <summary>     
    /// 產品單價     
    /// </summary>     
    [DataMember(Name = "product_price")]     
    public decimal ProductPrice { get; set; }     
}

WPF窗口的XAML

<Grid>     
    <Grid.RowDefinitions>     
        <RowDefinition Height="auto"/>     
        <RowDefinition Height="auto"/>     
    </Grid.RowDefinitions>     
    <Grid Grid.Row="0" Margin="11">     
        <Grid.ColumnDefinitions>     
            <ColumnDefinition Width="auto"/>     
            <ColumnDefinition />     
        </Grid.ColumnDefinitions>     
        <Grid.RowDefinitions>     
            <RowDefinition Height="auto"/>     
            <RowDefinition Height="auto"/>     
            <RowDefinition Height="auto"/>     
        </Grid.RowDefinitions>     
        <TextBlock Text="產品編號:" Grid.Column="0" Grid.Row="0" Style="{DynamicResource tbLabel}"/>     
        <TextBlock Text="產品名稱:" Grid.Column="0" Grid.Row="1" Style="{DynamicResource tbLabel}"/>     
        <TextBlock Text="產品單價:" Grid.Column="0" Grid.Row="2" Style="{DynamicResource tbLabel}"/>     
        <TextBox x:Name="txtProductNo" Grid.Column="1" Grid.Row="0" Style="{DynamicResource txtInput}"/>     
        <TextBox x:Name="txtProductName" Grid.Column="1" Grid.Row="1" Style="{DynamicResource txtInput}"/>     
        <TextBox x:Name="txtProductPrice" Grid.Column="1" Grid.Row="2" Style="{DynamicResource txtInput}"/>     
    </Grid>     
    <StackPanel Grid.Row="1" Margin="9" Orientation="Horizontal">     
        <Button x:Name="btnStartListen" Content="開始偵聽" Padding="10,6" Click="OnStartListen"/>     
        <Button x:Name="btnStopListen" Content="停止偵聽" Padding="10,6" IsEnabled="False" Margin="18,0,0,0" Click="OnStopListen"/>     
    </StackPanel>     
</Grid>

處理代碼如下。

using System;     
using System.Collections.Generic;     
using System.Linq;     
using System.Text;     
using System.Windows;     
using System.Windows.Controls;     
using System.Windows.Data;     
using System.Windows.Documents;     
using System.Windows.Input;     
using System.Windows.Media;     
using System.Windows.Media.Imaging;     
using System.Windows.Navigation;     
using System.Windows.Shapes;     
using System.IO;     
using System.Net;     
using System.Net.Sockets;     
using System.Runtime.Serialization;     
using System.Runtime.Serialization.Json;     
         
namespace ServerApp     
{     
    /// <summary>     
    /// MainWindow.xaml 的交互邏輯     
    /// </summary>     
    public partial class MainWindow : Window     
    {     
        TcpListener listener = null;     
        const int LOCAL_PORT = 2785;//監聽端口     
        public MainWindow()     
        {     
            InitializeComponent();     
        }     
         
        private void OnStartListen(object sender, RoutedEventArgs e)     
        {     
            this.listener = new TcpListener(IPAddress.Any, LOCAL_PORT);     
            this.listener.Start();     
            btnStartListen.IsEnabled = false;     
            btnStopListen.IsEnabled = true;     
            // 接受傳入連接     
            decimal d;     
            if (decimal.TryParse(txtProductPrice.Text, out d) == false)     
            {     
                d = 0.00M;     
            }     
            Product prd = new Product     
            {     
                ProductNo = txtProductNo.Text == "" ? "000" : txtProductNo.Text,     
                ProductName = txtProductName.Text == "" ? "No Name" : txtProductName.Text,     
                ProductPrice = d     
            };     
            listener.BeginAcceptTcpClient(new AsyncCallback(EndAcceptClientMethod), prd);     
            MessageBox.Show("正在接受連接。");     
        }     
         
        private void EndAcceptClientMethod(IAsyncResult ar)     
        {     
            Product prd = ar.AsyncState as Product;     
            TcpClient client = null;     
            // 發送消息     
            byte[] sendBuffer = this.SerializeObject<Product>(prd);     
            try 
            {     
                client = listener.EndAcceptTcpClient(ar);     
                var networkStream = client.GetStream();     
                // 先發送數據長度     
                byte[] bfLen = BitConverter.GetBytes(sendBuffer.Length);     
                networkStream.Write(bfLen, 0, 4);     
                // 發送數據     
                networkStream.Write(sendBuffer, 0, sendBuffer.Length);     
            }     
            catch (SocketException ex)     
            {     
                System.Diagnostics.Debug.WriteLine(ex.Message);     
            }     
            catch (ObjectDisposedException dex)     
            {     
                System.Diagnostics.Debug.WriteLine("對象已釋放。" + dex.Message);     
            }     
            catch (Exception eex)     
            {     
                System.Diagnostics.Debug.WriteLine(eex.Message);     
            }     
            finally 
            {     
                if (client != null)     
                {     
                    client.Close();     
                }     
            }     
            // 繼續接受連接     
            try 
            {     
                listener.BeginAcceptTcpClient(new AsyncCallback(EndAcceptClientMethod), prd);     
            }     
            catch { }     
        }     
         
        private void OnStopListen(object sender, RoutedEventArgs e)     
        {     
            if (this.listener != null)     
            {     
                this.listener.Stop();     
            }     
            btnStartListen.IsEnabled = true;     
            btnStopListen.IsEnabled = false;     
        }     
         
        /// <summary>     
        /// 將對象序列化     
        /// </summary>     
        /// <typeparam name="T">要進行序列化的類型</typeparam>     
        /// <param name="t">要序列化的對象</param>     
        /// <returns>序列化後的字節數組</returns>     
        private byte[] SerializeObject<T>(T t) where T : class 
        {     
            byte[] buffer = null;     
            // 開始序列化     
            using (MemoryStream ms = new MemoryStream())     
            {     
                DataContractJsonSerializer ss = new DataContractJsonSerializer(t.GetType());     
                ss.WriteObject(ms, t);     
                buffer = ms.ToArray();     
            }     
            return buffer;     
        }     
    }     
         
    [DataContract(Name = "product")]     
    public class Product     
    {     
        /// <summary>     
        /// 產品編號     
        /// </summary>     
        [DataMember(Name = "product_no")]     
        public string ProductNo { get; set; }     
        /// <summary>     
        /// 產品名稱     
        /// </summary>     
        [DataMember(Name = "product_name")]     
        public string ProductName { get; set; }     
        /// <summary>     
        /// 產品單價     
        /// </summary>     
        [DataMember(Name = "product_price")]     
        public decimal ProductPrice { get; set; }     
    }     
}

由於只做演示,接受連接後只發送一次數據就關閉連接。

二、客戶端

這裡只放出核心代碼。

namespace ClientApp     
{     
    /// <summary>     
    /// 可用於自身或導航至 Frame 內部的空白頁。     
    /// </summary>     
    public sealed partial class MainPage : Page     
    {     
        public MainPage()     
        {     
            this.InitializeComponent();     
        }     
         
        /// <summary>     
        /// 在此頁將要在 Frame 中顯示時進行調用。     
        /// </summary>     
        /// <param name="e">描述如何訪問此頁的事件數據。Parameter     
        /// 屬性通常用於配置頁。</param>     
        protected override void OnNavigatedTo(NavigationEventArgs e)     
        {     
        }     
         
        /// <summary>     
        /// 反序列化對象     
        /// </summary>     
        /// <typeparam name="T">要反序列化的類型</typeparam>     
        /// <param name="buffer">字節數組</param>     
        /// <returns>反序列化後的對象</returns>     
        private T DeSerializeObject<T>(byte[] buffer) where T : class 
        {     
            T t = default(T);     
            using (MemoryStream ms = new MemoryStream(buffer))     
            {     
                ms.Position = 0;     
                DataContractJsonSerializer ss = new DataContractJsonSerializer(typeof(T));     
                t = (T)ss.ReadObject(ms);     
            }     
            return t;     
        }     
         
        private async void OnClick(object sender, RoutedEventArgs e)     
        {     
            StreamSocket socket = new StreamSocket();     
            HostName host = new HostName(txtHost.Text);     
            // 連接服務器     
            await socket.ConnectAsync(host, txtPort.Text);     
            // 讀取數據     
            DataReader reader = new DataReader(socket.InputStream);     
            reader.ByteOrder = ByteOrder.LittleEndian;     
            // 加載4字節,讀取長度     
            await reader.LoadAsync(sizeof(int));     
            int len = reader.ReadInt32();     
            // 加載剩余字節     
            await reader.LoadAsync((uint)len);     
            IBuffer readBuffer = reader.ReadBuffer((uint)len);     
            // 反序列化     
            Product prd = this.DeSerializeObject<Product>(readBuffer.ToArray());     
            if (prd != null)     
            {     
                this.tbContent.Text = string.Format("產品編號:{0}\n產品名稱:{1}\n產品單價:{2}", prd.ProductNo, prd.ProductName, prd.ProductPrice.ToString("C2"));     
            }     
        }     
    }     
         
    [DataContract(Name = "product")]     
    public class Product     
    {     
        /// <summary>     
        /// 產品編號     
        /// </summary>     
        [DataMember(Name = "product_no")]     
        public string ProductNo { get; set; }     
        /// <summary>     
        /// 產品名稱     
        /// </summary>     
        [DataMember(Name = "product_name")]     

        public string ProductName { get; set; }     
        /// <summary>     
        /// 產品單價     
        /// </summary>     
        [DataMember(Name = "product_price")]     
        public decimal ProductPrice { get; set; }     
    }     
         
         
}

下在是測試結果。

本例我使用的是JSON序列化和反序列化。

這個例子只是知識與技巧的綜合,沒有涉及到新的概念,所以就不多解釋了。用socket收發數據比較好的做法是先將要發送的數據的長度先發送給對方,然後再發數據內容,這樣可以保證正確的讀取數據。

大家在編寫socket應用程序時,不妨試試這種思路。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved