Sharing a COM port over TCP

What would be a simple design pattern for sharing a COM port over TCP to multiple clients?

For example, a local GPS device that could transmit co-ordinates to remote hosts in realtime.

So I need a program that would open the serial port and accept multiple TCP connections like:

class Program
{
    public static void Main(string[] args)
    {
        SerialPort sp = new SerialPort("COM4", 19200, Parity.None, 8, StopBits.One); 

        Socket srv = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        srv.Bind(new IPEndPoint(IPAddress.Any, 8000));
        srv.Listen(20);

        while (true)
        {
            Socket soc = srv.Accept();
            new Connection(soc);
        }
    }
}

I would then need a class to handle the communication between connected clients, allowing them all to see the data and keeping it synchronized so client commands are received in sequence:

class Connection
{
    static object lck = new object();
    static List<Connection> cons = new List<Connection>();

    public Socket socket;
    public StreamReader reader;
    public StreamWriter writer;

    public Connection(Socket soc)
    {
        this.socket = soc;
        this.reader = new StreamReader(new NetworkStream(soc, false));
        this.writer = new StreamWriter(new NetworkStream(soc, true));
        new Thread(ClientLoop).Start();
    }

    void ClientLoop()
    {
        lock (lck)
        {
            connections.Add(this);
        }
        while (true)
        {
            lock (lck)
            {
                string line = reader.ReadLine();
                if (String.IsNullOrEmpty(line))
                    break;

                foreach (Connection con in cons)
                    con.writer.WriteLine(line);
            }
        }
        lock (lck)
        {
            cons.Remove(this);
            socket.Close();
        }
    }
}

The problem I'm struggling to resolve is how to facilitate communication between the SerialPort instance and the threads.

I'm not certain that the above code is the best way forward, so does anybody have another solution (the simpler the better)?

Answers


Why write at such a low-level (sockets)? Why not use WCF as the communication between the clients and the server and present a cleaner, strongly-typed interface instead of raw access to the GPS device?

Devices like this are often best managed independently from the clients calling in - i.e. you have your own separate thread that talks to the GPS device, polling it at the appropriate interval and populating shared data structures with the current location - while the clients make service calls and are supplied with data from the shared data structures. All error handling and recovery for the sometimes unreliable device connection is handled by the GPS thread and the clients don't need to each get involved with such nastiness. They can make non-blocking calls to get status updates and those updates might include a status 'position unavailable' while the GPS thread is frantically trying to re-establish communication.

So I would create a service that abstracts the particulars of dealing with this specific device and provides a clean interface to the clients. It might for example offer a services like GetPosition() which returns some class like "GeoCoordinate". That way if you ever need to support other location sensing devices you can add them without making any changes to the client code.

                   GPS <--Serial--> Server <--WCF--> Clients

I have a system that communicates with hundreds of different devices, many over serial ports and other semi-reliable connections and this is the approach I use. See http://blog.abodit.com.

----- per your additional requirement to use TELNET: maybe something like:

Create a thread that handles all communication with the device itself.

Create a class that encapsulates a single WorkItem - what to send, the response, and a WaitHandle.

Use a Queue to queue up requests from clients. Each client waits on the WaitHandle for its response to be ready.

Let the single communication thread pull work items off that queue, send them to the GPS device, get the response, store the response in the WorkItem (or set a flag for failures), and then set the wait handle to say that the WorkItem is done.

If the requests come in faster than the GPS can handle, add code so it can return cached values for requests coming within a small time window from the last successful request to the device.

In effect you are now presenting a virtual GPS device to all the clients but internally you are serializing all their requests (on a Queue) and managing communication with the GPS device on a single thread so you can do the Request-Response cycle easily without interference.

This also allows you to time-out nicely (on the wait handle) to inform a client that no response is currently available.


Need Your Help

datatables initialize again after success ajax pull data

javascript jquery datatables

Im using datatables and I came across for a self requirements where I want a live table data and I use datatables for this requirements, however Im having an issue where datatables wont initialize ...

How to get rid of theme stealers?

php javascript jquery html obfuscation

So basically I'm creating wordpress themes for Themeforest, and I would like to get rid of theme stealers. I need ideas how to do that, I have a plan, but I'm not sure what would be the best soluti...