socket 实现单一串口共享读写操作

 
Double-click
Select to translate

前提:物理串口连接到PC上,通过串口号被PC唯一识别。

此时,物理串口通过该串口号仅能被单一线程或进程实例并占用,
其他线程或进程不能再通过该串口号与物理串口通信。这个暂称为串口独占性。

解决思路:
核心思想:利用计算机软件中的socket编程,一个socket server 可以连接多个socket client,由socket server 完成多个socket client与物理串口的通信。

实现过程:
1、编程语言根据物理串口的串口号实例化一个串口操作类,串口操作类负责与物理串口通信。建立串口写线程和串口读线程。其中,串口读线程不断收取物理串口输出,并存放到读缓存。串口写线程不断从写缓存取命令,由其不断发往物理串口。
2、建立一个可靠的Socket Server,当有Socket Client连接时,由其将读缓存中的数据发给Socket Client,并不断收取Socket Client发来的命令,存放到写缓存中。
3、编程语言线程/进程通过建立Socket Client连接到Socket Server,既可实现多个线程/进程
与物理串口的通信

 

以下没有实现缓存机制,而是将读取到的串口数据放入队列:

socket server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
import socket
import psutil
import traceback
import threading
import SocketServer
import json
import sys
import Queue
import time
 
from serial import Serial
from SocketServer import StreamRequestHandler as SRH
from CustomStringIO import  CustomStringIO
 
SERIALCOMNUM = {}
 
class MainHandler(SRH):
     
    def handle(self):
        try:
            print 'Client [%s] is Connected To The Server On Port [%s].' % (self.client_address[0], self.client_address[1])
            self.keep_alive = True
            while self.keep_alive:
                data = self.request.recv(4096 * 3)
                if not data:
                    break
                data_json = json.loads(data)
                if "RequestType" in data_json:
                    if data_json["RequestType"] == "DevSerialHandle":
                        if "Port" in data_json["Args"]:
                            self.dev_serial_handler(data_json, close_timeout=60)
                            break
                else:
                    break
        except Exception as e:
            traceback.print_exc()
        finally:
          print '<------ SerialSocketServer handle request finish ------>'
 
 
    def dev_serial_handler(self, data_json, close_timeout=60):
        self.read_queue = Queue.Queue()
        read_id = str(time.time())
        if data_json["Args"]['Port'] in SERIALCOMNUM:
            self.dev_serial = SERIALCOMNUM[data_json["Args"]["Port"]]['serial']
            self.dev_serial.client_buffer.update({read_id:self.read_queue})
            SERIALCOMNUM[data_json["Args"]["Port"]]['count'] += 1
        else:
            self.dev_serial = SerialHandle(data_json["Args"]['Port'])
            self.dev_serial.client_buffer.update({read_id:self.read_queue})
            SERIALCOMNUM.update({data_json["Args"]['Port']:{'serial':self.dev_serial,'count':1}})
 
        print str(SERIALCOMNUM)
        th_dev_serial_read = threading.Thread(target=self.read_dev_serial)
        th_dev_serial_read.start()
         
        is_recv_data_none = False
        while self.keep_alive:
            try:
                data = self.request.recv(4096 * 3)
                print 'your input is %s' % str(data)
            except socket.error:
                self.keep_alive = False
                print "close dut serial"
                break
            else:
                if data:
                    self.dev_serial.write(data)
                    end_time = time.time() + close_timeout
                # socket client 关闭后,self.request.recv会一直收到空字符串,等待一段时间后,关闭连接
                else:
                    if is_recv_data_none == False:
                        is_recv_data_none = True
                        end_time = time.time() + close_timeout
                    if time.time() > end_time:
                        print 'wait for webbroswer connect timeout'
                        print "close dut serial"
                        self.keep_alive = False
                        break
 
        if SERIALCOMNUM[data_json["Args"]["Port"]]['count'] > 0:
            SERIALCOMNUM[data_json["Args"]["Port"]]['count'] -= 1
            SERIALCOMNUM[data_json["Args"]["Port"]]['serial'].client_buffer.pop(read_id)
        print str(SERIALCOMNUM)
 
        if SERIALCOMNUM[data_json["Args"]["Port"]]['count'] <= 0:
            print 'clear serial start'
            SERIALCOMNUM[data_json["Args"]["Port"]]['serial'].close()
            if data_json["Args"]['Port'] in SERIALCOMNUM:
                SERIALCOMNUM.pop(data_json["Args"]["Port"])
         
 
 
    def read_dev_serial(self):
        try:
            while self.keep_alive:
                # serial_log = self.dev_serial.read()
                serial_log = self.read_queue.get()
                self.request.send(serial_log)
        except socket.error:
            pass
 
 
 
class ThreadingServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
 
    def _threading_server(self):
        pass
 
 
class SerialSocketServer(object):
 
    def __init__(self, port=33233):
        self.server = None
        self.port = 33233
 
    def start(self):
        netcard_ips = self.get_netcard()
        for netcard_ip in netcard_ips:
            host = netcard_ip[1]
 
            try:
                port = self.port
                addr = (host, port)
                self.server = ThreadingServer(addr, MainHandler)
                self.server.allow_resuse_address = True
                server_thread = threading.Thread(target=self.server.serve_forever)
                server_thread.daemon = True
                server_thread.start()
                print "Starting Serial Socket Successfully!"
                while True:
                    try:
                        INPUT = raw_input()
                    except KeyboardInterrupt:
                        sys.exit(0)
                        break
                    except EOFError:
                        print 'Unknown End of file!'
                        continue
 
            except Exception, e:
                print "Starting Serial Socket Server Fail:%s" % e
 
    def stop(self):
        print "Shutdown Slave Socket Server!"
        if self.server != None:
            self.server.shutdown()
            self.server.server_close()
 
 
    def get_netcard(self):
        """获取网卡信息和ip地址
 
        """
        netcard_info = []
        info = psutil.net_if_addrs()
        for k, v in info.items():
            for item in v:
                if item[0] == 2 and not (item[1] == '127.0.0.1' or item[1] == '192.168.2.201'):
                    netcard_info.append((k, item[1]))
        return netcard_info
 
class SerialHandle():
 
    def __init__(self, port=None, baudrate=115200, timeout=30, *args, **kargs):
        self.serial = Serial(port=port, baudrate=baudrate, timeout=timeout, *args, **kargs)
        self.is_running = True
        self.read_buffer = ""
        self.write_queue = Queue.Queue()
        self.read_buffer = CustomStringIO(4096)
        th_wt = threading.Thread(target=self.__write)
        th_wt.start()
        th_rd = threading.Thread(target=self.__read)
        th_rd.start()
        self.client_buffer = {}
 
    def read(self, read_id):
        return self.read_buffer.getvalue()
 
    def __read(self):
        while self.is_running:
            serial_log = self.serial.readline()
            for key, value in self.client_buffer.items():
                self.client_buffer[key].put(serial_log)
        
    def write(self,write_string):
        self.write_queue.put(write_string)
 
    def __write(self):
        while self.is_running:
            write_string = self.write_queue.get()
            self.serial.write(write_string)
 
    def close(self):
        self.is_running = False
        self.serial.close()
        print 'close serial'
 
if __name__ == '__main__':
    SerialSocketServer().start()

启动服务器:python serial_socket_server.py

socket client :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import threading
import socket
import traceback
import json
import sys
import re
import Queue
 
class DevSerialLoadClient(threading.Thread):
 
    def __init__(self, node_ip, server_port=33233, serial_port="COM19"):
        threading.Thread.__init__(self)
        self.slave_serial_serial_server = node_ip
        self.server_port = server_port
        self.serial_port = serial_port
        self.bufsize = 4096 * 4
        self.setDaemon(True)
        self._is_running = True
        self._is_establish_connection = False
        self.client = None
 
    def connect(self):
        try:
            addr = (self.slave_serial_serial_server, self.server_port)
            self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.client.connect(addr)
            self._is_establish_connection = True
        except Exception as e:
            self._is_establish_connection = False
            print "Create Socket Connect Fail: %s" % e
 
    def run(self):
        self.connect()
        if self.is_establish_connection:
            request_msg = json.dumps({"RequestType":"DevSerialHandle","Args":{"Port":self.serial_port}})
            self.client.send(request_msg)
           
            while self._is_running:
                try:
                    response = self.client.recv(self.bufsize)
                    if not response:
                        continue
                    handle_response = re.compile('[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f|\\xff]').sub(' ', response.decode('unicode-escape'))
                    print '%s' % str(handle_response)
                except socket.error:
                    print 'socket error'
                    self.connect()
                except:
                    traceback.print_exc()
            print "------stop dev serial communication------"
            self.close() 
        
    def close(self):
        try:
            if self.client:
                self.client.shutdown(socket.SHUT_RDWR)
                self.client.close()
        except Exception, e:
            print "close socket client Error[%s]" % str(e)
 
    @property
    def is_establish_connection(self):
        return self._is_establish_connection
 
    def stop(self):
        self._is_running = False
 
if __name__ == '__main__':
    import getopt
    opts, args = getopt.getopt(sys.argv[1:], "h:s:")
    server_ip = "localhost"
    server_port = 33233
    serial_port = None
    for op, value in opts:
        if op == "-h":
            server_ip = value
        if op == '-s':
            serial_port = value
 
    if not serial_port:
        print 'should provide serial port args: like -p COM19'
 
    dev_serial = DevSerialLoadClient(node_ip=server_ip, server_port=server_port, serial_port=serial_port)
    dev_serial.start()
    while True:
        try:
            INPUT = raw_input()
            dev_serial.client.send(INPUT+'\n')
        except KeyboardInterrupt:
            sys.exit(0)
            break
        except EOFError:
            print 'Unknown End of file!'
            continue

 启动socket client:python serial_socket_client.py -h 对端ip -s 串口号

在命令行可向要连接的串口发送指令。

可建立多个client读写同一串口,所有client都可向串口发送数据;当一个client向串口输入数据后,其他client都可以收到串口的打印

posted @   yihailin  阅读(2317)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
点击右上角即可分享
微信分享提示