Skip to content

Commit d4a1813

Browse files
committed
Add readme
1 parent 87f35b9 commit d4a1813

File tree

1 file changed

+226
-0
lines changed

1 file changed

+226
-0
lines changed

readme.md

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# Tempest
2+
Tempest is a simple library for sending and receiving messages across
3+
any number of transports and dealing with them in a uniform manner. For
4+
example, you could receive the same message from a HTTP POST and from a
5+
TCP socket and from your perspective there would be no difference in
6+
handling it.
7+
8+
**Features:**
9+
10+
- Simple protocol definition
11+
- Encryption
12+
- Message signing
13+
- Multiple message transports (raw tcp, http, etc.) feeding to the same
14+
message handlers
15+
- Supports multiple Tempest protocols over a single connection
16+
17+
## Getting Started
18+
19+
### Protocol
20+
21+
The first thing we'll want to do is to define our protocol. We'll define
22+
a simple protocol that will simply broadcast a message to everyone else
23+
that's connected. We're only going to want reliable messages here,
24+
which we'll keep in mind for later.
25+
26+
As Tempest supports multiple protocols over a single connection, we'll
27+
give our protocol a unique identifier. `1` is reserved for Tempest, so
28+
we'll use `2`.
29+
30+
```csharp
31+
static class SimpleChatProtocol
32+
{
33+
public static Protocol Instance = new Protocol (2);
34+
35+
static SimpleChatProtocol()
36+
{
37+
// We need to tell our protocol about all the message
38+
// types belonging to it. Discover() does this automatically.
39+
Instance.Discover();
40+
}
41+
}
42+
```
43+
44+
Each message type needs a unique identifier. Any `ushort` value will do, but
45+
personally I like to put the values in an enum to make things easier:
46+
47+
```csharp
48+
public enum SimpleChatMessageType
49+
: ushort
50+
{
51+
ChatMessage = 1
52+
}
53+
```
54+
55+
We define messages by subclassing from `Message` and we'll need to tell it
56+
which `Protocol` it belongs to and its message identifier. To take care of
57+
this, I like to provide a small subclass of `Message` for the protocol:
58+
59+
```csharp
60+
public abstract class SimpleChatMessage
61+
: Message
62+
{
63+
protected SimpleChatMessage (SimpleChatMessageType type)
64+
: base (SimpleChatProtocol.Instance, (ushort)type)
65+
{
66+
}
67+
}
68+
```
69+
70+
Now to define our actual chat message. To define a message type, you'll
71+
override `WritePayload` and `ReadPayload`. These methods pass in a
72+
`IValueWriter` and `IValueReader` respectively, which you'll use to
73+
serialize and deserialize your message.
74+
75+
```csharp
76+
public sealed class ChatMessage
77+
: SimpleChatMessage
78+
{
79+
public string Message
80+
{
81+
get;
82+
set;
83+
}
84+
85+
public override void WritePayload (ISerializationContext context, IValueWriter writer)
86+
{
87+
writer.WriteString (Message);
88+
}
89+
90+
public override void ReadPayload (ISerializationContext context, IValueReader reader)
91+
{
92+
Message = reader.ReadString();
93+
}
94+
}
95+
```
96+
97+
And that's it for defining the protocol. **Note:** When using a platform where
98+
`System.Reflection.Emit` is unavailable, such as iOS or Windows Phone, you
99+
will need to use `Protocol.Register` and pass in your messages manually
100+
as `Protocol.Discover` is unavailable.
101+
102+
### Client
103+
104+
For most applications, Tempest has a built in `TempestClient` class you can use
105+
to handle the fundamentals. We'll subclass to provide an easy method to send
106+
a chat message and add a listener for other's chat messages:
107+
108+
```csharp
109+
public sealed class ChatClient
110+
: TempestClient
111+
{
112+
// We need to tell TempestClient what kinds of messages we're dealing
113+
// with. Earlier we decided that we only need Reliable messages here.
114+
public ChatClient (IClientConnection connection)
115+
: base (connection, MessageTypes.Reliable)
116+
{
117+
// Here we setup a handler for any `ChatMessage`s that come through.
118+
this.RegisterMessageHandler<ChatMessage> (OnChatMessage);
119+
}
120+
121+
// A simple event for brevity
122+
public event Action<string> ChatReceived;
123+
124+
public Task SendChatAsync (string message)
125+
{
126+
var msg = new ChatMessage { Message = message };
127+
return Connection.SendAsync (msg);
128+
}
129+
130+
private void OnChatMessage (MessageEventArgs<ChatMessage> e)
131+
{
132+
ChatMessage msg = e.Message;
133+
134+
var received = ChatReceived;
135+
if (received != null)
136+
received (msg.Message);
137+
}
138+
}
139+
```
140+
141+
To connect to a server, we'll need to pick a transport. We'll use the built in network
142+
transport.
143+
144+
```csharp
145+
var connection = new Tempest.Providers.Network.NetworkClientConnection (SimpleChatProtocol.Instance);
146+
147+
var client = new ChatClient (connection);
148+
await client.ConnectAsync (new Target ("hostname", port));
149+
```
150+
151+
### Server
152+
153+
For the server side of things, we also have a `TempestServer` class you can use
154+
to handle the fundamentals. We'll need to keep track of connections ourselves,
155+
where later on we may want to associate them with user data. So we'll subclass
156+
and add our connection list and a handler for `ChatMessage`:
157+
158+
```csharp
159+
public sealed class ChatServer
160+
: TempestServer
161+
{
162+
public ChatServer (IConnectionProvider provider)
163+
: base (provider, MessageTypes.Reliable)
164+
{
165+
this.RegisterMessageHandler<ChatMessage> (OnChatMessage);
166+
}
167+
168+
private readonly List<IConnection> connections = new List<IConnection>();
169+
private void OnChatMessage (MessageEventArgs<ChatMessage> e)
170+
{
171+
ChatMessage msg = e.Message;
172+
173+
// Messages come in on various threads, we'll need to make
174+
// sure we stay thread safe.
175+
lock (this.connections) {
176+
foreach (IConnection connection in this.connections)
177+
connection.SendAsync (e.Message);
178+
}
179+
}
180+
181+
protected override void OnConnectionMade (object sender, ConnectionMadeEventArgs e)
182+
{
183+
lock (this.connections)
184+
this.connections.Add (e.Connection);
185+
186+
base.OnConnectionMade (sender, e);
187+
}
188+
189+
protected override void OnConnectionDisconnected (object sender, DisconnectedEventArgs e)
190+
{
191+
lock (this.connections)
192+
this.connections.Remove (e.Connection);
193+
194+
base.OnConnectionDisconnected (sender, e);
195+
}
196+
}
197+
```
198+
199+
To start the server, we'll need to provide a transport mechanism. You can add multiple
200+
transports to listen to, but for now we'll just add a network connection provider as we've
201+
already told the client to connect with it:
202+
203+
```
204+
// NetworkConnectionProvider requires that you tell it what local target to listen
205+
// to and the maximum number of connections you'll allow.
206+
var provider = new Tempest.Providers.Network.NetworkConnectionProvider (SimpleChatProtocol.Instance, Target.AnyIP, 100);
207+
208+
var server = new ChatServer (provider);
209+
server.Start();
210+
```
211+
212+
### Transports
213+
There are currently two available transports:
214+
215+
- TCP
216+
- Supports reliable messaging
217+
- Supports encryption and signing
218+
- `NetworkClientConnection` for client connections
219+
- `NetworkConnectionProvider` for connection listeners
220+
- UDP
221+
- _Experimental_
222+
- Supports reliable and unreliable messaging
223+
- Supports encryption and signing
224+
- `UdpClientConnection` for client connections
225+
- `UdpConnectionProvider` for connection listeners
226+

0 commit comments

Comments
 (0)