午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

JSON.NET與ProtoBuf在Socket下的應(yīng)用

 9loong 2012-08-07

1:前言

Socket通信中,客戶端與服務(wù)器之間傳遞的是字節(jié)流。而在現(xiàn)實(shí)的應(yīng)用中我們需要傳遞有一定含義的結(jié)構(gòu)。如何傳遞有意義的結(jié)構(gòu)那?別慌本文就從這里給您做個(gè)簡單介紹。
首先我們來簡單認(rèn)識一下今天的主角:JSON.NET和ProtoBuf

2:JSON.NET與ProtoBuf

這兩個(gè)都是開源的項(xiàng)目,項(xiàng)目的地址如下
接下來我們看看兩個(gè)項(xiàng)目在序列化對象時(shí)都是怎么做的。
先看JSON.NET

    [JsonObject]
    
public class Person {
        
public string userName { getset; }
        
public string pwd { getset; }

        
public Person(string name, string code) {
            userName = name;
            pwd = code;
        }

        
public void Write() {
            Console.WriteLine(string.Format("用戶名:" + userName + "密碼:" + pwd));
        }

    }
public class Json {

        
private string jsonStr;
        
private List<Person> list;


        
public Json(int num) {
            list = new List<Person>();

            Person p = new Person("dabing""110110");
            
for (int i = 0; i < num;i++ )
                list.Add(p);

        }

        
#region json
        
public void Set() {
            
//jsonStr = JsonConvert.SerializeObject(list, Formatting.Indented, new JsonSerializerSettings() {
            
//    TypeNameHandling = TypeNameHandling.Objects
            
//});
            Stopwatch watch = new Stopwatch();
            watch.Start();
            jsonStr = JsonConvert.SerializeObject(list);
            watch.Stop();
            Console.WriteLine("寫入耗時(shí)(MS):" + watch.ElapsedMilliseconds);
        }

        
public List<Person> Get()
        {
            
//object person = JsonConvert.DeserializeObject(jsonStr, null, new JsonSerializerSettings {
            
//    TypeNameHandling = TypeNameHandling.Objects
            
//});
            Stopwatch watch = new Stopwatch();
            watch.Start();
            List<Person> obj = JsonConvert.DeserializeObject<List<Person>>(jsonStr);
            watch.Stop();
            Console.WriteLine("獲取耗時(shí)(MS):" + watch.ElapsedMilliseconds);
            
return obj;

        }
        
#endregion

我們可以看到它對序列化的對象沒有什么要求。(“[JsonObject]”可以去掉)
其實(shí)JSON的原理也很簡單,底層通過反射獲取對象的屬性然后拼接出形如[{"userName":"dabing","pwd":"110110"},{"userName":"dabing","pwd":"110110"}]的字符串。
下面我們看ProtoBuf

    [DataContract]
    
public class PBPerson {

        [ProtoMember(1)]
        
public string userName { getset; }

        [ProtoMember(2)]
        
public string pwd { getset; }

        
public void Write() {
            Console.WriteLine(string.Format("用戶名:" + userName + "密碼:" + pwd));
        }

    }
public class Protobuf {

        MemoryStream ms;
        List<PBPerson> list;

        
public Protobuf(int num) {
            ms = new MemoryStream();
            list = new List<PBPerson>();

            PBPerson p = new PBPerson();
            p.userName = "fengyun";
            p.pwd = "110110";

            
for (int i = 0; i < num; i++) {
                list.Add(p);
            }

        }
        
        
#region ProtoBuf
        
public void Set() {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            Serializer.Serialize(ms,list);
            watch.Stop();
            Console.WriteLine("寫入耗時(shí)(MS):" + watch.ElapsedMilliseconds);
        }

        
public List<PBPerson> Get() {
            ms.Position = 0;

            Stopwatch watch = new Stopwatch();
            watch.Start();
            List<PBPerson> obj=Serializer.Deserialize<List<PBPerson>>(ms);
            watch.Stop();
            Console.WriteLine("獲取耗時(shí)(MS):" + watch.ElapsedMilliseconds);
            
return obj;
        }
        
#endregion
ProtoBuf對要序列化的對象要求首先要有特性來規(guī)定像[DataContract],[ProtoMember(1)]其次就是不能有帶參的構(gòu)造函數(shù)。

3:JSON.NET與ProtoBuf性能的簡單對比

100(J/P) 1000(J/P) 10000(J/P) 100000(J/P)
寫 53/100 64/104 162/114 1139/239
讀     29/13           64/16              382/42          3561/322
以上表格中100(J/P)表示100個(gè)對象在JSON/ProtoBuf下耗費(fèi)的MS。
以上數(shù)據(jù)為三次得到的平均值。

從以上數(shù)據(jù)我們可以簡單得出結(jié)論(僅供參考)
傳遞的對象越多兩者耗費(fèi)的時(shí)間越長。
傳遞單個(gè)對象的時(shí)候JSON表現(xiàn)出更好的性能。傳遞多個(gè)對象的時(shí)候ProtoBuf性能更快更穩(wěn)定。
到這里我們已經(jīng)把兩種框架下序列化和反序列化對象的方法和性能進(jìn)行了簡單的說明,接下來我們再看看兩個(gè)框架在Socket下是如何應(yīng)用的。

4:JSON.NET與ProtoBuf在Socket下的寫法

以JSON方式傳遞對象數(shù)組

    public class SocketServer {

        RequestHandler handler;
        Socket listenSocket;

        
public void Start() {

            IPAddress[] addressList = Dns.GetHostEntry(Environment.MachineName).AddressList;
            IPEndPoint localEndPoint = new IPEndPoint(addressList[addressList.Length - 1], 12345);

            
this.listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            
if (localEndPoint.AddressFamily == AddressFamily.InterNetworkV6) {
                
this.listenSocket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27false);
                
this.listenSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, localEndPoint.Port));
            }
            
else {
                
this.listenSocket.Bind(localEndPoint);
            }

            
this.listenSocket.Listen(100);

            
this.accept_async();

            handler = new RequestHandler();
        }

        
private void accept_async() {
            SocketAsyncEventArgs accept = new SocketAsyncEventArgs();
            accept.Completed += accept_Completed;
            listenSocket.AcceptAsync(accept);
        }

        
void accept_Completed(object sender, SocketAsyncEventArgs e) {
            accept_async();
            var client = e.AcceptSocket;
            e.Completed -= accept_Completed;
            e.Completed += receive_Completed;

            var buffer = new byte[1024];
            e.SetBuffer(buffer, 0, buffer.Length);

            client.ReceiveAsync(e);
        }

        
void receive_Completed(object sender, SocketAsyncEventArgs e) {
            var client = sender as Socket;
            
if (e.BytesTransferred == 0) {
                client.Close();
                e.Dispose();
            }
            
else {
                String received = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
                
string[] msgArray = handler.GetActualString(received);
                
foreach (string m in msgArray) {
                    List<Entitly.Person> obj = JsonConvert.DeserializeObject<List<Entitly.Person>>(m);
                    
foreach (Entitly.Person p in obj) {
                        p.userName = "fengyun";
                    }
                    received = JsonConvert.SerializeObject(obj);
                    received = String.Format("[length={0}]{1}", received.Length, received);

                    
byte[] buffer = Encoding.UTF8.GetBytes(received);
                    client.Send(buffer);
                }
                client.ReceiveAsync(e);
            }
        }
    }

    
class Program {
        
static void Main(string[] args) {
            SocketServer server = new SocketServer();
            server.Start();

            Console.ReadLine();
        }
客戶端

    public sealed class SocketClient : IDisposable {
        RequestHandler handler;
        
/// <summary>
        
///  發(fā)送或接受操作
        
/// </summary>
        private const Int32 ReceiveOperation = 1, SendOperation = 0;

        
/// <summary>
        
/// 客戶端套接字
        
/// </summary>
        private Socket clientSocket;

        
/// <summary>
        
/// 是否鏈接到服務(wù)器
        
/// </summary>
        private Boolean connected = false;

        
/// <summary>
        
/// 接收端口{本地}
        
/// </summary>
        private IPEndPoint hostEndPoint;

        
/// <summary>
        
/// 連接信號量
        
/// </summary>
        private AutoResetEvent autoConnectEvent = new AutoResetEvent(false);

        
/// <summary>
        
/// 操作信號量
        
/// </summary>
        private AutoResetEvent[] autoSendReceiveEvents = new AutoResetEvent[]
        {
            
new AutoResetEvent(false),
            
new AutoResetEvent(false)
        };

        
public static object ConnLock = new object();

        
/// <summary>
        
/// 初始化客戶端
        
/// 鏈接到服務(wù)器后開始發(fā)送數(shù)據(jù)
        
/// </summary>
        
/// <param name="hostName">服務(wù)端地址{IP地址}</param>
        
/// <param name="port">端口</param>
        public SocketClient(String hostName, Int32 port) {
            IPHostEntry host = Dns.GetHostEntry(hostName);

            IPAddress[] addressList = host.AddressList;

            
this.hostEndPoint = new IPEndPoint(addressList[addressList.Length - 1], port);
            
this.clientSocket = new Socket(this.hostEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            handler = new RequestHandler();
        }

        
/// <summary>
        
/// 連接到服務(wù)器過程
        
/// </summary>
        
/// <returns>連接上為True否則為False</returns>
        public void Connect() {

            
lock (ConnLock) {
                
try {
                    clientSocket.Connect(this.hostEndPoint);
                    
this.connected = true;
                }
                
catch (Exception ex) {
                    
this.connected = false;
                }
            }

        }

        
/// <summary>
        
/// 斷開與服務(wù)器的鏈接
        
/// </summary>
        public void Disconnect() {
            clientSocket.Disconnect(false);
        }

        
private void OnConnect(object sender, SocketAsyncEventArgs e) {
            
// 通知連接已經(jīng)完成
            autoConnectEvent.Set();

            
this.connected = (e.SocketError == SocketError.Success);
        }

        
/// <summary>
        
/// 接收
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void OnReceive(object sender, SocketAsyncEventArgs e) {
            
string msg = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
            
string[] msgArray = handler.GetActualString(msg);
            
foreach (string m in msgArray) {
                List<Entitly.Person> obj = JsonConvert.DeserializeObject<List<Entitly.Person>>(m);
                
foreach (Entitly.Person p in obj) {
                    Console.WriteLine(p.userName);
                }
            }

            autoSendReceiveEvents[SendOperation].Set();

            (e.UserToken as Socket).ReceiveAsync(e);

        }

        
/// <summary>
        
/// 發(fā)送
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>
        private void OnSend(object sender, SocketAsyncEventArgs e) {
            
//發(fā)送完后置信號為接收
            autoSendReceiveEvents[ReceiveOperation].Set();

            
if (e.SocketError == SocketError.Success) {
                
if (e.LastOperation == SocketAsyncOperation.Send) {
                    Socket s = e.UserToken as Socket;
                    
byte[] receiveBuffer = new byte[255];
                    e.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
                    e.Completed += new EventHandler<SocketAsyncEventArgs>(OnReceive);
                    s.ReceiveAsync(e);
                }
            }
            
else {
                
this.ProcessError(e);
            }
        }

        
/// <summary>
        
/// 關(guān)閉客戶端
        
/// </summary>
        
/// <param name="e">SocketAsyncEventArg</param>
        private void ProcessError(SocketAsyncEventArgs e) {
            Socket s = e.UserToken as Socket;
            
if (s.Connected) {
                
//關(guān)閉一個(gè)獨(dú)立的客戶端連接
                try {
                    s.Shutdown(SocketShutdown.Both);
                }
                
catch (Exception) {
                    
//客戶端已經(jīng)關(guān)閉
                }
                
finally {
                    
if (s.Connected) {
                        s.Close();
                    }
                }
            }

            
throw new SocketException((Int32)e.SocketError);
        }

        
/// <summary>
        
/// 發(fā)送過程
        
/// </summary>
        
/// <param name="message">Message to send.</param>
        
/// <returns>Message sent by the host.</returns>
        public void Send(String message) {
            
if (this.connected) {

                
//將信息轉(zhuǎn)化為協(xié)議
                message = String.Format("[length={0}]{1}", message.Length, message);
                Byte[] sendBuffer = Encoding.UTF8.GetBytes(message);

                SocketAsyncEventArgs completeArgs = new SocketAsyncEventArgs();
                completeArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
                completeArgs.UserToken = this.clientSocket;
                completeArgs.RemoteEndPoint = this.hostEndPoint;
                completeArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnSend);

                clientSocket.SendAsync(completeArgs);

                AutoResetEvent.WaitAll(autoSendReceiveEvents);
            }
            
else {
                
throw new SocketException((Int32)SocketError.NotConnected);
            }
        }

        
#region IDisposable Members

        
/// <summary>
        
/// 銷毀客戶端
        
/// </summary>
        public void Dispose() {
            
this.connected = false;
            autoConnectEvent.Reset();
            autoSendReceiveEvents[SendOperation].Reset();
            autoSendReceiveEvents[ReceiveOperation].Reset();
            
if (this.clientSocket.Connected) {
                
this.clientSocket.Close();
            }
        }

        
#endregion
    }

    
class Program {
        
static void Main(string[] args) {

            String host = "192.168.65.35";
            Int32 port = 12345;
            
int num = 100;

            Entitly.Person person = new Entitly.Person("dabing""110110");
            List<Entitly.Person> list = new List<Entitly.Person>();
            
for (int i = 0; i < num; i++) {
                list.Add(person);
            }

            
string msg = JsonConvert.SerializeObject(list);

            
using (SocketClient sa = new SocketClient(host, port)) {
                sa.Connect();
                sa.Send(msg);
                sa.Disconnect();
            }


            Console.ReadLine();

        }
還有實(shí)體類

    public class Person {
        
public string userName { getset; }
        
public string pwd { getset; }

        
public Person(string name, string code) {
            userName = name;
            pwd = code;
        }
    }

    [DataContract]
    
public class PBPerson {

        [ProtoMember(1)]
        
public string userName { getset; }

        [ProtoMember(2)]
        
public string pwd { getset; }

        
public void Write() {
            Console.WriteLine(string.Format("用戶名:" + userName + "密碼:" + pwd));
        }

    }

    
public class RequestHandler {
        
/// <summary>
        
/// 存放沒有接受完的部分消息
        
/// </summary>
        private string temp = string.Empty;

        
/// <summary>
        
/// 獲取消息
        
/// </summary>
        
/// <param name="input"></param>
        
/// <returns></returns>
        public string[] GetActualString(string input) {
            
return GetActualString(input, null);
        }

        
private string[] GetActualString(string input, List<string> outputList) {
            
if (outputList == null)
                outputList = new List<string>();

            
if (!String.IsNullOrEmpty(temp))
                input = temp + input;

            
string output = "";
            
string pattern = @"(?<=^\[length=)(\d+)(?=\])";
            
int length;

            
if (Regex.IsMatch(input, pattern)) {

                Match m = Regex.Match(input, pattern);

                
// 獲取消息字符串實(shí)際應(yīng)有的長度
                length = Convert.ToInt32(m.Groups[0].Value);

                
// 獲取需要進(jìn)行截取的位置
                int startIndex = input.IndexOf(']'+ 1;

                
// 獲取從此位置開始后所有字符的長度
                output = input.Substring(startIndex);

                
if (output.Length == length) {
                    
// 如果output的長度與消息字符串的應(yīng)有長度相等
                    
// 說明剛好是完整的一條信息
                    outputList.Add(output);
                    temp = "";
                }
                
else if (output.Length < length) {
                    
// 如果之后的長度小于應(yīng)有的長度,
                    
// 說明沒有發(fā)完整,則應(yīng)將整條信息,包括元數(shù)據(jù),全部緩存
                    
// 與下一條數(shù)據(jù)合并起來再進(jìn)行處理
                    temp = input;
                    
// 此時(shí)程序應(yīng)該退出,因?yàn)樾枰却乱粭l數(shù)據(jù)到來才能繼續(xù)處理

                }
                
else if (output.Length > length) {
                    
// 如果之后的長度大于應(yīng)有的長度,
                    
// 說明消息發(fā)完整了,但是有多余的數(shù)據(jù)
                    
// 多余的數(shù)據(jù)可能是截?cái)嘞ⅲ部赡苁嵌鄺l完整消息

                    
// 截取字符串
                    output = output.Substring(0, length);
                    outputList.Add(output);
                    temp = "";

                    
// 縮短input的長度
                    input = input.Substring(startIndex + length);

                    
// 遞歸調(diào)用
                    GetActualString(input, outputList);
                }
            }
            
else {    // 說明“[”,“]”就不完整
                temp = input;
            }

            
return outputList.ToArray();
        }
以ProtoBuf方式傳遞對象數(shù)組
服務(wù)端

public class Service {

        
public void Start() {
            TcpListener listener = new TcpListener(IPAddress.Parse("192.168.65.35"), 12345);
            listener.Start();

            
while (true) {
                TcpClient client = listener.AcceptTcpClient();
                ClientConnected(client);
            }

        }

        
void ClientConnected(TcpClient client) {
            
try {
                
using (NetworkStream stream = client.GetStream()) {
                    Console.WriteLine("獲取到數(shù)據(jù)");
                    List<PBPerson> cust = Serializer.DeserializeWithLengthPrefix<List<PBPerson>>(stream, PrefixStyle.Base128);
                    Console.WriteLine("返回?cái)?shù)據(jù)");
                    
foreach (PBPerson p in cust) {
                        p.userName = "fengyun";
                    }
                    Serializer.SerializeWithLengthPrefix(stream, cust, PrefixStyle.Base128);

                    
int final = stream.ReadByte();
                    
if (final == 123) {
                        Console.WriteLine("SERVER: Got client-happy marker");
                    }
                    
else {
                        Console.WriteLine("SERVER: OOPS! Something went wrong");
                    }
                    Console.WriteLine("SERVER: Closing connection");
                    stream.Close();
                    client.Close();
                }
            }
            
finally {
            }
        }
    }
----------------我是猥瑣的分割線---------------------------
Service ser = new Service();
客戶端

    public class Client {
        
public void Send(int num) {

            Stopwatch watch = new Stopwatch();

            PBPerson p = new PBPerson();
            p.userName = "dabing";
            p.pwd = "110110";

            List<PBPerson> list = new List<PBPerson>();
            
for (int i = 0; i < num; i++) {
                list.Add(p);
            }

            
using (TcpClient client = new TcpClient()) {
                client.Connect(new IPEndPoint(IPAddress.Parse("192.168.65.35"), 12345));

                
using (NetworkStream stream = client.GetStream()) {

                    
//Console.WriteLine("獲取連接發(fā)送數(shù)據(jù)");
                    watch.Start();
                    Serializer.SerializeWithLengthPrefix(stream, list, PrefixStyle.Base128);

                    
//Console.WriteLine("獲取數(shù)據(jù)");
                    
                    List<PBPerson> newCust = Serializer.DeserializeWithLengthPrefix<List<PBPerson>>(stream, PrefixStyle.Base128);
                    watch.Stop();
                    Console.WriteLine(watch.ElapsedMilliseconds);
                    
//foreach (PBPerson per in newCust) {
                       
// Console.WriteLine(per.userName);
                    
//}
                    stream.WriteByte(123); // just to show all bidirectional comms are OK
                    stream.Close();
                }
                client.Close();
            }
        }
    }
----------------------我是猥瑣的分割線----------------------
Client c = new Client();
c.Send(10000
我們從代碼中可以看到,ProtoBuf本身具有很多與通信相關(guān)的特性。
有了以上寫法,我們再來看看兩個(gè)框架再傳遞對象時(shí)的相率對比

5:JSON.NET與ProtoBuf在Socket下傳遞對象效率簡單對比

我們就來看從發(fā)送開始到收完數(shù)據(jù)接收,兩個(gè)框架傳遞不同數(shù)量對象所消耗的時(shí)間。
100(J/P) 1000(J/P) 10000(J/P)
json/proto 97/264 150/143 2202/366

后記
按照慣例,附上本文涉及的所有源代碼。
作者:大兵

(###)

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多