zoukankan      html  css  js  c++  java
  • C# Socket和protoBuf新手村教程

    Boss->Socket

    此教程纯属Socket初级应用篇,因为网上全是理论篇(实践才是王道)

    1级->Client创建

    1. 首先创建一个C#命令行工程(别告诉这个不会)

    2. 创建Socket实例,别忘了引用System.Net和System.Net.Sockets

       Socket client = new Socket(SocketType.Stream, ProtocolType.Tcp); // TCP链接
      
    3. 设置要链接的服务器ip地址,IPAddress是C#提供的ip封装类

       IPAddress ip = IPAddress.Parse("127.0.0.1"); // 本地地址127.0.0.1(别说你不知道)
      
    4. 设置要链接的服务器ip和端口,IPEndPoint是C#提供的ip和端口的封装类

       IPEndPoint point = new IPEndPoint(ip, 2333); // 端口为2333,ip为上一段代码的ip
      
    5. 链接

       client.Connect(point);
       // client.Connect("127.0.0.1", 2333); // 等同于3,4
      
    6. 开启线程接收服务器消息,别忘了引用System.Threading

       Thread thread = new Thread(Recive);
       thread.IsBackground = true; // 后台执行线程
       thread.Start(client); // 传入客户端的Socket
      
       // Recive函数
       static void Recive(object o)
       {
           var client = o as Socket;
           while (true)
           {
               byte[] buffer = new byte[1024 * 1024 * 2];
               int effective = client.Receive(buffer); //二进制数据存储在buffer中,数据长度为effective
               if (effective == 0)
               {
                   break;
               }
               var str = Encoding.UTF8.GetString(buffer, 0, effective); // 将二进制数据转换为UTF8格式的String
               Console.WriteLine(str);
           }
       }
      
    7. 发送自定义数据给服务器

       while (true)
       {
           string s = Console.ReadLine();
           byte[] buffer = Encoding.ASCII.GetBytes(s); // 将数据转换为ASCII编码的二进制数组形式
           socketClient.Send(buffer); // 发送消息
           Console.WriteLine("Send Message");
       }
      
    8. client完整代码

    using System;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    namespace SocketTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                Socket client = new Socket(SocketType.Stream, ProtocolType.Tcp);
                IPAddress ip = IPAddress.Parse("127.0.0.1");
                IPEndPoint point = new IPEndPoint(ip, 2333);
                client.Connect("127.0.0.1", 2333);
    
                Thread thread = new Thread(Recive);
                thread.IsBackground = true;
                thread.Start(client);
    
                while (true)
                {
                    string s = Console.ReadLine();
                    byte[] buffer = Encoding.ASCII.GetBytes(s);
                    client.Send(buffer);
                    Console.WriteLine("Send Message");
                }
            }
    
            static void Recive(object o)
            {
                var client = o as Socket;
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    var effective = client.Receive(buffer);
                    if (effective == 0)
                    {
                        break;
                    }
                    var str = Encoding.UTF8.GetString(buffer, 0, effective);
                    Console.WriteLine(str);
                }
            }
        }
    }
    

    2级->Server创建

    1. 首先创建一个C#命令行工程(别告诉这个不会)

    2. 创建Socket实例(同client)

    3. 设置服务器的ip地址

       IPAddress ip = IPAddress.Parse("127.0.0.1");
       IPEndPoint point = new IPEndPoint(ip, 2333);
       server.Bind(point);
      
    4. 设置服务器的最大监听数

       server.Listen(10);
      
    5. 开启线程接收客户端连接和数据(※注意是连接)

       Thread thread = new Thread(Listen);
       thread.IsBackground = true;
       thread.Start(serverSocket);
      
       // Listen函数,等待客户端连接
       static void Listen(object o)
       {
           var serverSocket = o as Socket;
           while (true)
           {
               client = serverSocket.Accept(); // 等待客户端连接,返回客户端的Socket,之前的10限制就是在这里,最多有10个客户端可以建立连接
               var sendIpoint = client.RemoteEndPoint.ToString(); // 客户端的ip和端口
               Console.WriteLine($"{sendIpoint}Connection");
               // 连接成功则开启一个接收线程接收客户端发来的消息
               Thread thread = new Thread(Recive);
               thread.IsBackground = true;
               thread.Start(client);
           }
       }
      
       // Recive函数,同客户端
      
    6. server完整代码

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    namespace SocketServer
    {
        class Program
        {
            static void Main(string[] args)
            {
                Socket server = new Socket(SocketType.Stream, ProtocolType.Tcp);
                IPAddress ip = IPAddress.Parse("127.0.0.1");
                IPEndPoint point = new IPEndPoint(ip, 2333);
                server.Bind(point);
                server.Listen(10);
    
                Thread thread = new Thread(Listen);
                thread.IsBackground = true;
                thread.Start(server);
    
                Console.Read();
            }
    
            static void Listen(object o)
            {
                var server = o as Socket;
                while (true)
                {
                    client = server.Accept();
                    var clientIpoint = client.RemoteEndPoint.ToString();
                    Console.WriteLine($"{clientIpoint}Connection");
                    Thread thread = new Thread(Recive);
                    thread.IsBackground = true;
                    thread.Start(client);
                }
            }
    
            static void Recive(object o)
            {
                var client = o as Socket;
                while (true)
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    var effective = client.Receive(buffer);
                    if (effective == 0)
                    {
                        break;
                    }
                    var str = Encoding.UTF8.GetString(buffer, 0, effective);
                    Console.WriteLine(str);
                }
            }
        }
    }
    

    Boss->ProtoBuf

    如果我们要用ProtoBuf用在C#中就得集齐各种神器

    1. ProtoBuf源码:其中有C#的示例代码
    2. ProtoBuf编译器:编译.proto文件
    3. ProtoBuf的C#工具集(你可能会需要下载nuget):提供工程中的dll引用文件
    4. ProtoBuf官方教程(蜜汁上网)

    1级->编写proto文件

    都说ProtoBuf不依赖于任何语言是一个跨语言的神器,然而他的语言格式是scheme(Lisp的方言),并且编译器就是把.proto文件翻译成各个不同语言的编译器。

    proto文件示例(教程中示例的文件)

    // [START declaration]
    syntax = "proto3";
    package tutorial;
    
    import "google/protobuf/timestamp.proto";
    // [END declaration]
    
    // [START java_declaration]
    option java_package = "com.example.tutorial";
    option java_outer_classname = "AddressBookProtos";
    // [END java_declaration]
    
    // [START csharp_declaration]
    option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
    // [END csharp_declaration]
    
    // [START messages]
    message Person {
      string name = 1;
      int32 id = 2;  // Unique ID number for this person.
      string email = 3;
    
      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }
    
      message PhoneNumber {
        string number = 1;
        PhoneType type = 2;
      }
    
      repeated PhoneNumber phones = 4;
    
      google.protobuf.Timestamp last_updated = 5;
    }
    
    // Our address book file is just one of these.
    message AddressBook {
      repeated Person people = 1;
    }
    // [END messages]
    

    2级->编译proto文件

    将上面的文件用proto.exe编译

    格式:proto -I=当前目录 -out_csharp=当前目录 目录/文件名.扩展名
    例:
    文件名为:address.proto
    目录为:D:/Work下
    proto -I=D:/Work -out_csharp=D:/Work D:/Wrok/address.proto
    

    编译完后会生成一个.cs文件,文件很长我就不展示了

    3级->nuget下载dll

    nuget install Google.Protobuf -Version 3.8.0 // 版本随意
    

    下载下来找到dll

    Google.Protobuf.3.8.0/bin/net45/Google.Protobuf.dll
    

    引用到客户端和服务器中,然后把生成的.cs文件也复制到项目中

    4级->编写序列化和反序列化代码

    在客户端和服务器的Program(上面的代码)中加入序列化和反序列化的函数,需要引用Google.Protobuf、Google.Protobuf.Examples.AddressBook和static Google.Protobuf.Examples.AddressBook.Person.Types(C#6 才支持)

    public static byte[] Serialize<T>(T obj) where T : IMessage
    {
        return obj.ToByteArray();
    }
    
    public static T Deserialize<T>(byte[] data) where T : class, IMessage, new()
    {
        T obj = new T();
        IMessage message = obj.Descriptor.Parser.ParseFrom(data);
        return message as T;
    }
    

    注释:

    1. 可以看到由.proto转换成.cs的文件的父接口为IMessage
    2. ToByteArray()是protoBuf自带的类转二进制的函数(所谓的序列化)
    3. obj.Descriptor.Parser.ParseFrom是是protoBuf自带的二进制转类的函数(所谓的反序列化)

    知道这些之后就可以传输二进制数据啦,所以我们的客户端代码的发送数据部分改为

    // 建立数据
    Person john = new Person
    {
        Id = 1234,
        Name = "John Doe",
        Email = "jdoe@example.com",
        Phones = { new PhoneNumber { Number = "555-4321", Type = PhoneType.Home } }
    };
    var message = Serialize(john); // 得到byte[]的message
    
    while (true)
    {
        string s = Console.ReadLine();
        socketClient.Send(message); //  直接传输message
        Console.WriteLine("Send Message");
    }
    

    服务端的接受部分改一下,这部分注意反序列化素组的长度必须和序列化后的一致,所以这边新建了一个正确长度的b2数组把数据复制过去

    static void Recive(object o)
    {
        var send = o as Socket;
        while (true)
        {
            byte[] buffer = new byte[1024 * 1024 * 2];
            var effective = send.Receive(buffer);
            byte[] b2 = new byte[effective];
            Array.Copy(buffer, 0, b2, 0, effective); // 把数据拷贝给b2
            if (effective == 0)
            {
                break;
            }
            var message = Deserialize<Person>(b2); // 解析时候必须为正确的长度
            Console.WriteLine("ID = {0}", message.Id);
            Console.WriteLine("Name = {0}", message.Name);
            Console.WriteLine("Email = {0}", message.Email);
            Console.WriteLine("Phone Number = {0}", message.Phones[0].Number);
            Console.WriteLine("Phone Type = {0}", message.Phones[0].Type);
        }
    }
    

    完结撒花~

  • 相关阅读:
    UI设计学习路径(一个)—好酒也怕巷子深
    shell script 入门 笔记
    HDU 4864Task(更多的联合培训学校1)(贪婪)
    XCL-Charts绘画面积图(AreaChart) 例1
    table插入标签form标记怪现象
    ECharts SSH+JQueryAjax+Json+JSP在数据库中的数据来填充ECharts在
    Xcode 6 AutoLayout Size Classes
    POJ 1984 Navigation Nightmare (数据结构-并检查集合)
    DataTable填补了实体类返回泛型集合
    Cisco C2900XL
  • 原文地址:https://www.cnblogs.com/SHOR/p/10977035.html
Copyright © 2011-2022 走看看