李朝强的博客

人之为学,如饮河海,大饮则大盈,小饮则小颖!
posts - 35, comments - 144, trackbacks - 0, articles - 2
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

.Net RPC框架Thrift的用法

Posted on 2018-06-20 18:26 李朝强 阅读(...) 评论(...) 编辑 收藏

  关于Thrift

下面是来自百度百科关于Thrift的介绍:

    thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

Apache开源地址:http://thrift.apache.org/

Thrift也是.Net平台上一款不错的RPC框架,更何况可以实现与众多编程语言之间的远程调用。下面具体介绍下Thrift在.Net上的用法。

 

首先,下载Thrift工具,这里选择windows平台的,下载地址:http://www.apache.org/dyn/closer.cgi?path=/thrift/0.11.0/thrift-0.11.0.exe

其次,下载具体的Thrift Sdk包,http://www.apache.org/dyn/closer.cgi?path=/thrift/0.11.0/thrift-0.11.0.tar.gz

内部包含了所支持的语言源码,这里,我们选择csharp目录里的内容,用VS打开,编译生成DLL文件,可供后续使用。

其次,编写Thrift文件,内容如下,这里定义了一个User类,包含一个Int32类型的ID和一个string类型的Name属性,同时,在定义了一个服务,UserService,其中包含GetUserByID和GetAllUser两个方法。  、

struct User { 
    1: i32 ID 
    2: string Name 
  }


service UserService { 
    User GetUserByID(1:i32 userID)
    list<User> GetAllUser()  
}      

  然后,通过上面下载的Thrif.exe命令行工具,进行代码的生成,windows平台,cmd进入thrift.exe所在目录,执行如下命令:

thrift --gen csharp user.thrift

 命令格式如:

thrift --gen <language> <Thrift filename>

   也可以把thrift.exe放到指定目录或C盘位置,配置环境变量Path,然后可以直接执行以上命令。

执行完以上命令后,会在当前目录下,生成一个名为gen-csharp的文件夹,里面包含了2个类(User.cs和UserService.cs)。

User.cs代码如下:

/**
 * Autogenerated by Thrift Compiler (0.11.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Thrift;
using Thrift.Collections;
using System.Runtime.Serialization;
using Thrift.Protocol;
using Thrift.Transport;


#if !SILVERLIGHT
[Serializable]
#endif
public partial class User : TBase
{
  private int _ID;
  private string _Name;

  public int ID
  {
    get
    {
      return _ID;
    }
    set
    {
      __isset.ID = true;
      this._ID = value;
    }
  }

  public string Name
  {
    get
    {
      return _Name;
    }
    set
    {
      __isset.Name = true;
      this._Name = value;
    }
  }


  public Isset __isset;
  #if !SILVERLIGHT
  [Serializable]
  #endif
  public struct Isset {
    public bool ID;
    public bool Name;
  }

  public User() {
  }

  public void Read (TProtocol iprot)
  {
    iprot.IncrementRecursionDepth();
    try
    {
      TField field;
      iprot.ReadStructBegin();
      while (true)
      {
        field = iprot.ReadFieldBegin();
        if (field.Type == TType.Stop) { 
          break;
        }
        switch (field.ID)
        {
          case 1:
            if (field.Type == TType.I32) {
              ID = iprot.ReadI32();
            } else { 
              TProtocolUtil.Skip(iprot, field.Type);
            }
            break;
          case 2:
            if (field.Type == TType.String) {
              Name = iprot.ReadString();
            } else { 
              TProtocolUtil.Skip(iprot, field.Type);
            }
            break;
          default: 
            TProtocolUtil.Skip(iprot, field.Type);
            break;
        }
        iprot.ReadFieldEnd();
      }
      iprot.ReadStructEnd();
    }
    finally
    {
      iprot.DecrementRecursionDepth();
    }
  }

  public void Write(TProtocol oprot) {
    oprot.IncrementRecursionDepth();
    try
    {
      TStruct struc = new TStruct("User");
      oprot.WriteStructBegin(struc);
      TField field = new TField();
      if (__isset.ID) {
        field.Name = "ID";
        field.Type = TType.I32;
        field.ID = 1;
        oprot.WriteFieldBegin(field);
        oprot.WriteI32(ID);
        oprot.WriteFieldEnd();
      }
      if (Name != null && __isset.Name) {
        field.Name = "Name";
        field.Type = TType.String;
        field.ID = 2;
        oprot.WriteFieldBegin(field);
        oprot.WriteString(Name);
        oprot.WriteFieldEnd();
      }
      oprot.WriteFieldStop();
      oprot.WriteStructEnd();
    }
    finally
    {
      oprot.DecrementRecursionDepth();
    }
  }

  public override string ToString() {
    StringBuilder __sb = new StringBuilder("User(");
    bool __first = true;
    if (__isset.ID) {
      if(!__first) { __sb.Append(", "); }
      __first = false;
      __sb.Append("ID: ");
      __sb.Append(ID);
    }
    if (Name != null && __isset.Name) {
      if(!__first) { __sb.Append(", "); }
      __first = false;
      __sb.Append("Name: ");
      __sb.Append(Name);
    }
    __sb.Append(")");
    return __sb.ToString();
  }

}

  UserService.cs代码如下:

/**
 * Autogenerated by Thrift Compiler (0.11.0)
 *
 * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
 *  @generated
 */
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Thrift;
using Thrift.Collections;
using System.Runtime.Serialization;
using Thrift.Protocol;
using Thrift.Transport;

public partial class UserService {
  public interface ISync {
    User GetUserByID(int userID);
    List<User> GetAllUser();
  }

  public interface Iface : ISync {
    #if SILVERLIGHT
    IAsyncResult Begin_GetUserByID(AsyncCallback callback, object state, int userID);
    User End_GetUserByID(IAsyncResult asyncResult);
    #endif
    #if SILVERLIGHT
    IAsyncResult Begin_GetAllUser(AsyncCallback callback, object state);
    List<User> End_GetAllUser(IAsyncResult asyncResult);
    #endif
  }

  public class Client : IDisposable, Iface {
    public Client(TProtocol prot) : this(prot, prot)
    {
    }

    public Client(TProtocol iprot, TProtocol oprot)
    {
      iprot_ = iprot;
      oprot_ = oprot;
    }

    protected TProtocol iprot_;
    protected TProtocol oprot_;
    protected int seqid_;

    public TProtocol InputProtocol
    {
      get { return iprot_; }
    }
    public TProtocol OutputProtocol
    {
      get { return oprot_; }
    }


    #region " IDisposable Support "
    private bool _IsDisposed;

    // IDisposable
    public void Dispose()
    {
      Dispose(true);
    }
    

    protected virtual void Dispose(bool disposing)
    {
      if (!_IsDisposed)
      {
        if (disposing)
        {
          if (iprot_ != null)
          {
            ((IDisposable)iprot_).Dispose();
          }
          if (oprot_ != null)
          {
            ((IDisposable)oprot_).Dispose();
          }
        }
      }
      _IsDisposed = true;
    }
    #endregion


    
    #if SILVERLIGHT
    public IAsyncResult Begin_GetUserByID(AsyncCallback callback, object state, int userID)
    {
      return send_GetUserByID(callback, state, userID);
    }

    public User End_GetUserByID(IAsyncResult asyncResult)
    {
      oprot_.Transport.EndFlush(asyncResult);
      return recv_GetUserByID();
    }

    #endif

    public User GetUserByID(int userID)
    {
      #if !SILVERLIGHT
      send_GetUserByID(userID);
      return recv_GetUserByID();

      #else
      var asyncResult = Begin_GetUserByID(null, null, userID);
      return End_GetUserByID(asyncResult);

      #endif
    }
    #if SILVERLIGHT
    public IAsyncResult send_GetUserByID(AsyncCallback callback, object state, int userID)
    #else
    public void send_GetUserByID(int userID)
    #endif
    {
      oprot_.WriteMessageBegin(new TMessage("GetUserByID", TMessageType.Call, seqid_));
      GetUserByID_args args = new GetUserByID_args();
      args.UserID = userID;
      args.Write(oprot_);
      oprot_.WriteMessageEnd();
      #if SILVERLIGHT
      return oprot_.Transport.BeginFlush(callback, state);
      #else
      oprot_.Transport.Flush();
      #endif
    }

    public User recv_GetUserByID()
    {
      TMessage msg = iprot_.ReadMessageBegin();
      if (msg.Type == TMessageType.Exception) {
        TApplicationException x = TApplicationException.Read(iprot_);
        iprot_.ReadMessageEnd();
        throw x;
      }
      GetUserByID_result result = new GetUserByID_result();
      result.Read(iprot_);
      iprot_.ReadMessageEnd();
      if (result.__isset.success) {
        return result.Success;
      }
      throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "GetUserByID failed: unknown result");
    }

    
    #if SILVERLIGHT
    public IAsyncResult Begin_GetAllUser(AsyncCallback callback, object state)
    {
      return send_GetAllUser(callback, state);
    }

    public List<User> End_GetAllUser(IAsyncResult asyncResult)
    {
      oprot_.Transport.EndFlush(asyncResult);
      return recv_GetAllUser();
    }

    #endif

    public List<User> GetAllUser()
    {
      #if !SILVERLIGHT
      send_GetAllUser();
      return recv_GetAllUser();

      #else
      var asyncResult = Begin_GetAllUser(null, null);
      return End_GetAllUser(asyncResult);

      #endif
    }
    #if SILVERLIGHT
    public IAsyncResult send_GetAllUser(AsyncCallback callback, object state)
    #else
    public void send_GetAllUser()
    #endif
    {
      oprot_.WriteMessageBegin(new TMessage("GetAllUser", TMessageType.Call, seqid_));
      GetAllUser_args args = new GetAllUser_args();
      args.Write(oprot_);
      oprot_.WriteMessageEnd();
      #if SILVERLIGHT
      return oprot_.Transport.BeginFlush(callback, state);
      #else
      oprot_.Transport.Flush();
      #endif
    }

    public List<User> recv_GetAllUser()
    {
      TMessage msg = iprot_.ReadMessageBegin();
      if (msg.Type == TMessageType.Exception) {
        TApplicationException x = TApplicationException.Read(iprot_);
        iprot_.ReadMessageEnd();
        throw x;
      }
      GetAllUser_result result = new GetAllUser_result();
      result.Read(iprot_);
      iprot_.ReadMessageEnd();
      if (result.__isset.success) {
        return result.Success;
      }
      throw new TApplicationException(TApplicationException.ExceptionType.MissingResult, "GetAllUser failed: unknown result");
    }

  }
  public class Processor : TProcessor {
    public Processor(ISync iface)
    {
      iface_ = iface;
      processMap_["GetUserByID"] = GetUserByID_Process;
      processMap_["GetAllUser"] = GetAllUser_Process;
    }

    protected delegate void ProcessFunction(int seqid, TProtocol iprot, TProtocol oprot);
    private ISync iface_;
    protected Dictionary<string, ProcessFunction> processMap_ = new Dictionary<string, ProcessFunction>();

    public bool Process(TProtocol iprot, TProtocol oprot)
    {
      try
      {
        TMessage msg = iprot.ReadMessageBegin();
        ProcessFunction fn;
        processMap_.TryGetValue(msg.Name, out fn);
        if (fn == null) {
          TProtocolUtil.Skip(iprot, TType.Struct);
          iprot.ReadMessageEnd();
          TApplicationException x = new TApplicationException (TApplicationException.ExceptionType.UnknownMethod, "Invalid method name: '" + msg.Name + "'");
          oprot.WriteMessageBegin(new TMessage(msg.Name, TMessageType.Exception, msg.SeqID));
          x.Write(oprot);
          oprot.WriteMessageEnd();
          oprot.Transport.Flush();
          return true;
        }
        fn(msg.SeqID, iprot, oprot);
      }
      catch (IOException)
      {
        return false;
      }
      return true;
    }

    public void GetUserByID_Process(int seqid, TProtocol iprot, TProtocol oprot)
    {
      GetUserByID_args args = new GetUserByID_args();
      args.Read(iprot);
      iprot.ReadMessageEnd();
      GetUserByID_result result = new GetUserByID_result();
      try
      {
        result.Success = iface_.GetUserByID(args.UserID);
        oprot.WriteMessageBegin(new TMessage("GetUserByID", TMessageType.Reply, seqid)); 
        result.Write(oprot);
      }
      catch (TTransportException)
      {
        throw;
      }
      catch (Exception ex)
      {
        Console.Error.WriteLine("Error occurred in processor:");
        Console.Error.WriteLine(ex.ToString());
        TApplicationException x = new TApplicationException      (TApplicationException.ExceptionType.InternalError," Internal error.");
        oprot.WriteMessageBegin(new TMessage("GetUserByID", TMessageType.Exception, seqid));
        x.Write(oprot);
      }
      oprot.WriteMessageEnd();
      oprot.Transport.Flush();
    }

    public void GetAllUser_Process(int seqid, TProtocol iprot, TProtocol oprot)
    {
      GetAllUser_args args = new GetAllUser_args();
      args.Read(iprot);
      iprot.ReadMessageEnd();
      GetAllUser_result result = new GetAllUser_result();
      try
      {
        result.Success = iface_.GetAllUser();
        oprot.WriteMessageBegin(new TMessage("GetAllUser", TMessageType.Reply, seqid)); 
        result.Write(oprot);
      }
      catch (TTransportException)
      {
        throw;
      }
      catch (Exception ex)
      {
        Console.Error.WriteLine("Error occurred in processor:");
        Console.Error.WriteLine(ex.ToString());
        TApplicationException x = new TApplicationException      (TApplicationException.ExceptionType.InternalError," Internal error.");
        oprot.WriteMessageBegin(new TMessage("GetAllUser", TMessageType.Exception, seqid));
        x.Write(oprot);
      }
      oprot.WriteMessageEnd();
      oprot.Transport.Flush();
    }

  }


  #if !SILVERLIGHT
  [Serializable]
  #endif
  public partial class GetUserByID_args : TBase
  {
    private int _userID;

    public int UserID
    {
      get
      {
        return _userID;
      }
      set
      {
        __isset.userID = true;
        this._userID = value;
      }
    }


    public Isset __isset;
    #if !SILVERLIGHT
    [Serializable]
    #endif
    public struct Isset {
      public bool userID;
    }

    public GetUserByID_args() {
    }

    public void Read (TProtocol iprot)
    {
      iprot.IncrementRecursionDepth();
      try
      {
        TField field;
        iprot.ReadStructBegin();
        while (true)
        {
          field = iprot.ReadFieldBegin();
          if (field.Type == TType.Stop) { 
            break;
          }
          switch (field.ID)
          {
            case 1:
              if (field.Type == TType.I32) {
                UserID = iprot.ReadI32();
              } else { 
                TProtocolUtil.Skip(iprot, field.Type);
              }
              break;
            default: 
              TProtocolUtil.Skip(iprot, field.Type);
              break;
          }
          iprot.ReadFieldEnd();
        }
        iprot.ReadStructEnd();
      }
      finally
      {
        iprot.DecrementRecursionDepth();
      }
    }

    public void Write(TProtocol oprot) {
      oprot.IncrementRecursionDepth();
      try
      {
        TStruct struc = new TStruct("GetUserByID_args");
        oprot.WriteStructBegin(struc);
        TField field = new TField();
        if (__isset.userID) {
          field.Name = "userID";
          field.Type = TType.I32;
          field.ID = 1;
          oprot.WriteFieldBegin(field);
          oprot.WriteI32(UserID);
          oprot.WriteFieldEnd();
        }
        oprot.WriteFieldStop();
        oprot.WriteStructEnd();
      }
      finally
      {
        oprot.DecrementRecursionDepth();
      }
    }

    public override string ToString() {
      StringBuilder __sb = new StringBuilder("GetUserByID_args(");
      bool __first = true;
      if (__isset.userID) {
        if(!__first) { __sb.Append(", "); }
        __first = false;
        __sb.Append("UserID: ");
        __sb.Append(UserID);
      }
      __sb.Append(")");
      return __sb.ToString();
    }

  }


  #if !SILVERLIGHT
  [Serializable]
  #endif
  public partial class GetUserByID_result : TBase
  {
    private User _success;

    public User Success
    {
      get
      {
        return _success;
      }
      set
      {
        __isset.success = true;
        this._success = value;
      }
    }


    public Isset __isset;
    #if !SILVERLIGHT
    [Serializable]
    #endif
    public struct Isset {
      public bool success;
    }

    public GetUserByID_result() {
    }

    public void Read (TProtocol iprot)
    {
      iprot.IncrementRecursionDepth();
      try
      {
        TField field;
        iprot.ReadStructBegin();
        while (true)
        {
          field = iprot.ReadFieldBegin();
          if (field.Type == TType.Stop) { 
            break;
          }
          switch (field.ID)
          {
            case 0:
              if (field.Type == TType.Struct) {
                Success = new User();
                Success.Read(iprot);
              } else { 
                TProtocolUtil.Skip(iprot, field.Type);
              }
              break;
            default: 
              TProtocolUtil.Skip(iprot, field.Type);
              break;
          }
          iprot.ReadFieldEnd();
        }
        iprot.ReadStructEnd();
      }
      finally
      {
        iprot.DecrementRecursionDepth();
      }
    }

    public void Write(TProtocol oprot) {
      oprot.IncrementRecursionDepth();
      try
      {
        TStruct struc = new TStruct("GetUserByID_result");
        oprot.WriteStructBegin(struc);
        TField field = new TField();

        if (this.__isset.success) {
          if (Success != null) {
            field.Name = "Success";
            field.Type = TType.Struct;
            field.ID = 0;
            oprot.WriteFieldBegin(field);
            Success.Write(oprot);
            oprot.WriteFieldEnd();
          }
        }
        oprot.WriteFieldStop();
        oprot.WriteStructEnd();
      }
      finally
      {
        oprot.DecrementRecursionDepth();
      }
    }

    public override string ToString() {
      StringBuilder __sb = new StringBuilder("GetUserByID_result(");
      bool __first = true;
      if (Success != null && __isset.success) {
        if(!__first) { __sb.Append(", "); }
        __first = false;
        __sb.Append("Success: ");
        __sb.Append(Success== null ? "<null>" : Success.ToString());
      }
      __sb.Append(")");
      return __sb.ToString();
    }

  }


  #if !SILVERLIGHT
  [Serializable]
  #endif
  public partial class GetAllUser_args : TBase
  {

    public GetAllUser_args() {
    }

    public void Read (TProtocol iprot)
    {
      iprot.IncrementRecursionDepth();
      try
      {
        TField field;
        iprot.ReadStructBegin();
        while (true)
        {
          field = iprot.ReadFieldBegin();
          if (field.Type == TType.Stop) { 
            break;
          }
          switch (field.ID)
          {
            default: 
              TProtocolUtil.Skip(iprot, field.Type);
              break;
          }
          iprot.ReadFieldEnd();
        }
        iprot.ReadStructEnd();
      }
      finally
      {
        iprot.DecrementRecursionDepth();
      }
    }

    public void Write(TProtocol oprot) {
      oprot.IncrementRecursionDepth();
      try
      {
        TStruct struc = new TStruct("GetAllUser_args");
        oprot.WriteStructBegin(struc);
        oprot.WriteFieldStop();
        oprot.WriteStructEnd();
      }
      finally
      {
        oprot.DecrementRecursionDepth();
      }
    }

    public override string ToString() {
      StringBuilder __sb = new StringBuilder("GetAllUser_args(");
      __sb.Append(")");
      return __sb.ToString();
    }

  }


  #if !SILVERLIGHT
  [Serializable]
  #endif
  public partial class GetAllUser_result : TBase
  {
    private List<User> _success;

    public List<User> Success
    {
      get
      {
        return _success;
      }
      set
      {
        __isset.success = true;
        this._success = value;
      }
    }


    public Isset __isset;
    #if !SILVERLIGHT
    [Serializable]
    #endif
    public struct Isset {
      public bool success;
    }

    public GetAllUser_result() {
    }

    public void Read (TProtocol iprot)
    {
      iprot.IncrementRecursionDepth();
      try
      {
        TField field;
        iprot.ReadStructBegin();
        while (true)
        {
          field = iprot.ReadFieldBegin();
          if (field.Type == TType.Stop) { 
            break;
          }
          switch (field.ID)
          {
            case 0:
              if (field.Type == TType.List) {
                {
                  Success = new List<User>();
                  TList _list0 = iprot.ReadListBegin();
                  for( int _i1 = 0; _i1 < _list0.Count; ++_i1)
                  {
                    User _elem2;
                    _elem2 = new User();
                    _elem2.Read(iprot);
                    Success.Add(_elem2);
                  }
                  iprot.ReadListEnd();
                }
              } else { 
                TProtocolUtil.Skip(iprot, field.Type);
              }
              break;
            default: 
              TProtocolUtil.Skip(iprot, field.Type);
              break;
          }
          iprot.ReadFieldEnd();
        }
        iprot.ReadStructEnd();
      }
      finally
      {
        iprot.DecrementRecursionDepth();
      }
    }

    public void Write(TProtocol oprot) {
      oprot.IncrementRecursionDepth();
      try
      {
        TStruct struc = new TStruct("GetAllUser_result");
        oprot.WriteStructBegin(struc);
        TField field = new TField();

        if (this.__isset.success) {
          if (Success != null) {
            field.Name = "Success";
            field.Type = TType.List;
            field.ID = 0;
            oprot.WriteFieldBegin(field);
            {
              oprot.WriteListBegin(new TList(TType.Struct, Success.Count));
              foreach (User _iter3 in Success)
              {
                _iter3.Write(oprot);
              }
              oprot.WriteListEnd();
            }
            oprot.WriteFieldEnd();
          }
        }
        oprot.WriteFieldStop();
        oprot.WriteStructEnd();
      }
      finally
      {
        oprot.DecrementRecursionDepth();
      }
    }

    public override string ToString() {
      StringBuilder __sb = new StringBuilder("GetAllUser_result(");
      bool __first = true;
      if (Success != null && __isset.success) {
        if(!__first) { __sb.Append(", "); }
        __first = false;
        __sb.Append("Success: ");
        __sb.Append(Success);
      }
      __sb.Append(")");
      return __sb.ToString();
    }

  }

}

  最后,我们新建3个项目,一个Server,一个Client,一个Service(定义的服务),结构如下:

让我们先看看ThrifServer服务端吧。

        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {

            Console.Title = "Thrift服务端-Server";
            TServerSocket serverTransport = new TServerSocket(8080, 0, false);
            UserService.Processor processor = new UserService.Processor(new Services.TheUserService());
            TServer server = new TSimpleServer(processor, serverTransport);
            Console.WriteLine("启动服务器,监听端口8080 ...");
            server.Serve();
        }

  这里启动服务。在服务端,我们定义了一个TheUserService类,来实现Service中定义的成员。

 /// <summary>
    /// 用户服务
    /// </summary>
    public class TheUserService:Iface
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="userID"></param>
        /// <returns></returns>
        public User GetUserByID(int userID)
        {
            return new User() { ID = 1, Name = "lichaoqiang" };
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public List<User> GetAllUser()
        {
            List<User> users = new List<User>(){
                new User() { ID = 1, Name = "lichaoqiang" },
                new User() { ID = 2, Name = "yuyuangfang" }
            };
            return users;
        }
    }  

Thrift客户端:

        /// <summary>
        /// 
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            Console.Title = "Thrift客户端-Client";
            TTransport transport = new TSocket("10.10.10.12", 8080);

            TProtocol protocol = new TJSONProtocol(transport);
            UserService.Client client = new UserService.Client(protocol);
            transport.Open();
            //var users = client.GetAllUser();

            //users.ForEach(u => Console.WriteLine(string.Format("User ID : {0}, User Name {1}", u.ID, u.Name)));
            var user = client.GetUserByID(1);
            Console.WriteLine("------------------");
            Console.WriteLine(string.Format("User ID : {0}, User Name {1}", user.ID, user.Name));
            Console.ReadLine();
        }

  完成以上步骤后,让我们启动服务端和客户端,来看看效果吧!