通过UDP协议进行数据采集,首先通过本地机器(电脑)发送采集指令,
之后每秒钟进行设备发送数据包的接受和处理。
设备: IP = 192.168.1.252, 端口号:1031
电脑: IP = 192.168.1.101,端口号:1032
使用网络调试助手,按照下图设置是可以正常采集到数据的。
BOOL CDeviceInterfaceMAXA02::StartMeasureUDP()
{
SOCKET m_Local;
SOCKADDR_IN m_LocalAddress; //本地地址
SOCKADDR_IN m_RemoteAddress; //远程地址
int m_LocalAddressLen = sizeof(SOCKADDR);
int m_RemoteAddressLen = sizeof(SOCKADDR);
// socket环境
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return false;
// socket对象
m_Local = socket(AF_INET, SOCK_DGRAM, 0);
if (m_Local == INVALID_SOCKET)
{
closesocket(m_Local);
m_Local = INVALID_SOCKET;
return false;
}
unsigned long on = 1; //此值为0,会将套接字设置为阻塞(默认的);为1设置为非阻塞
if (0 != ioctlsocket(m_Local, FIONBIO, &on))
{
AfxMessageBox("设置为非阻塞失败.");
}
// 绑定占用
const char* ip = "192.168.1.101";
short port = 1032;
m_LocalAddress.sin_family = AF_INET;
m_LocalAddress.sin_addr.S_un.S_addr = inet_addr(ip);
m_LocalAddress.sin_port = 1032;
auto ret = bind(m_Local, (sockaddr*)&m_LocalAddress, sizeof(m_LocalAddress));
if (ret == SOCKET_ERROR)
{
closesocket(m_Local);
m_Local = INVALID_SOCKET;
return false;
}
listen(m_Local, 10);
// 接收和发送
m_RemoteAddress.sin_family = AF_INET;
m_RemoteAddress.sin_port = 1031;
m_RemoteAddress.sin_addr.S_un.S_addr = inet_addr("192.168.1.252");
m_RemoteAddressLen = sizeof(m_RemoteAddress);
int bytes_sent;
bytes_sent = sendto(m_Local, (char*)xaCS, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaRU, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaMC, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaGS, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaST1, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaST2, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaHV, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
bytes_sent = sendto(m_Local, (char*)xaCB, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
std::vector<char> ucBuffer;
ucBuffer.resize(24600);
while (1)
{
int recvLen = recvfrom(m_Local, ucBuffer.data(), 24600, 0, (sockaddr*)&m_LocalAddress, &m_LocalAddressLen);
}
closesocket(m_Local);
WSACleanup();
return true;
}
目前的问题是recvfrom返回的数据长度为-1。
希望像“网络调试助手”一样,1:“打开”本地机器(电脑)的IP地址和端口,2:实现电脑发送指令和接受数据。
根据您提供的代码,问题可能出现在两个地方:接收数据的地址和端口号设置不正确,以及接收缓冲区的大小设置不足。
首先,关于接收地址和端口号的设置。您在发送数据时指定了远程地址和端口号(192.168.1.252和1031),但在接收数据时,却将本地地址和端口号(192.168.1.101和1032)作为参数传递给了recvfrom函数。这可能会导致recvfrom无法正确接收到数据包。因此,您需要将接收地址和端口号设置为设备的IP地址和端口号,即:
m_LocalAddress.sin_family = AF_INET;
m_LocalAddress.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
m_LocalAddress.sin_port = htons(1031);
// ...
int recvLen = recvfrom(m_Local, ucBuffer.data(), 24600, 0, (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
另外,由于接收数据的缓冲区大小为24600,这可能不足以容纳一些大型数据包。如果数据包的大小超过了缓冲区的大小,那么接收操作可能会失败。建议将接收缓冲区的大小设置得更大一些,以确保能够接收到所有的数据包。您可以根据实际情况调整缓冲区的大小。
最后,建议在代码中添加一些错误处理的代码,以便在出现问题时能够及时发现和解决问题。例如,可以在发送和接收操作之后检查返回值,如果出现错误,则输出错误信息并退出程序。
希望这些提示能够帮助你解决问题。
一种解决方案:
在绑定本地地址时,端口号应该使用变量port,而不是硬编码的1032。
修改代码如下:
m_LocalAddress.sin_port = htons(port);
在接收数据时,应该使用m_RemoteAddressLen作为recvfrom函数的最后一个参数,而不是m_LocalAddressLen。
修改代码如下:
int recvLen = recvfrom(m_Local, ucBuffer.data(), 24600, 0, (sockaddr*)&m_RemoteAddress, &m_RemoteAddressLen);
在发送数据时,没有对发送结果进行检查,可能会导致发送失败但程序继续执行的问题。
修改代码如下:
bytes_sent = sendto(m_Local, (char*)xaCS, 6, 0, (sockaddr*)&m_RemoteAddress, m_RemoteAddressLen);
if (bytes_sent == SOCKET_ERROR) {
AfxMessageBox("发送失败.");
break;
}
在程序正常结束前应该关闭socket和清理WSA。
修改代码如下:
closesocket(m_Local);
WSACleanup();
如果答案对您有所帮助,望采纳。
按照您的建议修改端口和接受地址后,可以获得6字节的数据,6字节数据的结构貌似合理,就是下面说的一个“子包”。While循环内接受,但每次只有6字节数据。
问:另外有一个问题,指令“xaCB”等是无符号字符串:
unsigned char xaCB[6]={0xfe,0x01,0x01,0x00,0x00,0xff};
强制转换成char*发送,这样发的指令有没有问题?
原数据:下位机每秒向上位机发送一帧数据,数据由 4096 个子包+2 包时
间信息组成,总共 40966+26=24588 个字节,每个子包的格式如下:FD+ChannelNum(2 字节)+ChannelCnt(2 字节)+FF 共 6 个字节
时间信息:2F + Input counts(4 字节)+FF (输入脉冲总数) 共 6 个字节3F + SlowCounts(4 字节)+FF (有效脉冲总数) 共 6 个字节
通过COM端口已实现全部数据的接受和处理,现在想实现UDP。使用“网络调试助手”是可以接受全部数据的。