C# Socket和protoBuf新手村教程

Boss->Socket

此教程純屬Socket初級應用篇,由於網上全是理論篇(實踐纔是王道)java

1級->Client建立

  1. 首先建立一個C#命令行工程(別告訴這個不會)
  2. 建立Socket實例,別忘了引用System.Net和System.Net.Socketsgit

    Socket client = new Socket(SocketType.Stream, ProtocolType.Tcp); // TCP連接
  3. 設置要連接的服務器ip地址,IPAddress是C#提供的ip封裝類github

    IPAddress ip = IPAddress.Parse("127.0.0.1"); // 本地地址127.0.0.1(別說你不知道)
  4. 設置要連接的服務器ip和端口,IPEndPoint是C#提供的ip和端口的封裝類api

    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. 發送自定義數據給服務器socket

    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. 設置服務器的最大監聽數this

    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);
    }
}

完結撒花~

相關文章
相關標籤/搜索