c\#RPC 프레임 워 크 사용 안내

13689 단어 c#rpc프레임
앞 에 쓰기:
RPC,들 은 지 꽤 되 었 지만 무엇 인지 잘 모 르 겠 습 니 다.오늘 은 훑 어 보 겠 습 니 다.
설명:
[Remote Procedure Call Protocol]원 격 프로 세 스 호출(즉,A 프로그램 은 b 방법 을 호출 해 야 합 니 다.그러나 이 b 방법의 실현 은 B 프로그램 내부 에 있 습 니 다.B 프로그램 은 A 와 같은 컴퓨터 에 있 지 않 을 수도 있 습 니 다.어떻게 호출 합 니까?http 호출/rpc 도 가능 합 니 다.로 컬 방법 처럼 호출 할 수 있 습 니 다)
초기 탐색 사용:
시중 의 rpc 프레임 워 크 를 사 용 했 습 니 다.절 차 는 다음 과 같 습 니 다.
1.기본 적 인 코드 를 써 서 어떤 방법 이 있 는 지 알려 주세요.
2.그리고 서버 통합,
3.클 라 이언 트 통합,
4.OK 호출 이 발효 되 었 습 니 다.
마치 TCP 가 데 이 터 를 전송 하고 A 서버 에서 전달 하 며 유형 명,방법 명,파라미터,값 을 전달 한 다음 에 B 서버 가 데 이 터 를 받 아 결 과 를 계산 한 다음 에 데 이 터 를 A 에 게 전송 하 는 것 같다.이렇게 이해 하면 간단 해 요.
     다음은 손 을 써 보 겠 습 니 다.
자기 손:
서버:
서버 가 실현 되 는 곳 인 만큼 실현 류 라 고 할 수 있 는 방법 을 써 보 자.인터페이스 하나 와 실현 하 나 를 썼 고 효 과 를 보 여주 기 위해 두 가지 방법 을 썼 다.

public interface IMyTestService
  {
    int calc(int x, int y);

    bool login(string name, string pwd);
  }

  public class MyTestServiceImpl : IMyTestService
  {
    public int calc(int x, int y)
    {
      return x + y;
    }

    public bool login(string name, string pwd)
    {
      if (name == "test" && pwd == "123456")
      {
        return true;
      }

      return false;
    }
  }
OK,서버 의 대부분 이 완성 되 었 습 니 다.
그 다음 에 TCP 서버 입 니 다.TCP 서버 는 여러분 에 게 너무 간단 합 니 다.Socket 대상 을 만 들 고 포트 를 연결 하 며 클 라 이언 트 가 요청 한 Socket 대상 을 가 져 와 서 그 와 상호작용 을 하 는 것 이 아 닙 니까?더 이상 할 말 이 없다.

class Program
  {
    static void Main(string[] args)
    {
      Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      server.Bind(new IPEndPoint(IPAddress.Any, 10000));
      server.Listen(1000);

      Thread t = new Thread(Execute);
      t.IsBackground = true;
      t.Start(server);

      Console.WriteLine("rpc      ");
      Console.ReadLine();
    }

    private static void Execute(Object obj)
    {
      Socket server = obj as Socket;
      while (true)
      {
        Socket client = server.Accept();

        Thread t = new Thread(SingleExecute);
        t.IsBackground = true;
        t.Start(client);
      }
    }

    private static void SingleExecute(object obj)
    {
      //    
      Socket client = obj as Socket;

      byte[] buffer = new byte[8192];
      int count = client.Receive(buffer);
      if (count > 0)
      {
        var data = ServiceHelpercs.Handle(buffer);
        client.Send(data);
      }

      client.Shutdown(SocketShutdown.Both);
    }
  }
우 리 는 모든 클 라 이언 트 데 이 터 를 하나의 요청 패키지 에서 분석 할 수 있다 고 가정 합 니 다.만약 에 한 번 의 데이터 수신 이 분석 되 지 않 으 면 크기 를 추가 해 야 하기 때 문 입 니 다.클 라 이언 트 는 저 에 게 얼마나 많은 메 시 지 를 주 었 는 지 알려 주 고 지정 한 데 이 터 를 읽 어서 모든 내용 을 가 져 가 야 합 니 다.
실제 알고리즘 호출 을 돕 기 위해 ServiceHelpers 를 만 듭 니 다.다음 과 같다.

public class ServiceHelpercs
  {
    public static byte[] Handle(byte[] buffer)
    {
      MemoryStream ms = new MemoryStream(buffer);
      BinaryReader br = new BinaryReader(ms);

      int inter_len = br.ReadByte();
      string inter_name = Encoding.UTF8.GetString(br.ReadBytes(inter_len));

      int method_len = br.ReadByte();
      string method_name = Encoding.UTF8.GetString(br.ReadBytes(method_len));

      int args_length = br.ReadByte();
      int return_type = br.ReadByte();

      List<object> list = new List<object>();
      for (int i = 0; i < args_length; i++)
      {          // 0:void    1:int 2:bool 3:string
        int arg_type = br.ReadByte();
        if (arg_type == 1)
        {
          byte[] values = br.ReadBytes(4);
          list.Add(bytes2int(values));
        }
        else if (arg_type == 2)
        {
          bool value = br.ReadByte() == 1;
          list.Add(value);
        }
        else if (arg_type == 3)
        {
          int str_len = bytes2int(br.ReadBytes(4));
          string str = Encoding.UTF8.GetString(br.ReadBytes(str_len));
          list.Add(str);
        }
      }

      Type inter_type = null;
      var types = Assembly.GetExecutingAssembly().GetTypes();
      foreach (var type in types)
      {
        var ts = type.GetInterfaces();
        foreach (var t in ts)
        {
          if (t.Name == inter_name)
          {
            inter_type = type;
            break;
          }
        }
      }

      MethodInfo invokeMethod = null;
      if (inter_type != null)
      {
        var methods = inter_type.GetMethods();
        foreach (var method in methods)
        {
          if (method.Name == method_name)
          {
            invokeMethod = method;
            break;
          }
        }
      }

      if (invokeMethod != null)
      {
        Object thisObj = Activator.CreateInstance(inter_type);
        object result = invokeMethod.Invoke(thisObj, list.ToArray());
        if (return_type == 1)
        {
          int value = Convert.ToInt32(result);

          return int2bytes(value);
        }
        else if (return_type == 2)
        {
          return new byte[1] { Convert.ToBoolean(result) ? (byte)1 : (byte)0 };
        }
        else if (return_type == 2)
        {
          List<byte> result_data = new List<byte>();
          var str = (result == null ? "" : result.ToString());
          var data = Encoding.UTF8.GetBytes(str);

          result_data.AddRange(int2bytes(data.Length));
          result_data.AddRange(data);

          return result_data.ToArray();
        }
      }

      return new byte[1] { 0xFF };
    }

    public static byte[] int2bytes(int len)
    {
      byte[] data_len = new byte[4];
      data_len[0] = (byte)((len >> 8 * 3) & 0xFF);
      data_len[1] = (byte)((len >> 8 * 2) & 0xFF);
      data_len[2] = (byte)((len >> 8 * 1) & 0xFF);
      data_len[3] = (byte)(len & 0xFF);

      return data_len;
    }

    public static int bytes2int(byte[] buffer)
    {
      int value = 0;
      value += (int)(buffer[0] << (8 * 3));
      value += (int)(buffer[1] << (8 * 2));
      value += (int)(buffer[2] << (8 * 1));
      value += (int)(buffer[3]);

      return value;
    }
  }
여기 서 만 든 데이터 구조 가 간단 하기 때문에 분 석 된 클래스 는 매우 간단 합 니 다.

우리 의 약속 에 따라,여기 서 데 이 터 를 내 가 정의 한 방식 으로 분해 하면 된다.
서버 가 완성 되 었 습 니 다.간단 하지 않 습 니까?물론 클 라 이언 트 도 같은 방식 으로 포장 을 처리 하면 됩 니 다.
클 라 이언 트:
클 라 이언 트 는 간단 합 니 다.서버 에 연결 하기 만 하면 자동 으로 생 성 된 코드(여기 자동 생 성 이 라 고 쓰 지 않 고 수 동 입 니 다)를 통 해 결 과 를 되 돌 릴 수 있 습 니 다.

class Program
  {
    static void Main(string[] args)
    {
      IMyService service = new MyServiceProxy();
      DateTime startTime = DateTime.Now;
      int result = service.add(123, 321);

      int min_seconds = (int)(DateTime.Now - startTime).TotalMilliseconds;

      Console.WriteLine(result + "    " + min_seconds);
      Console.ReadLine();
    }
  }
위 에서 직접 호출 되 었 습 니 다.인터페이스,인터페이스의 실현 에 대해 여기 서 절 차 는 세 가지 입 니 다.1.구조 가 요청 해 야 할 데이터,2.서버 를 연결 하고 데 이 터 를 보 냅 니 다.3.반환 내용 을 받 고 결 과 를 분석 합 니 다.

public class MyServiceProxy : IMyService
  {
    public int add(int x, int y)
    {
      List<ArgInfo> argList = new List<ArgInfo>();
      argList.Add(new ArgInfo(TypeEnu.Int, x));
      argList.Add(new ArgInfo(TypeEnu.Int, y));

      byte[] send_data = create_send_package("IMyService", "add", 2, TypeEnu.Int, argList);
      Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      client.Connect(new IPEndPoint(IPAddress.Parse("192.168.0.105"), 10000));
      client.Send(send_data);

      byte[] buffer = new byte[4];
      int count = client.Receive(buffer);
      if (count > 0)
      {
        return bytes2int(buffer);
      }

      throw new Exception("    ");
    }

    public bool login(string name, string pwd)
    {
      List<ArgInfo> argList = new List<ArgInfo>();
      argList.Add(new ArgInfo(TypeEnu.String, name));
      argList.Add(new ArgInfo(TypeEnu.String, pwd));

      byte[] send_data = create_send_package("IMyService", "login", 2, TypeEnu.Bool, argList);
      Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
      client.Connect(new IPEndPoint(IPAddress.Parse("192.168.0.105"), 10000));
      client.Send(send_data);

      byte[] buffer = new byte[1];
      int count = client.Receive(buffer);
      if (count > 0)
      {
        return buffer[0] == 1;
      }

      throw new Exception("    ");
    }

    private byte[] create_send_package(string inter_name, string method_name, int arg_length, TypeEnu return_type, List<ArgInfo> argList)
    {
      List<byte> list = new List<byte>();
      list.Add((byte)inter_name.Length);
      list.AddRange(Encoding.UTF8.GetBytes(inter_name));

      list.Add((byte)method_name.Length);
      list.AddRange(Encoding.UTF8.GetBytes(method_name));

      list.Add((byte)arg_length);
      list.Add((byte)return_type);

      foreach (var arg in argList)
      {
        list.Add((byte)arg.type);
        if (arg.type == TypeEnu.Int)
        {
          list.AddRange(int2bytes(Convert.ToInt32(arg.value)));
        }
        else if (arg.type == TypeEnu.Bool)
        {
          bool value = Convert.ToBoolean(arg.value);
          list.Add(value ? (byte)1 : (byte)0);
        }
        else if (arg.type == TypeEnu.String)
        {
          string value = arg.value.ToString();
          list.AddRange(int2bytes(value.Length));
          list.AddRange(Encoding.UTF8.GetBytes(value));
        }
      }

      return list.ToArray();
    }

    public byte[] int2bytes(int len)
    {
      byte[] data_len = new byte[4];
      data_len[0] = (byte)((len >> 8 * 3) & 0xFF);
      data_len[1] = (byte)((len >> 8 * 2) & 0xFF);
      data_len[2] = (byte)((len >> 8 * 1) & 0xFF);
      data_len[3] = (byte)(len & 0xFF);

      return data_len;
    }

    public int bytes2int(byte[] buffer)
    {
      int value = 0;
      value += (int)(buffer[0] << (8 * 3));
      value += (int)(buffer[1] << (8 * 2));
      value += (int)(buffer[2] << (8 * 1));
      value += (int)(buffer[3]);

      return value;
    }
  }

  public class ArgInfo
  {
    public TypeEnu type { get; set; }

    public object value { get; set; }

    public ArgInfo(TypeEnu type, object value)
    {
      this.type = type;
      this.value = value;
    }
  }

  public enum TypeEnu
  {
    Void = 0,
    Int = 1,
    Bool = 2,
    String = 3
  }
인터페이스의 정 의 는 서버 의 것 을 그대로 사용 하면 된다.한 가지 설명:My ServiceProxy 라 는 종 류 는 제 가 손 으로 쓴 것 입 니 다.진실 한 환경 입 니 다.이런 종 류 는 우리 가 정의 한 특정한 형식 이 어야 합 니 다.그리고 코드 생 성 기 를 써 서 그 가 자동 으로 생 성 하도록 한 다음 에 힘 들 이지 않 고 모든 호출 을 호 환 할 수 있 습 니 다.
물론 여 기 는 네 가지 유형 만 지원 하고 더 많은 유형 을 확장 할 수 있 으 며 데 이 터 를 전달 하 는 방식 만 찾 으 면 된다.예 를 들 어 하나의 대상,우 리 는 어떻게 전달 해 야 할 지 모 르 고 대상 을 json 문자열 로 정의 하거나 서열 을 바 이 너 리 로 바 꿀 수 있 습 니 다.양 끝 만 이 유형 을 알 면 됩 니 다.
디자인 모드 에 해당 하 는(약속 이 설정 보다 큽 니 다)
지식 점 정리
여기 에는 자주 쓰 지 않 는 지식 이 있 는데,여기 서 정리 해 냈 다.
  1、MemoryStream ms = new MemoryStream(buffer); BinaryReader br = new BinaryReader(ms); biary Reader 방식 을 통 해 C/C+포인터 처럼 데 이 터 를 얻 을 수 있 습 니 다.
  2、var types = Assembly.GetExecutingAssembly().GetTypes(); Assembly 를 통 해 현재 exe 또는 dll 의 모든 유형 을 얻 을 수 있 습 니 다(클래스 인 터 페 이 스 는 하나의 유형 입 니 다)
  3、Object thisObj = Activator.CreateInstance(inter_type); Activator 를 통 해 기본 구 조 를 호출 하여 대상 의 초기 화 를 실현 합 니 다.
요약:
이러한 rpc 프레임 워 크 는 그 자체 가 최적화 되 지 않 았 고 최적화 할 수 있 는 부분 이 많 습 니 다.예 를 들 어 캐 시(매번 조회 유형 등 을 옮 겨 다 니 지 않 아 도 됩 니 다),udp 지원(여 기 는 tcp 만 지원 합 니 다).
자동 코드 생 성(규범 과 지원 프로그램 을 정의 하고 지원),오류 재 시도,데이터 유일 성,패 킷 의 크기 처리 등 이 있 기 때문에 사용 하기 쉬 운 프레임 워 크 를 개발 하려 면 계속 발전 해 야 한다.여 기 는 그의 원 리 를 간단하게 분석 할 뿐이다.
마지막 으로 복원 벽돌...움직이다
코드:git:https://github.com/supperlitt/tcp_all
이상 은 c\#RPC 프레임 워 크 의 사용 프로필 에 대한 상세 한 내용 입 니 다.c\#RPC 프레임 워 크 에 관 한 자 료 는 다른 관련 글 을 주목 하 세 요!

좋은 웹페이지 즐겨찾기