views:

277

answers:

2

Hello people ! That is my first question around here, my name is Anna!

My problem: My client has several personal devices (blackbox with gps) to locate people/car... There are about 12000 people/car using that device... It sends their location to specified IP/Port... I can´t do anything on that side...

My job? Devolope a listener to catch all data sended by devices and load that in database using .NET...

My idea: A Window Service using threads (maybe ThreadPool?). So the service will catch all incoming messages, create a thread and put into DB...

Is that the best solution for the problem? I was reading here about Message Queue (MSMQ)... Do you think should I use that?

Anna

+3  A: 

The number of locations / time, as well as the way the location information is transmitted (is it TCP, UDP, etc) will help determine the optimal approach.

Some questions:

  • How frequently do the 12000 devices send a message?
  • How are the locations transmitted? UDP? TCP?
  • What reliability requirements do you have?

From the sounds of it, having a service that can capture the requests and just maintain a queue internally of things to save to the database should work fine. I don't believe MSMQ will be of use to you unless you can change the transmission side, and even then, it may or may not be necessary.


EDIT: Given the comments below, I would suggest something where you have a TCP listener pass requests off to the threadpool to handle.

I would take a look at this tutorial about setting up a TCP listening server using the thread pool. The biggest potential problem I see is the number of requests - from what you're saying, you're going to have roughly 400 requests / second. This is going to be a bit of a challenge to handle without a pretty good system in place. The threadpool will probably perform better than trying to do your own threading, since you'll want to avoid the overhead of having to create new threads constantly. You'll definitely want to have very little delay in the main processing loop (like Sleep(0), or no sleep at all), since you'll have one request per 2.5 ms on average. A single Sleep tends to time slice at 14-15 ms minimum, so you probably won't want any sleep in the loop.

I would say, though, that you may find this doesn't work too well, since the raw amount of connections may be problematic. If there is any way you could convert to UDP packets being sent, it may get you better throughput (at the expense of some reliability).

Reed Copsey
It´s about 1 message each 30 second (2 per minute)...And it´s transmitted by TCP...
Anna
Anna - I updated given the comment - but since it's TCP, and you can't control the transmission, I think you're going to be limited in your options.
Reed Copsey
I am a bit surprised this solution uses TCP instead of UDP given the overhead of TCP and the cost associated with sending data over wireless networks. If there was any way to change the solution to UDP if would make your life much easier from a programming standpoint as well.
J. Scarbrough
I agree, completely - that was part of why I was asking. UDP would be much better, unless there is some need for the reliability constraints, etc. However, the question says they can't change the transmission, which is very limiting.
Reed Copsey
+1  A: 

The code below has not been tested or should only be considered as guidence. It uses the thread pool for everything. I've put everything in the same file for the example only. You should break up everything into several files.

It's important not to let all clients save to the DB directly since that would starve the thread pool. Experiment with the "MaxThreads" constant to get a value that works with your db/server.

Also remember that I do not handle any exceptions at all below. You need for instance handle SocketException on BeginRead, EndRead and TcpListener methods.

I've tried to use a minimum number of thread sync locks, the code should be quite efficient. The bottle neck will most likely be the database.

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

namespace FastListener
{
    /// <summary>
    /// Example position class, replace with a real definition
    /// </summary>
    public class Position
    {
        public int X { get; set; }
        public int Y { get; set; }
    }

    /// <summary>
    /// Needed to be able to pass socket/buffer info
    /// between asynchronous requests.
    /// </summary>
    public struct ClientContext
    {
        public Socket socket;
        public byte[] buffer;
    }

    class Program
    {
        /// <summary>
        /// Positions received from mobile clients but not yet saved
        /// into the database.
        /// </summary>
        private readonly Queue<Position> _positions = new Queue<Position>();

        /// <summary>
        /// Number of threads currently saving stuff to the database.
        /// </summary>
        private int _poolThreads;

        /// <summary>
        /// Maximum number of threads that can save info to the database.
        /// </summary>
        private const int MaxThreads = 10;

        static void Main(string[] args)
        {
            new Program().Start();
        }

        private void Start()
        {
            TcpListener listener = new TcpListener(IPAddress.Any, 1343);
            listener.Start(50);
            listener.BeginAcceptSocket(OnAccept, listener);
        }

        // Listener got a new connection
        private void OnAccept(IAsyncResult ar)
        {
            TcpListener listener = (TcpListener) ar.AsyncState;

            // It's very important to start listening ASAP
            // since you'll have a lot of incoming connections.
            listener.BeginAcceptSocket(OnAccept, listener);

            // I recommend that you create a buffer pool to improve performance
            byte[] buffer = new byte[400];

            // Now accept the socket.
            Socket socket = listener.EndAcceptSocket(ar);
            StartRead(new ClientContext {buffer = buffer, socket = socket});
        }

        private void StartRead(ClientContext context)
        {
            // start reading from the client.
            context.socket.BeginReceive(context.buffer, 0, 400, SocketFlags.None, OnReceive, context);
        }


        // Stuff from a client.
        private void OnReceive(IAsyncResult ar)
        {
            ClientContext context = (ClientContext) ar.AsyncState;

            int bytesRead = context.socket.EndReceive(ar);
            if (bytesRead == 0)
            {
                // Put the buffer back in the pool
                context.socket.Close();
                return;
            }

            // convert bytes to position.
            // i'll just fake that here.
            Position pos = new Position();

            // Either handle the request directly
            if (_poolThreads < MaxThreads)
                ThreadPool.QueueUserWorkItem(SaveToDatabase, pos);
            else
            {
                // Or enqueue it to let a already active
                // thread handle it when done with the previous position
                lock (_positions)
                    _positions.Enqueue(pos);
            }

            // Don't forget to read from the client again
            StartRead(context); 
        }

        // will save stuff to the database.
        private void SaveToDatabase(object state)
        {
            // Could use Interlocked.Increment, but not really vital if 
            // one more more extra threads are saving to the db.
            ++_poolThreads; 

            Position position = (Position) state;
            while (true)
            {
                // IMPLEMENT DB SAVE LOGIC HERE.


                // check if another position is in the queue.
                lock (_positions)
                {
                    if (_positions.Count > 0)
                        position = _positions.Dequeue();
                    else
                        break; // jump out of the loop
                }
            }

            --_poolThreads;
        }
    }
}
jgauffin