通过TCP共享COM端口

时间:2010-04-07 05:38:02

标签: c# serial-port communication device

通过TCP将COM端口共享到多个客户端的简单设计模式是什么?

例如,本地GPS设备可以实时向远程主机发送坐标。

所以我需要一个程序来打开串口并接受多个TCP连接,如:

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);
        }
    }
}

然后我需要一个类来处理连接客户端之间的通信,允许它们全部查看数据并使其保持同步,以便按顺序接收客户端命令:

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();
        }
    }
}

我正在努力解决的问题是如何促进SerialPort实例和线程之间的通信。

我不确定上面的代码是最好的前进方式,所以有人有另一个解决方案(越简单越好)?

2 个答案:

答案 0 :(得分:2)

为什么要写这么低级别(套接字)?为什么不使用WCF作为客户端和服务器之间的通信,并提供更清晰,强类型的接口,而不是对GPS设备的原始访问?

这样的设备通常最好独立于呼入的客户端进行管理 - 即您拥有自己的单独线程,与GPS设备通信,以适当的间隔轮询它并使用当前位置填充共享数据结构 - 而客户端进行服务调用,并提供来自共享数据结构的数据。有时不可靠的设备连接的所有错误处理和恢复都由GPS线程处理,并且客户端不需要每个都涉及这种肮脏。他们可以进行非阻塞调用以获取状态更新,并且这些更新可能包括状态“位置不可用”,而GPS线程正在疯狂地尝试重新建立通信。

所以我会创建一个服务来抽象处理这个特定设备的细节,并为客户提供一个干净的界面。例如,它可以提供像GetPosition()这样的服务,它返回一些像“GeoCoordinate”这样的类。这样,如果您需要支持其他位置感应设备,您可以添加它们而无需对客户端代码进行任何更改。

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

我有一个与数百个不同设备通信的系统,很多通过串口和其他半可靠连接,这就是我使用的方法。请参阅http://blog.abodit.com

-----根据您使用TELNET的额外要求:可能类似于:

创建一个处理与设备本身通信的线程。

创建一个封装单个WorkItem的类 - 要发送的内容,响应和WaitHandle。

使用队列排队来自客户端的请求。每个客户端都在WaitHandle上等待其响应准备就绪。

让单个通信线程将工作项从该队列中拉出,将它们发送到GPS设备,获取响应,将响应存储在WorkItem中(或设置失败标志),然后设置等待句柄以表示WorkItem已经完成。

如果请求的速度超过GPS可以处理的速度,请添加代码,以便它可以返回从上次成功请求到设备的小时间窗口内发出的请求的缓存值。

实际上,您现在向所有客户端呈现虚拟GPS设备,但在内部您正在序列化所有请求(在队列上)并在单个线程上管理与GPS设备的通信,以便您可以执行请求 - 响应周期很容易没有干扰。

这也允许你很好地超时(在等待句柄上)以通知客户当前没有响应。

答案 1 :(得分:0)

您有socat,ser2net和其他程序,但是我的经验很糟糕……无法正常工作。我已经完成了这个小的python程序,可能会有用。更新端口,波特率...然后使用任何TCP客户端。如果不想用作自动可执行脚本,请删除第一行

#!/usr/bin/python

import socket
import sys
import serial

#open serial port
ser = serial.Serial('/dev/ttyAMA0', 115200, timeout=0)
#create socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

#bond to the port. Don't use localhost to accept external connections
server_address = ('', 2105)
print('starting up on {} port {}'.format(*server_address))
sock.bind(server_address)

#listen
sock.listen(1)

#loop
while True:
    #waits for a new connection
    print('waiting for a connection')
    connection, client_address = sock.accept()
    try:
        print('connection from', client_address)
        #continously send from serial port to tcp and viceversa
        connection.settimeout(0.1)
        while True:
            try:
                data = connection.recv(16)
                if data == '': break
                ser.write(data)
            except KeyboardInterrupt:
                connection.close()
                sys.exit()
            except Exception as e:
                pass
            received_data = ser.read(ser.inWaiting())
            connection.sendall(received_data)
    except Exception as e:
        print e

    finally:
        #clean up connection
        connection.close()