TCP/IP开发实例(C++与LabVIEW的TCP通信 多线程)
一、本文目标:
将C++端作为Server,主要用于进行中心数据处理,而Labview作为用户端,主要进行指令的发送以及接受来自服务器的数据进行实时显示。这样做的好处有:
1.Labview对于流数据的梳理有着很多强大的封装好的API,可以进行调用,尤其是用于强度图(二维数组)的显示。
2.Labview是基于图形化变成,对于程序的控制灵活度没有使用代码编程的语言便捷,且C++有着强大的开源社区以及各种数据处理的头文件。
3.使用C++的多线程结构,进一步提高程序效率与灵活度。
二、写在前面:
本案例将使用C++的 WinSock2 进行TCP/IP,通信,且运行的编译环境为VSCode,在导入这个头文件后运行程序会报错,需要读者进行如下操作:打开task.json文件,将“-lws2_32”下如下图红色框所在位置(千万不要写错位置,也不要写在其他行,否则亲测也会报错)
三、C++代码(作为服务器端)
//code C++
#include <iostream>
#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <thread>
using namespace std;
#pragma comment(lib,"WS2_32.lib")
//定义一些全局变量,便于所有函数调用
char recvBuffer[200] = ""; //接受指令的缓存区,大小根据实际需要开辟,这里为200
char sendBuffer[12500] = "";//发送数据的缓存区,大小根据实际需要开辟,这里为12500
sockaddr_in serverAddr;
sockaddr_in clientAddr;
SOCKET serverSock;
int client;
/*############################################################################
功能:与上位机建立建立TCP连接
############################################################################*/
void buildTcpServer(){
//初始化Socket
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
serverSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
serverAddr.sin_port = htons(9999);
//先绑定
if (bind(serverSock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
{
printf("127.0.0.1 bind fail!\n");
return;
}
//设置服务器上的socket为监听状态
if (listen(serverSock, 5) < 0)
{
printf("127.0.0.1 listen fail!\n");
return;
}
}
/*############################################################################
功能:接受来自上位机的指令
############################################################################*/
void recvCommand()
{
while (true)
{
printf("Listening on port: %d\n", 9999);
//调用accept函数后,会进入阻塞状态
//accept返回一个套接字的文件描述符,这样服务器端便有两个套接字的文件描述符,
//serverSocket和client。
//serverSocket仍然继续在监听状态,client则负责接收和发送数据
//clientAddr是一个传出参数,accept返回时,传出客户端的地址和端口号
//addr_len是一个传入-传出参数,传入的是调用者提供的缓冲区的clientAddr的长度,以避免缓冲区溢出。
//accept阻塞执行,直到获取到有客户端连接
int clientAddrLen = sizeof(clientAddr);
client = accept(serverSock, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (client < 0)
{
perror("accept");
continue;
}
printf("\nrecv client data...n");
//inet_ntoa ip地址转换函数,将网络字节序IP转换为点分十进制IP
//表达式:char *inet_ntoa (struct in_addr);
printf("IP is %s\n", inet_ntoa(clientAddr.sin_addr));
printf("Port is %d\n", htons(clientAddr.sin_port));
while (1)
{
if (recv(client, recvBuffer, 200, 0) < 0)
{
perror("recv");
continue;
}
printf("the data we received is %s \n",recvBuffer);
//清空接受数据的数组
recvBuffer[200]={0};
memset(recvBuffer,'\0',sizeof(recvBuffer));
}
}
}
//向上位机发送数据,这里我们一次性发送从0~124一共125个数字
void sendData(){
while(1){
for(int i=0;i<125;i++) sendBuffer[i] = i%125;
send(client, sendBuffer, 125, 0);
}
}
int main()
{
buildTcpServer();
thread sendDataThread(sendData);
thread recvCommandThread(recvCommand);
sendDataThread.join();
recvCommandThread.join();
sendDataThread.detach();
recvCommandThread.detach();
return 0;
}
四、客户端(Labview)
图中有不明白的地方可以留言,要是读者感兴趣可以专门出一期关于Labview的一些操作。其中主要就是通过Channel实现数据的异步非阻塞传输。特别注意这里的IP地址和端口号一定要和C++C中设置的已知且没有被其他程序占用。