My first wrangle with NoSQL. An explanation of what I made and my thoughts on NoSQL versus the traditional RDBMS

I had a random idea the other day. I wanted to write a server that would serve, generate and store image manipulations on the fly. So that’s what I made. You can check out the GitHub repository here. The server itself takes HTTP requests much like a standard web server. It takes an image id and any sizing or cropping parameters you would like and returns the image. When you pass sizing or cropping parameters it checks to see if the image with those parameters already exists and returns that if it does. This avoids regenerating images all the time. It stores information about the images in the Mongo database.

This was sort of a new field to me as it was my first venture into the world of NoSQL. I really like the way everything works. I think this project worked well with the “Unstructured Data” that NoSQL is really good for. Different images could have a couple of sets of different attributes (and of course this would expand if I added more types of manipulations). It would certainly have been doable in an relational database, but I think this works better.

MongoDB uses a notation called BSON for its definitions and queries. BSON is much like the  - probably more – familiar JSON, except with the ability to have binary objects (hence the B in BSON). Defining the data in BSON is rather trivial and, in my opinion, more natural than SQL (and certainly less verbose). I also like the fact that you can have nested data. In some instances this can be used as opposed to the using a foreign key in an RDBMS.

I did a good bit of reading before I dove into NoSQL to see if it was really any better. I had heard lots on how “NoSQL is the new wave and it’s so much better!”. Well the answer I found: It depends. There’s lots of arguments that say NoSQL, because of it’s unstructured nature, is much better at scaling. However, there’s plenty that say RDBMSs scale just fine too. I think it’s not that one can scale up better than the other, I think it’s just easier to scale up a NoSQL database in terms of code implementation. What I really got out of my reading was that the reason to use NoSQL is not necessarily for scaling, but really, it comes down to whether you need the ability to have unstructured data. As I stated, what I did with my server was not necessarily completely structured, however it could still have been implemented in typical relational style. I’ve heard that there are scenarios where you would need to have unstructured data storing abilities. I assume it’s true, though I can’t really think of an example so who knows.

All in all, I like the interactions with MongoDB a lot better than I do using SQL like I would with MySQL (Which is what I have been using for data storage since I started programming 8 years ago). However, I could get very similar interactions by using an ORM (Object Relational Mapper). Those pretty much keeps the SQL away from my eyes and lets me use an Object-Oriented interface with the server. In general, for my needs, I could use either one. I don’t really have to worry about the scale issue (for now anyways) but like i said, it really comes down to the need for structured vs. unstructured data.

I should note: my GitHub project code is not ‘ready for distribution’. It was really just a test project. The code is there for anyone to play with and modify, or look at as an example. It wont run straight away if you download it and fire it up. You will need to modify it for your environment.

 

Here is a little tutorial for handling multiple simultaneous connections in C#.

The trick to doing asynchronous I/O with C# sockets is the AsyncCallback. You call the socket.Begin* methods, passing them an AsyncCallback object (which is a method) and a state object. The state object you pass is the socket itself. When the callback is called, it is passed an IAsyncResult. This contains the AsyncState, which is the state object you passed. You can cast it into a Socket and continue processing. Now we can get to the code:

The first thing we need is to include the proper references:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

You may be wondering why we need System.Threading. This is because we need a ManualResetEvent. This is used to signal events between the methods.

We’ll now write a class called ServerRunner, which starts the serving by its method Run(). It has 3 other methods, AcceptCon(), SendData(), and ReceiveData(). All 3 methods take an IAsyncResult “iar”.

First we need a couple of class variables

        private Byte[] data = new Byte[2048];
        private int size = 2048;
        private Socket server;
        static ManualResetEvent allDone = new ManualResetEvent(false);

This gives us some stuff for the actual transmission of the data, and of course the ManualResetEvent that I explained earlier. Heres our Run method that starts everything:

        public void Run()
        {
            try
            {
                server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint iep = new IPEndPoint(IPAddress.Any, 33333);
                server.Bind(iep);
                Console.WriteLine("Server initialized..");
                server.Listen(100);
                Console.WriteLine("Listening...");
                while (true)
                {
                    allDone.Reset();
                    server.BeginAccept(new AsyncCallback(AcceptCon), server);
                    allDone.WaitOne();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }

This starts like any listening socket. Create an IPEndPoint and bind your server socket to it. Then call the listen method. Then you want to start an infinite loop calling the BeginAccept() method with the AsyncCallback and state object. Around it, you want your ManualResetEvent’s Reset() and WaitOne() methods. This makes it so it waits until the connection has actually been accepted and started to be dealt with before it can start to accept a new one. In the next method You’ll see the ManualResetEvent’s Set() method, which tells it that it is ok to continue to the next connection. Heres the AcceptCon() method we put as the AsyncCallback to the BeginAccpet()

        void AcceptCon(IAsyncResult iar)
        {
            allDone.Set();
            try
            {
                Socket oldserver = (Socket)iar.AsyncState;
                Socket client = oldserver.EndAccept(iar);
                Console.WriteLine(client.RemoteEndPoint.ToString() + " connected");
                byte[] message = Encoding.ASCII.GetBytes("Welcome");
                client.BeginSend(message, 0, message.Length, SocketFlags.None, new AsyncCallback(SendData), client);
            }
            catch (Exception)
            {
                Console.WriteLine("Connection closed..");
                return;
            }
        }

In this method, first we call the ManualResetEvent’s Set() method, which tells it that we have gotten what we need. Then we cast the iar.AsyncState (the state object we passed into the method, which was a Socket) back to what it originally was so we can use it. This code sends a simple “Welcome” message to the client that connects. However you can choose to do whatever you want. We then call the BeginSend method, again with an AsyncCallback (this time to the SendData() method) and a state object (this time client socket).

        void SendData(IAsyncResult iar)
        {
            try
            {
                Socket client = (Socket)iar.AsyncState;
                int sent = client.EndSend(iar);
                client.BeginReceive(data, 0, size, SocketFlags.None, new AsyncCallback(ReceiveData), client);
            }
            catch (Exception)
            {
                Console.WriteLine("Connection closed..");
                return;
            }
        }

This method finishes off the send, and then starts to listen for more data by calling the ReceiveData() method as an AsyncCallback, again passing the client socket as a state object.

        void ReceiveData(IAsyncResult iar)
        {
            try
            {
                Socket client = (Socket)iar.AsyncState;
                int recv = client.EndReceive(iar);
                if (recv == 0)
                {
                    client.Close();
                    server.BeginAccept(new AsyncCallback(AcceptCon), server);
                    return;
                }
                string receivedData = Encoding.ASCII.GetString(data, 0, recv);
                // process received data here
                // decide what to send back
                byte[] message2 = Encoding.ASCII.GetBytes("reply");
                client.BeginSend(message2, 0, message2.Length, SocketFlags.None, new AsyncCallback(SendData), client);
            }
            catch (Exception)
            {
                Console.WriteLine("Connection closed..");
                return;
            }
        }

This is where we do all the data handling. It takes in data, does what you need to do with the data, and sends back a response. In this method we check to see if the socket is done, in which case we close it, call BeginAccept again to continue listening, and return to end the method execution. This method doesn’t actually have any data handling in it, it simply sends the string “reply” as a response to every piece of data that comes in. But I left comments showing you where to put your methods to actually deal with the data and come up with a response. When we are done handling the data, we call the BeginSend, which sends off the data, and then goes back to receiving again. It continues until the connection is closed.

A small warning about this code: The only exception handling in here is to keep the server from crashing if the client disconnects unexpectedly. If you are planning to use this as any sort of production code, I suggest you put in much more detailed exception handling.

Well. There it is. It’s much simpler than I thought it was going to be, and it only requires those 3 methods really. Hope you can all put this to good use.

 

I’m writing this post for two reasons:

  1. It is a pretty cool thing to do and very useful
  2. The amount of useful documentation and links you can find is very small

It took me about 12 hours of futsing with this and Googling things to get this correct. Most of my information actually came from bug reports of various open source projects that use UPnP for peer-to-peer connections.

I haven’t put much error checking in here, I want to keep the code short and as easy to understand as possible. I’ll leave that one up to the users to figure out.

To use this code, you must first call NAT.Discover(). This makes sure you have a UPnP device available. After that you can go ahead and Add and Delete ports as you wish. Heres the code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Xml;
using System.IO;

namespace enChatClient
{
    public class NAT
    {
        static TimeSpan _timeout = new TimeSpan(0, 0, 0, 3);
        public static TimeSpan TimeOut
        {
            get { return _timeout; }
            set { _timeout = value; }
        }
        static string _descUrl, _serviceUrl, _eventUrl;
        public static bool Discover()
        {
            System.Net.NetworkInformation.NetworkInterface nic = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()[0];

            System.Net.NetworkInformation.GatewayIPAddressInformation gwInfo = nic.GetIPProperties().GatewayAddresses[0];
            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
            string req = "M-SEARCH * HTTP/1.1rn" +
            "HOST: " + gwInfo.Address.ToString() + ":1900rn" +
            "ST:upnp:rootdevicern" +
            "MAN:"ssdp:discover"rn" +
            "MX:3rnrn";
            Socket client = new Socket(AddressFamily.InterNetwork,
                SocketType.Dgram, ProtocolType.Udp);
            IPEndPoint endPoint = new
            IPEndPoint(IPAddress.Parse(gwInfo.Address.ToString()), 1900);

            client.SetSocketOption(SocketOptionLevel.Socket,
                SocketOptionName.ReceiveTimeout, 5000);

            byte[] q = Encoding.ASCII.GetBytes(req);
            client.SendTo(q, q.Length, SocketFlags.None, endPoint);
            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint senderEP = (EndPoint)sender;

            byte[] data = new byte[1024];
            int recv = client.ReceiveFrom(data, ref senderEP);
            string queryResponse = "";
            queryResponse = Encoding.ASCII.GetString(data);

            DateTime start = DateTime.Now;

            string resp = queryResponse;
            if (resp.Contains("upnp:rootdevice"))
            {
                resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9);
                resp = resp.Substring(0, resp.IndexOf("r")).Trim();
                if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp)))
                {
                    _descUrl = resp;
                    return true;
                }
            }
            return false;
        }

        private static string GetServiceUrl(string resp)
        {
            XmlDocument desc = new XmlDocument();
            try
            {
                desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
            }
            catch (Exception)
            {
                return null;
            }   
            XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
            nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
            XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
            if (!typen.Value.Contains("InternetGatewayDevice"))
                return null;
            XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType="urn:schemas-upnp-org:service:WANIPConnection:1"]/tns:controlURL/text()", nsMgr);
            if (node == null)
                return null;
            XmlNode eventnode = desc.SelectSingleNode("//tns:service[tns:serviceType="urn:schemas-upnp-org:service:WANIPConnection:1"]/tns:eventSubURL/text()", nsMgr);
            _eventUrl = CombineUrls(resp, eventnode.Value);
            return CombineUrls(resp, node.Value);
        }

        private static string CombineUrls(string resp, string p)
        {
            int n = resp.IndexOf("://");
            n = resp.IndexOf('/', n + 3);
            return resp.Substring(0, n) + p;
        }

        public static void ForwardPort(int port, ProtocolType protocol, string description)
        {
            if (string.IsNullOrEmpty(_serviceUrl))
                throw new Exception("No UPnP service available or Discover() has not been called");

            IPHostEntry ipEntry = Dns.GetHostByName(Dns.GetHostName());
            IPAddress addr = ipEntry.AddressList[0];

            XmlDocument xdoc = SOAPRequest(_serviceUrl,
                "<m:AddPortMapping xmlns:m="urn:schemas-upnp-org:service:WANIPConnection:1"><NewRemoteHost xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string"></NewRemoteHost><NewExternalPort xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui2">" +
                port.ToString() + "</NewExternalPort><NewProtocol xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">" +
                protocol.ToString().ToUpper() + "</NewProtocol><NewInternalPort xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui2">" +
                port.ToString() + "</NewInternalPort><NewInternalClient xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">" +
                addr + "</NewInternalClient><NewEnabled xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="boolean">1</NewEnabled><NewPortMappingDescription xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">" +
                description + "</NewPortMappingDescription><NewLeaseDuration xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui4">0</NewLeaseDuration></m:AddPortMapping>",
                "AddPortMapping");
        }

        public static void DeleteForwardingRule(int port, ProtocolType protocol)
        {
            if (string.IsNullOrEmpty(_serviceUrl))
                throw new Exception("No UPnP service available or Discover() has not been called");
            
            XmlDocument xdoc = SOAPRequest(_serviceUrl,
            "<u:DeletePortMapping xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">" +
            "<NewRemoteHost></NewRemoteHost>" +
            "<NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
            "<NewProtocol>" + protocol.ToString().ToUpper() + "</NewProtocol>" +
            "</u:DeletePortMapping>", "DeletePortMapping");
        }

        public static IPAddress GetExternalIP()
        {
            if (string.IsNullOrEmpty(_serviceUrl))
                throw new Exception("No UPnP service available or Discover() has not been called");
            XmlDocument xdoc = SOAPRequest(_serviceUrl, "<u:GetExternalIPAddress xmlns:u="urn:schemas-upnp-org:service:WANIPConnection:1">" +
            "</u:GetExternalIPAddress>", "GetExternalIPAddress");
            XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
            nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
            string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
            return IPAddress.Parse(IP);
        }

        private static XmlDocument SOAPRequest(string url, string soap, string function)
        {
            string req = "<?xml version="1.0"?>" +
            "<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">" +
            "<s:Body>" +
            soap +
            "</s:Body>" +
            "</s:Envelope>";
            WebRequest r = HttpWebRequest.Create(url);
            r.Timeout = 10000;
            r.Method = "POST";
            byte[] b = Encoding.UTF8.GetBytes(req);
            r.Headers.Add("SOAPACTION", ""urn:schemas-upnp-org:service:WANIPConnection:1#" + function + """);
            r.ContentType = "text/xml; charset="utf-8"";
            r.ContentLength = b.Length;
            r.GetRequestStream().Write(b, 0, b.Length);
            XmlDocument resp = new XmlDocument();
            WebResponse wres = r.GetResponse();
            Stream ress = wres.GetResponseStream();
            resp.Load(ress);
            return resp;
        }
    }
}

These SOAP requests use two UPnP “commands”:

  • AddPortMapping
  • DeletePortMapping

AddPortMapping needs the following parameters:

  1. NewRemoteHost – Used to make it so that only one remote host can connect. Generally not used and OK to leave blank.
  2. NewExternalPort – The port that the incoming outside connection should be connecting on.
  3. NewProtocol – This is the protocol that will be running on the port. should be either “TCP” or “UDP”.
  4. NewInternalPort – The port on the inside of the network that the connection will be forwarded to.
  5. NewInternalClient – The internal IP address the connection should be forwarded to.
  6. NewEnabled – Whether the connection should be enabled or not. You want to set this to 1 so it actually works.
  7. NewPortMappingDescription – Just a short description of what the port is actually being used for. Generally the program name.
  8. NewLeaseDuration – Just set this to 0.

If the AddPortMapping doesn’t work for some reason you will get a server 500 error. Otherwise you will get an xml response

DeletePortMapping needs the following parameters:

  1. NewRemoteHost – Same thing as AddPortMapping. Not really used.
  2. NewExternalPort – The external port that the forwarding you’re deleting is running on.
  3. NewProtocol – The protocol it is running. “TCP” or “UDP”

If there is no such port mapping in the table, you will get a server 500 error, so be careful.

You can also check to see if a mapping exists by using GetSpecificPortMapping entry which takes NewRemoteHost, NewExternalPort, and NewPortMappingProtocol, and it returns a bunch of data on the connection. However the downside is that if it doesn’t exist, you get a server 500 error, and these can be trick to handle if you’re using a WebRequest, as it just throws it as a WebException.

The other way to do it is to GetGenericPortMappingEntry which returns the table of them and you can search it for the one you want.

NOTE: AddPortMapping will overwrite the previous mapping on the same port and protocol.

 

I’ve been working on Fizzure A LOT recently. I made a FizzSrvLight that is not a distributed system like the regular one, which therefore allowed me to write one effectively in about 3 hours. On the way I decided to make a few of my own methods and then realized, hey these can be used in other projects too!

So I made a class library (.dll – Dynamically Linked Library ) with a few methods that have to do with TCP Data transmition. The most important of which is the Send method that I made. Now this is really only useful for the client. Anyway, heres the snippet:


public static void Send(TcpClient Client, String Command)
{
Console.WriteLine("Opening Server Stream");
NetworkStream n = Client.GetStream();
String send = Command;
String receive = null;
byte[] msg = System.Text.Encoding.ASCII.GetBytes(send);
n.Write(msg, 0, msg.Length);
Console.WriteLine("SENT: {0}", send);
}

this method is meant for console programs, but if you are using a GUI all you really need to do is delete the Console.WriteLines()’s in there and replace it with wherever you want the output.

Hope this is helpful to everyone!

 

Ok, this is just a quick snippet of code I wrote to get a working server up. Obviously theres more commands I could put in there in plenty of different ways, but I really just wanted to keep things simple for now. This took me about 2 hours.

This snippet is the main body of code that controls everything. If you go through it and read you’ll see that I made a struct to hold the information on files named File, in the namespace Structure. So you would access it by saying in this [MainNamespace].Structure.File; or you can just use Structure.File. I’ll paste the code for the struct at the end.

I didn’t leave too many comments because I used a lot of Writelines to tell me what it was doing, and for debugging purposes. Those kind of tell you what things do what.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace FizzSrvLight
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(“FizzSrvLight :: Non-Distributed Fizzure Serving Capabilities”);
System.Threading.Thread.Sleep(1000);
Console.Write(“Loading…”);
Console.WriteLine(“!”);

Console.WriteLine(“Initiating Server Variables…”);
System.Net.IPAddress localaddr = System.Net.IPAddress.Parse(“127.0.0.1″);

Console.WriteLine(“Constructing Server Objects…”);
System.Net.Sockets.TcpListener MainServer = new System.Net.Sockets.TcpListener(localaddr, 9000);

Console.WriteLine(“Starting Server…”);
MainServer.Start();

Byte[] bytes = new Byte[1024];
String data = null;
String send = null;

while (true)
{
Console.WriteLine(“Waiting for connection…”);

// Accept Requests
System.Net.Sockets.TcpClient client = MainServer.AcceptTcpClient();
Console.WriteLine(“Client Connected!”);

// Clear Buffers
data = null;
send = null;

// Get Stream Object for reading and writing
System.Net.Sockets.NetworkStream stream = client.GetStream();

int i;

// Initialize File Holder
System.Collections.ArrayList CurrentFiles = new System.Collections.ArrayList();

// Loop to recieve all data sent from client
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Clear buffers again
data = null;
send = null;
string message = “OK”;
// Get data as string
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
Console.WriteLine(“FIZZ_RCV: {0}”, data);

String[] command = data.Split(‘ ‘);

// Insert Possible Commands Here
if (command[0] == “FIZZ_ADDFILE”)
{
FizzSrvLight.Structure.File file = new FizzSrvLight.Structure.File(command[1], command[2], command[3], command[4], command[5], command[6]);
CurrentFiles.Add(file);
}
else if (command[0] == “FIZZ_RMVFILE”)
{
FizzSrvLight.Structure.File file = new FizzSrvLight.Structure.File(command[1], command[2], command[3], command[4], command[5], command[6]);
CurrentFiles.Remove(file);
}
else if (command[0] == “FIZZ_AUTH”)
{
string username = command[1];
string password = command[2];
}
else
{
Console.WriteLine(“FIZZ_INVALID_INPUT”);
Console.WriteLine(“Error Handled”);
message = “ERROR”;
}

send = message;

byte[] msg = System.Text.Encoding.ASCII.GetBytes(send);

// Send back an OK response;
stream.Write(msg, 0, msg.Length);
Console.WriteLine(“FIZZ_SND: ” + message);
}
System.Threading.Thread.Sleep(1000);
}
}
}
}

Now, time for the struct.

namespace FizzSrvLight
{
namespace Structure
{
public struct File
{
public string FileName;
public string FilePath;
public string FileType;
public string SharedBy;
public string IPAddress;
public string Blacklist;

public File(string name, string path, string type, string user, string ipaddr, string blacklisted)
{
FileName = name;
FilePath = path;
FileType = type;
SharedBy = user;
IPAddress = ipaddr;
Blacklist = blacklisted;
}

}
}
}

Well, there you have it. A very simple TcpListener Serve. Obviously theres better ways to do it but this is pretty simple, straight forward, and just all around easy. Please leave comments if you find bugs in it or see errors or even if you just don’t understand what some of it does.

© 2012 Code Brain Suffusion theme by Sayontan Sinha