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 프레임 워 크 에 관 한 자 료 는 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
C#Task를 사용하여 비동기식 작업을 수행하는 방법라인이 완성된 후에 이 라인을 다시 시작할 수 없습니다.반대로 조인(Join)만 결합할 수 있습니다 (프로세스가 현재 라인을 막습니다). 임무는 조합할 수 있는 것이다. 연장을 사용하여 그것들을 한데 연결시키는 것이...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.