3

通过 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 实例和线程之间的通信。

我不确定上面的代码是最好的方法,那么有没有人有另一种解决方案(越简单越好)?

4

2 回答 2

2

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

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

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

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

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

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

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

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

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

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

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

实际上,您现在正在向所有客户端呈现一个虚拟 GPS 设备,但在内部,您正在序列化他们的所有请求(在队列上)并在单个线程上管理与 GPS 设备的通信,因此您可以轻松地执行请求-响应周期而不会受到干扰.

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

于 2010-04-07T07:21:34.747 回答
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()
于 2020-10-16T18:53:49.570 回答