本文记录了笔者参与的一个工业机器人双机协作项目(细节不表),项目中考虑数据传输的稳定性、安全性等,使用TCP/IP通讯,上位机软件使用C++/Qt开发(基于WINDOWS环境下),传输指令数据控制下位机工业机器人,同时反馈下位机信号用于整体装配节拍规划。
本文收录于网络编程专栏,因此本文仅记录网络通讯部分代码并解析,本文也是对本专栏所学知识的总结和验证,由于该项目基于WINDOWS环境,与Linux环境下Socket编程会有所不同。
话不多说,先贴上代码。
#pragma once
#include
#include
#include
#include
#include
#include
#pragma comment (lib, "ws2_32.lib") //加载 ws2_32.dll
const char ip_abb[] = "192.168.125.6"; //ABB IP地址
const char ip_ur[] = "192.168.125.130";//UR IP地址
const char ip_ca[] = "192.168.125.200";
class Server : public QObject {
Q_OBJECT
private:
WSADATA wsaData;
SOCKET serverSock;
sockaddr_in sockAddr;
SOCKET clntSock;
SOCKET abb_clnt;
SOCKET ur_clnt;
SOCKET ca_clnt;
const char *address;
public:
bool statue; //0表示未连接,1表示已连接
bool ca_statue;
bool ur_statue;
bool abb_statue;
int N = 20;
int port;
Server(const char *add,int po)
{
// 初始化DLL
statue = 0;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
clntSock = NULL;
address = add;
port = po;
}
bool opentolisten()
{
int net = 0;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
sockAddr.sin_addr.s_addr = inet_addr(address); //具体的IP地址
sockAddr.sin_port = htons(port); //端口设置 htons为宏定义,主要的作用在于为了避免大小端的问题,
net = ::bind(serverSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
if (net == SOCKET_ERROR)
{
qDebug() << "Error #" << WSAGetLastError();
closesocket(serverSock);
return false;
}
//进入监听状态
net = listen(serverSock, 20);
if (net == SOCKET_ERROR)
{
qDebug() << "Error #" << WSAGetLastError();
closesocket(serverSock);
return false;
}
return true;
}
bool opentoconect()
{
int timeout = 1000000;
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
clntSock = accept(serverSock, (SOCKADDR*)&clntAddr, &nSize);
if (clntSock != -1)
{
statue = 1;
qDebug() << "服务器接收到一个客户端的连接" ;
//发送时限
//setsockopt(clntSock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int));
//接收时限
//setsockopt(clntSock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
//获取地址
sockaddr_in sin;
memcpy(&sin, &clntAddr, sizeof(sin));
const char*p = inet_ntoa(sin.sin_addr);
QString str = QString(QLatin1String(p));
qDebug() << str;
if (!strcmp(p, ip_ca))
{
qDebug() << "ca is connected";
ca_clnt = clntSock;
ca_statue = 1;
}
else if (!strcmp(p, ip_abb))
{
qDebug() << "abb is connected";
abb_clnt = clntSock;
abb_statue = 1;
}
else if (!strcmp(p, ip_ur))
{
qDebug() << "ur is connected";
ur_clnt = clntSock;
ur_statue = 1;
}
return true;
}
else
{
statue = 0;
return false;
}
}
void calver(char * inmsg)
{
char ver = 0;
for (int i = 1; i < N - 2; i++)
{
ver = ver + inmsg[i];
}
inmsg[N - 2] = ver;
inmsg[N - 1] = 0xCC;
}
bool sendmes(char *message, int nlen)
{
//计算验证位
calver(message);
int sendnum = 0;
if (statue)
{
sendnum = send(clntSock, message, nlen, 0);
return true;
}
else
{
return false;
}
}
bool readmes(char * message, int nlen)
{
int readnum = 0;
if (statue)
{
readnum = recv(clntSock, message, nlen, 0);
if (readnum > 0)
return true;
else
return false;
}
else
{
return false;
}
}
bool closetcp()
{
if(clntSock!=NULL)
closesocket(clntSock);
return true;
}
bool ca_sendmes(char *message, int nlen)
{
int sendnum = 0;
if (ca_statue)
{
sendnum = send(ca_clnt, message, nlen, 0);
return true;
}
else
{
return false;
}
}
bool abb_sendmes(char *message, int nlen)
{
int sendnum = 0;
if (abb_statue)
{
sendnum = send(abb_clnt, message, nlen, 0);
return true;
}
else
{
return false;
}
}
bool ur_sendmes(char *message, int nlen)
{
int sendnum = 0;
if (ur_statue)
{
sendnum = send(ur_clnt, message, nlen, 0);
return true;
}
else
{
return false;
}
}
bool ca_readmes(char * message, int nlen)
{
int readnum = 0;
if (ca_statue)
{
readnum = recv(ca_clnt, message, nlen, 0);
if (readnum > 0)
return true;
else
return false;
}
else
{
return false;
}
}
bool ur_readmes(char * message, int nlen)
{
int readnum = 0;
if (ur_statue)
{
readnum = recv(ur_clnt, message, nlen, 0);
if (readnum > 0)
return true;
else
return false;
}
else
{
return false;
}
}
bool abb_readmes(char * message, int nlen)
{
int readnum = 0;
if (abb_statue)
{
readnum = recv(abb_clnt, message, nlen, 0);
if (readnum > 0)
return true;
else
return false;
}
else
{
return false;
}
}
};
主要实现通讯功能的是opentolisten()和opentoconect()两个成员函数,其作用分别是创建服务器并使服务器进入监听状态以及等待客户端连接。
bool opentolisten()
{
int net = 0;
memset(&sockAddr, 0, sizeof(sockAddr)); //每个字节都用0填充
sockAddr.sin_family = PF_INET; //使用IPv4地址
sockAddr.sin_addr.s_addr = inet_addr(address); //具体的IP地址
sockAddr.sin_port = htons(port); //端口设置 htons为宏定义,主要的作用在于为了避免大小端的问题,
net = ::bind(serverSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));
if (net == SOCKET_ERROR)
{
qDebug() << "Error #" << WSAGetLastError();
closesocket(serverSock);
return false;
}
//进入监听状态
net = listen(serverSock, 20);
if (net == SOCKET_ERROR)
{
qDebug() << "Error #" << WSAGetLastError();
closesocket(serverSock);
return false;
}
return true;
}
bool opentoconect()
{
int timeout = 1000000;
//接收客户端请求
SOCKADDR clntAddr;
int nSize = sizeof(SOCKADDR);
clntSock = accept(serverSock, (SOCKADDR*)&clntAddr, &nSize);
if (clntSock != -1)
{
statue = 1;
qDebug() << "服务器接收到一个客户端的连接" ;
//发送时限
//setsockopt(clntSock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(int));
//接收时限
//setsockopt(clntSock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(int));
//获取地址
sockaddr_in sin;
memcpy(&sin, &clntAddr, sizeof(sin));
const char*p = inet_ntoa(sin.sin_addr);
QString str = QString(QLatin1String(p));
qDebug() << str;
if (!strcmp(p, ip_ca))
{
qDebug() << "ca is connected";
ca_clnt = clntSock;
ca_statue = 1;
}
else if (!strcmp(p, ip_abb))
{
qDebug() << "abb is connected";
abb_clnt = clntSock;
abb_statue = 1;
}
else if (!strcmp(p, ip_ur))
{
qDebug() << "ur is connected";
ur_clnt = clntSock;
ur_statue = 1;
}
return true;
}
else
{
statue = 0;
return false;
}
}