Skip to content

LupinChiu/Regulus

 
 

Repository files navigation

Regulus Remote

Maintainability Actions Status Build status Coverage Status commit last date
HitCount Discord

Introduce

This is server-client connection framework, available for Unity development.

Feature

  • Support .Net Standard 2.0
  • Remote method invocation
  • Support Unity il2cpp

Latest Version

Download the latest Latest Version .

Architecture

Architecture

Communication

Instead of client communicating with server in packets, server send object to client through interface.
Here are the steps to set up the communication.
A. Define the interface IGreeter.

namespace Common
{
	public class HelloRequest
	{
		public string Name;
	}
	public class HelloReply
	{
		public string Message;
	}
	public interface IGreeter
	{
		Regulus.Remote.Value<HelloReply> SayHello(HelloRequest request);
	}
}

B. Implement the greeter class.

namespace Server
{	
	class Greeter : IGreeter
	{				
		Regulus.Remote.Value<HelloReply> SayHello(HelloRequest request)
		{
			return new HelloReply() { Message = $"Hello {request.Name}." };
		}
	}
}

C. Use binder to send Greeter to the client.

namespace Server
{
	public class Entry	
	{
		readonly Greeter _Greeter;
		readonly Regulus.Remote.IBinder _Binder;
		public Entry(Regulus.Remote.IBinder binder)
		{
			_Greeter = new Greeter();
			_Binder = binder;
			binder.Bind<IBinder>(_Greeter);
		}
		public void Dispose()
		{
			// you can call Unbind to notify the client to cancel the greeter.  
			_Binder.Unbind<IBinder>(_Greeter);
		}
	}
}

D. Use an Agent to receive greeter from the server.

namespace Client
{
	class Entry
	{
		public Entry(Regulus.Remote.IAgent agent)
		{
			agent.QueryNotifier<Common.IGreeter>().Supply += _AddGreeter;
			agent.QueryNotifier<Common.IGreeter>().Unsupply += _RemoveGreeter;
		}
		void _AddGreeter(Common.IGreeter greeter)
		{
			// todo: Having received the greeter from the server, 			 
			//       begin to implement the following code.
		}
		void _RemoveGreeter(Common.IGreeter greeter)
		{
			// todo: The server has canceled the greeter.
		}
	}
}

In this way, the server and the client can communicate through the interface and achieve object-oriented development as much as possible.
In addition, bind and unbind are used to switch the objects of the server, so as to control the access rights of the client conveniently.
The current communication capabilities of the interface are as follows...

Serialization supports the following types...
short, ushort, int, uint, bool, logn, ulong, float, decimal, double, System.Guid, char, byte, enum, string and array of the types.

Getting Start

This is a server-client framework, so it requires at least four projects: Common, Protocol, Server and Client.

Dependency

  • Visual Studio 2019.
  • .NET Core Sdk 2.0 or above.

Common And Protocol

Create a protocol component to handle the communication requirements between client and server.

Create Common Project.

Sample/Common>dotnet new classlib 

Add references to Common.csproj.

<ItemGroup>
	<ProjectReference Include="Regulus\Regulus.Remote\Regulus.Remote.csproj" />
</ItemGroup>

Add a sample file,IFoo.cs.

namespace Common
{
	public interface IFoo	
	{
	}
}

Create Protocol Project.

Sample/Protocol>dotnet new classlib 

Add references to Protocol.csproj.

<ItemGroup>
	<ProjectReference Include="Regulus\Regulus.Remote\Regulus.Remote.csproj" />
	<ProjectReference Include="Regulus\Regulus.Serialization\Regulus.Serialization.csproj" />
	<ProjectReference Include="Common\Common.csproj" />
</ItemGroup>

Generation the protocol code.
Use Regulus.Application.Protocol.CodeWriter.dll to generate code to Protocol project.

Sample>dotnet run --project Regulus/Regulus.Application.Protocol.CodeWriter  --common Common.dll --output Protocol

At this point, there are two projects, Common.csproj and Protocol.csproj.

Server

The following example sets up a server in console.

Sample/Server>dotnet new console 

Add references to Server.csproj.

<ItemGroup>
	<ProjectReference Include="Regulus\Regulus.Remote.Server\Regulus.Remote.Server.csproj" />
	<ProjectReference Include="..\Common\Common.csproj" />	
</ItemGroup>

The server needs an entry point for the startup environment.
Create a entry class that inherits from the Regulus.Remote.IEntry.

namespace Server
{
	public class Entry : Regulus.Remote.IEntry
	{
		void IBinderProvider.AssignBinder(IBinder binder)
		{
			// IBinder is what you get when your client completes the connection.
		}		
	}
}

Create service.

namespace Server
{
	static void Main(string[] args)
	{
		var protocolAsm = Assembly.LoadFrom("Protocol.dll");
		// Create protocol.
		var protocol = Regulus.Remote.Protocol.ProtocolProvider.Create(protocolAsm);
		// your server entry.
		var entry = new Entry();
		
		// Create service.
		var service = Regulus.Remote.Server.Provider.CreateService(entry, protocol);
		
		entry.Run();
	
		service.Dispose();
	}
}

Client

The following example sets up a client in console.

Sample/Client>dotnet new console 

Add references to Client.csproj.

<ItemGroup>
	<ProjectReference Include="Regulus\Regulus.Remote.Server\Regulus.Remote.Client.csproj" />
	<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>

Create a Regulus.Remote.IAgent to handle the connection and receive objects from the server.

var agent = Regulus.Remote.Client.Provider.CreateAgent(protocol);
// The agent uses single-thread continuations to process server requests and responses, so it needs to keep calling this method to stay operational. 
agent.Update(); 

Receive objects from the server side.

var notifier = agent.QueryNotifier<Common.IFoo>();
notifier.Supply += _AddFoo; // The Supply is the Bind for the corresponding server.
notifier.Unsupply += _RemoveFoo;// The Unsupply is the Unbind for the corresponding server.

Connection

By default, Tcp connectivity is provided.
Listener

var listener = Regulus.Remote.Server.CreateTcp(service);
listener.Bind(port);
listener.Close()

Connecter

var connecter = Regulus.Remote.Client.CreateTcp(agent);
var online = connecter.Connect(ipaddress);
if(online != null)
	// connect success.
else
	// connect failed.

Extension

If you want to customize. Simply provide IService and IAgent a data stream.
Implement Regulus.Network.IStreamable.

namespace Regulus.Network
{
    
    public interface IStreamable
    {
        /// <summary>
        ///     Receive data streams.
        /// </summary>
        /// <param name="buffer">Stream instance.</param>
        /// <param name="offset">Start receiving position.</param>
        /// <param name="count">Count of byte received.</param>
        /// <returns>Actual count of byte received.</returns>
        System.Threading.Tasks.Task<int> Receive(byte[] buffer, int offset, int count);
        /// <summary>
        ///     Send data streams.
        /// </summary>
        /// <param name="buffer">Stream instance.</param>
        /// <param name="offset">Start send position.</param>
        /// <param name="count">Count of byte send.</param>
        /// <returns>Actual count of byte send.</returns>
        System.Threading.Tasks.Task<int> Send(byte[] buffer, int offset, int count);
    }
}

Server

class Server
{
	Regulus.Remote.Soul.IService _Service;
	// When listening on a connection.
	void Acctpt(Regulus.Network.IStreamable stream)
	{
		_Service.Join(stream);
	}
}

Client

class Client
{
	void Start()
	{
		// custom connecter
		var connecter = CreateFromCustom();
		var agent = Regulus.Remote.Client.provider.CreateAgent(protocol);
		agent.Start(connecter);
		// begin your connect.
		connecter.Connect("127.0.0.1:21861");
	}
}

Sample

Regulus.Samples ,This repository shows applications such as chat rooms.

About

A simple C# network library.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C# 99.9%
  • Batchfile 0.1%