欲看图文版,请PC浏览器下载附件pdf。
我想实现两个exe之间的信息互通,豆包说用TCP,遂开始学习CVI2010 TCP的例程。
遗憾的是,CVI例程向来缺乏参考文档,于是用户极不容易入门原厂例程。尤其是TCP这样的例程,本来就牵涉了一个晦涩的网路通信协议,理解例程就需要同时开两个或两个以上的project,作为主从机对着干才有实效。我于是瞎蒙了两天,终于try出来一些名堂,并写成文档,供后来者快速理解。
一、 学习TCP/IP例程Message.cws
1, 找到CVI2010的TCP/IP例程Message.cws入口
该例程Message.cws在我笔记本上的完整路径名是C:\Users\Public\Documents\National Instruments\CVI2010\samples\tcp\Message.cws。下面是通过CVI的起始页面,搜索原厂例程的步骤。
2, CVI打开Message.cws
可见一个cws里面,包含了两个project,分别是Message,和MessageReader。由于Message是加粗了的,所以是当前激活的project,在CVI中直接运行的将是MessageWriter.c对应的exe。
这里我们还需要运行MessageReader.c对应的exe,就必须再运行一个CVI并打开Message.cws,然后鼠标右键点击MessageReader,选"Set Active Project",之后这个MessageReader就变粗体了,成为当前project。
3, CVI运行Message.cws,从server端传数据到client端
豆包说,在该例程里,MessageReader是客户端,MessageWriter是服务器,所以两个exe的启动顺序是由讲究的,必须先运行服务器MessageWriter的exe。
如何区分哪个程序是Client,哪个程序是Server呢?豆包说应以代码里的实际函数为准:
谁调用了StartTCPServer(启动监听),谁就是服务器,就必须先启动;
谁调用了ConnectToTCPServer(发起连接),谁就是客户端,就必须后启动。
如果,我们错误地首先运行了客户端的MessageReader的exe,就会在运行到ConnectToTCPServer()函数时报错,Timeout error:
所以得老打老实地先运行服务器MessageWriter的exe,后运行客户端的MessageReader的exe,两个exe才能愉快地玩耍:
您观察得很敏锐!两个GUI背后的CMD黑窗,应该跟CVI的exe没有直接关系。但是为什么我会一起截屏呢?这是因为,我最开运行客户端的MessageWriter的exe,其界面上"Send Date"按键是失活的,不晓得为啥,例程源码中也没有说明,甚至没有提及应该首先运行哪个exe呢。不由得感慨------很多时候,一个工程师如果没有第三方的支援,就会卡死在万里长征的第一步。
还好有诲人不倦的豆包啊,豆包说有可能是其他进程已经率先占用了端口号10000,导致同样使用端口号10000的MessageWriter无法启动TCP服务器。于是我就按照豆包说的,输入查看端口10000是否被占用的指令:netstat -ano | findstr 10000,结果如下,果然是有个进程已经占用了本机10000号端口。然后还可以根据端口号输入指令tasklist | findstr xxxxx,来反查是哪个软件进程占用的,居然是百度云盘。
豆包说,要么把这个进程用任务管理器杀掉,要么修改CVI例程的端口号,从10000改到10001。两种方案我都试过了,都OK。前面黑窗背景的图就是杀掉YunDetectService.exe之后再运行例程的结果,下图是修改CVI例程源码的端口号到10001之后再运行的查询结果:
其中,
进程 ID 4188 是MessageWriter_dbg.exe(服务器端),它已经成功监听了10001端口(LISTENING表示服务器在等待连接)。
进程 ID 8756是MessageReader_dbg.exe(客户端),用临时端口 63310。
二、 学习TCP/IP例程 server.cws和client.cws
该例程是基于 TCP/IP 协议的客户端 - 服务器(C/S)通信经典例程,核心演示了如何利用CVI的 sockutil 库实现跨进程/跨设备的网络数据交互。
该例程在我笔记本的文件路径是C:\Users\Public\Documents\National Instruments\CVI2010\samples\tcp\server.cws和client.cws。下面是通过CVI的起始页面,搜索原厂例程的步骤。
先后运行server.cws和client.cws,当用户往文本框输入一条字符串之后回车,会发送到另一侧并显示出来:
Server.c解析
注意到,开篇#include <tcpsupp.h>,这是CVI专属TCP通信支撑库(RegisterTCPServer/ServerTCPRead等核心API)。
函数RegisterTCPServer (portNum, ServerTCPCB, 0) 是注册 TCP 服务器的核心API。其中,portNum是端口号比如10000,ServerTCPCB是回调函数。该函数的作用是让程序在指定端口监听客户端连接,并绑定 "TCP事件回调函数ServerTCPCB"------ 即后续所有 TCP事件(客户端连接、数据收发、断开)都通过该回调函数来响应。
函数ServerTCPWrite (g_hconversation, transmitBuf, strlen(transmitBuf), 1000) 是向已连接的TCP客户端发送数据的核心API,专门用于服务器端向客户端写入数据流。
函数dataSize = ServerTCPRead (g_hconversation, receiveBuf, dataSize, 1000) 是服务器端读取客户端发送数据的核心 API,负责从已连接的TCP客户端接收数据流。该函数一般是放在回调函数ServerTCPCB中被TCP_DATAREADY事件触发所调用。
UnregisterTCPServer (portNum) 是注销 TCP 服务器的核心API,是 RegisterTCPServer 的 "反向操作"------ 用于停止服务器对指定端口的监听、释放底层套接字资源、注销 TCP 事件回调。
Client.c解析
函数ConnectToTCPServer (&g_hconversation, portNum, tempBuf, ClientTCPCB, NULL, 5000) 是 TCP 客户端主动连接服务器的核心API,用于客户端程序向指定 IP+port的TCP服务器发起连接请求,并绑定客户端的TCP事件回调函数ClientTCPCB。
函数dataSize = ClientTCPRead (g_hconversation, receiveBuf, dataSize, 1000) 是 TCP客户端读取服务器发送数据的核心API,负责从已建立的 TCP 连接中接收服务器下发的流数据。该函数一般是放在回调函数ClientTCPCB中被TCP_DATAREADY事件触发所调用。
函数ClientTCPWrite (g_hconversation, transmitBuf, strlen(transmitBuf), 1000) < 0 是TCP客户端向服务器发送数据的核心API,负责将客户端缓冲区的数据通过已建立的TCP连接发送给服务器。
三、 学习TCP/IP例程 MultiClientServer.cws
CVI2010的MultiClientServer.cws例程是一个典型的多客户端TCP服务器演示程序,主要功能是展示如何实现一个服务器同时与多个客户端建立TCP连接并进行通信。其中,服务器端(Server):作为TCP服务器,启动后监听指定端口(如默认的5000),支持同时接收多个客户端的连接请求,并为每个客户端创建独立的通信线程。客户端(Client):可同时启动多个实例,每个客户端通过TCP协议连接到服务器,实现与服务器的双向数据交互。
该例程特点如下:
多客户端并发连接:服务器通过线程池(Cmt函数库)管理多个客户端连接,避免单个客户端阻塞其他连接,支持同时处理多个客户端的请求。
双向数据通信:客户端可向服务器发送数据(如文本、数值等);
服务器接收后可实时响应(如返回确认信息、处理后的数据),并支持向指定客户端或所有客户端广播消息。
连接状态管理:服务器实时显示已连接的客户端信息(如客户端IP、端口、连接时间),支持主动断开某个客户端连接;客户端也能显示与服务器的连接状态。
异常处理:包含连接超时、客户端异常断开、网络错误等场景的处理逻辑,确保服务器稳定运行。
总的来说,该例程的核心价值是展示CVI中TCP多连接处理和线程并发编程的最佳实践,代码中包含了服务器监听、客户端连接、多线程数据收发、连接管理等关键逻辑,适合作为开发多客户端通信系统的参考模板。
该项目在我笔记本上的完整路径是C:\Users\Public\Documents\National Instruments\CVI2010\samples\tcp\MultiClientServer.cws。下面是通过CVI的起始页面,搜索原厂例程的步骤。
刚才试通了一主多从的TCP通信的例程,没有参考文档就纯粹靠瞎蒙了。这里需要开三个CVI,分别打开MultiClientServer.cws、和两个client.prj。
老规矩,先运行server即MultiClientServer.cws。初始界面这样的,很多控件都是失活的,等待client上线:
然后在默认的debug模式下运行第一个client.prj,显然server这边侦听到有一个client上线了,客户端名称列表框就多了一行记录,同时还会弹出一个窗口Message from Client出来:
然后手动修改成release模式运行第二个client.prj,可见server这边就一共侦听到两个client上线,客户端名称列表框就有了两行记录。同时再弹出一个窗口Message from Client。这里手动把两个client界面和两个Message from Client弹窗都摆好位置:
然后用户就可以尝试双边通信了:
所以我上午的设想的,一个Server.exe配合4个ATE.exe去控制一台4通道OpticaslSuit,应是完全可行的了?