|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionSockets are an important part of the modern programmer's armory. To read this article, you are making extensive use of sockets – the article itself, and each image, come down a socket to your machine, and perhaps two, if you're reading this at work, behind a router. Communicating with other machines lets you spread computationally expensive tasks, share data and so on. Yet the Framework doesn't provide as good a toolkit as it does for, for example, Windows Forms (unless you are doing something 'standard' like making HTTP requests). If someone sends me some data, or connects to my server, I want an event to be thrown, just like if someone presses a button on my form. There are also problems with networks, and specifically with TCP. Connections can be lost at any time, so an I have written a small assembly which provides event-driven client-server communication over TCP (using the tools provided in Being a Simple ClientA client is the term for a user who connects to a server, typically to request data. Your browser acts as a client while it downloads material from the Internet. For most problems, there is one server and many clients, and if you're writing an application which talks to existing servers, all you need to use is a client socket. Creating a client socket with ordinary .NET functions is slightly messy, so I provide a cover. And once you have the socket, you need to create an instance of // At the top of the file, you will always need
using System.Net.Sockets;
using RedCorona.Net;
class SimpleClient{
ClientInfo client;
void Start(){
Socket sock = Sockets.CreateTCPSocket("www.myserver.com", 2345);
client = new ClientInfo(sock, false); // Don't start receiving yet
client.OnReadBytes += new ConnectionReadBytes(ReadData);
client.BeginReceive();
}
void ReadData(ClientInfo ci, byte[] data, int len){
Console.WriteLine("Received "+len+" bytes: "+
System.Text.Encoding.UTF8.GetString(data, 0, len));
}
}
Et voilà, you can receive data! (Assuming you have a server that sends you some, of course.) To send text data, use Messaged CommunicationReceiving data with Text MessagesOften, being able to pass text with a suitable end marker, often a new line, is all we need in an application. Similar things can then be done with data received like this as with command strings. To do this, assign a handler to the class TextClient{
ClientInfo client;
void Start(){
Socket sock = Sockets.CreateTCPSocket("www.myserver.com", 2345);
client = new ClientInfo(sock, false); // Don't start receiving yet
client.OnRead += new ConnectionRead(ReadData);
client.Delimiter = '\n'; // this is the default, shown for illustration
client.BeginReceive();
}
void ReadData(ClientInfo ci, String text){
Console.WriteLine("Received text message: "+text);
}
}
Now, any data received will be interpreted as text (UTF8 text, to be precise), and Binary MessagesFor more advanced applications, you may want to pass a byte stream (which can represent anything), but still keep the message together. This is typically done by sending the length first, and class MessageClient{
ClientInfo client;
void Start(){
Socket sock = Sockets.CreateTCPSocket("www.myserver.com", 2345);
client = new ClientInfo(sock, false); // Don't start receiving yet
client.MessageMode = MessageMode.Length;
client.OnReadMessage += new ConnectionRead(ReadData);
client.BeginReceive();
}
void ReadData(ClientInfo ci, uint code, byte[] bytes, int len){
Console.WriteLine("Received "+len+" bytes: "+
System.Text.Encoding.UTF8.GetString(bytes, 0, len));
}
}
In the current version, a 4-byte length parameter is sent before the data (big-endian); for example, a message containing the word "Bob" would have the byte form By now, you're probably wondering what that void ReadData(ClientInfo ci, uint code, byte[] bytes, int len){
switch(code){
case 0:
Console.WriteLine("A message: "+
System.Text.Encoding.UTF8.GetString(bytes, 0, len));
break;
case 1:
Console.WriteLine("An error: "+
System.Text.Encoding.UTF8.GetString(bytes, 0, len));
break;
// ... etc
}
}
If I were to send the message "Bob" with the tag Being a ServerAs well as receiving and sending information (just like a client), a server has to keep track of who is connected to it. It is also useful to be able to broadcast messages, that is send them to every client currently connected (for example, a message indicating the server is about to be taken down for maintenance). Below is a simple server class which simply 'bounces' messages back where they came from, unless they start with '!' in which case it broadcasts them. class SimpleServer{
Server server;
ClientInfo client;
void Start(){
server = new Server(2345, new ClientConnect(ClientConnect));
}
bool ClientConnect(Server serv, ClientInfo new_client){
new_client.Delimiter = '\n';
new_client.OnRead += new ConnectionRead(ReadData);
return true; // allow this connection
}
void ReadData(ClientInfo ci, String text){
Console.WriteLine("Received from "+ci.ID+": "+text);
if(text[0] == '!')
server.Broadcast(Encoding.UTF8.GetBytes(text));
else ci.Send(text);
}
}
As you can see, the Connect handler is passed a
EncryptionThis library includes the ability to protect a socket, using the encryption algorithm I wrote about here. This can be turned on by passing a value for encryptionType in the ClientInfo constructor or setting the public void EncryptionExamples() {
// Client, Method 1: call the full constructor
ClientInfo encrypted1 = new ClientInfo(socket1, null, myReadBytesHandler,
ClientDirection.Both, true, EncryptionType.ServerKey);
// Client, Method 2: delay receiving and set EncryptionType
ClientInfo encrypted2 = new ClientInfo(socket2, false);
encrypted2.EncryptionType = EncryptionType.ServerRSAClientKey;
encrypted2.OnReadBytes = myReadBytesHandler;
encrypted2.BeginReceive();
// Server: set DefaultEncryptionType
server = new Server(2345, new ClientConnect(ClientConnect));
server.DefaultEncryptionType = EncryptionType.ServerRSAClientKey;
}
There are three encrpytion modes supported. The first, None, performs no encryption, and is the default. If you choose to use encrypted sockets, very little is different in your application code. However, in your server, you should respond to the History
|
||||||||||||||||||||||