具有多个从属ID的modbus tcp客户端服务器

问题描述 投票:0回答:1

我正在开发 win ce 6 modbus tcp 客户端服务器,应用程序是为客户端服务器通信而开发的,并且工作正常。现在 req 是我的从设备应该响应主设备/客户端轮询的差异从设备地址。我可以只更改从站 ID 并建立连接吗?或者我需要关闭以前的连接并再次建立新的连接

下面是代码,对于一个节点来说工作正常,如果我用其他节点 ID 进行轮询,那么它会给出异常。是什么改变了它要求同时与其他节点通信。我的设备应该能够与 modbus tcp 上的 32 个 diff 节点进行通信。我是否应该为每个节点创建单独的线程,但它们如何在同一端口上进行通信?在与其他节点建立连接之前我应该关闭前一个节点吗?

startupServer(int slaveAddr,  const TCHAR * const hostName)
{

   int result;
   int tcpOption;
   struct sockaddr_in hostAddress;

   if (isStarted())
      return (FTALK_ILLEGAL_STATE_ERROR);

   // Note: For TCP we allow 0 as slave address, -1 means ignore slave adr
   if ((slaveAddr < -1) || (slaveAddr > 255))
      return (FTALK_ILLEGAL_ARGUMENT_ERROR);
   this->slaveAddr = slaveAddr;

   //
   // Special treatment for the Win32 platform, needs to load WinSock DLL
   //
#ifdef _WINSOCKAPI_
   WSADATA wsaData;

   result = WSAStartup(0x0101, &wsaData);
   if (result != 0)
      return (FTALK_SOCKET_LIB_ERROR);
#endif

   //
   // Open socket
   //
   listenSocket = socket(PF_INET, SOCK_STREAM, 0);
   if (listenSocket == INVALID_SOCKET)
   {
      shutdownServer();
      return (FTALK_OPEN_ERR);
   }

   //
   // Configure listen socket options (we ignore errors here)
   //
#ifdef SO_REUSEADDR
   tcpOption = 1; // Enable option
   setsockopt(listenSocket, SOL_SOCKET, SO_REUSEADDR,
              (char *) &tcpOption, sizeof (tcpOption));
#endif

   //
   // Binding the listen socket to the port
   //
   hostAddress.sin_family = AF_INET;
   if ((hostName == NULL) || (hostName[0] == '\0'))
      hostAddress.sin_addr.s_addr = htonl(INADDR_ANY);
   else
   {
      hostAddress.sin_addr.s_addr = inet_addr((char *) hostName);
#if !defined(__VXWORKS__) // We don't support host name resolving with VxWorks
      if (hostAddress.sin_addr.s_addr == INADDR_NONE)
      {
         struct hostent *hostInfo;

         hostInfo = gethostbyname((char *) hostName);

         if (hostInfo == NULL)
            return (FTALK_TCPIP_CONNECT_ERR);
         hostAddress.sin_addr = *(struct in_addr *) hostInfo->h_addr;
      }
#endif
   }
   hostAddress.sin_port = htons(portNo);
   result = bind(listenSocket, (struct sockaddr *) &hostAddress,
                 sizeof (hostAddress));
   if (result == SOCKET_ERROR)
   {
      shutdownServer();
      switch (socketErrno)
      {
#ifdef _WINSOCKAPI_
         case WSAEACCES:
         return (FTALK_PORT_NO_ACCESS);
         case WSAEADDRINUSE:
         return (FTALK_PORT_ALREADY_BOUND);
         case WSAEADDRNOTAVAIL:
         default:
         return (FTALK_PORT_NOT_AVAIL);
#else
         case ENOTCONN: // Linux 7.2 reports this error no if no root privilege
         case EACCES:
         return (FTALK_PORT_NO_ACCESS);
         case EADDRINUSE:
         return (FTALK_PORT_ALREADY_BOUND);
         case EADDRNOTAVAIL:
         default:
         return (FTALK_PORT_NOT_AVAIL);
#endif
      }
   }

   //
   // Start listening to incoming connections
   //
   result = listen(listenSocket,
               ((MAX_CONNECTIONS < SOMAXCONN) ? MAX_CONNECTIONS : SOMAXCONN));
   if (result == SOCKET_ERROR)
   {
      shutdownServer();
      return (FTALK_LISTEN_FAILED);
   }
   return (FTALK_SUCCESS);
}

serverLoop()
{
   int iReturnCode = (FTALK_SUCCESS);
   int result;
   int sockIdx;
   int recvResult;
   int sendResult;
   fd_set fdSet;
   timeval timeVal;
   SOCKET maxFileDes;
   int replyCnt;
   int tcpOption;

   if (!isStarted())
      return (FTALK_ILLEGAL_STATE_ERROR);

   //
   // Prepare file descriptor set for select call
   //
   FD_ZERO (&fdSet);
#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable: 4127)
#endif
   FD_SET (listenSocket, &fdSet);
#ifdef _MSC_VER
#  pragma warning(pop)
#endif
   maxFileDes = listenSocket;
   for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
   {
      if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable: 4127)
#endif
         FD_SET (connectionSocketArr[sockIdx], &fdSet);
#ifdef _MSC_VER
#  pragma warning(pop)
#endif
      if (connectionSocketArr[sockIdx] > maxFileDes)
         maxFileDes = connectionSocketArr[sockIdx];
   }

   //
   // Block until accept request or received data or time-out
   //
   timeVal.tv_sec = (long) timeOut / 1000L;
   timeVal.tv_usec = ((long) timeOut % 1000L) * 1000L;
   if (timeOut == 0)
      result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, NULL);
   else
      result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, &timeVal);
   if (result == SOCKET_ERROR)
      return (FTALK_FILEDES_EXCEEDED);

   //
   // Check for time-out
   //
   if (result == 0)
   {
      TRACELOG1("Slave poll time-out!\n");
      dataTablePtr->timeOutHandler();

      iReturnCode = (FTALK_REPLY_TIMEOUT_ERROR);
   }

   //
   // Connection accept request
   //
   if (FD_ISSET (listenSocket, &fdSet))
   {
      // Search a free socket
      for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
      {
         if (connectionSocketArr[sockIdx] == INVALID_SOCKET)
         {
            struct sockaddr_in peerAddr;
            SOCK_LEN_TYPE peerAddrLen = sizeof(peerAddr);

            // Yes, socket is free, try to accept a connection on it
            connectionSocketArr[sockIdx] = accept(listenSocket,
                                                  (struct sockaddr *) &peerAddr,
                                                  &peerAddrLen);
            if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
            {
               //
               // Check id connection shall be accepted
               //
               if (!dataTablePtr->validateMasterIpAddr(inet_ntoa(peerAddr.sin_addr)))
               {
                  shutdown(connectionSocketArr[sockIdx], SD_BOTH);
                  closesocket(connectionSocketArr[sockIdx]);
                  connectionSocketArr[sockIdx] = INVALID_SOCKET;
                  TRACELOG2("Connection rejected on slot %d\n", sockIdx);
               }

               //
               // Set socket options (we ignore errors here, not critical)
               //
#ifdef TCP_NODELAY
               tcpOption = 1; // Enable option
               setsockopt(connectionSocketArr[sockIdx],
                          IPPROTO_TCP, TCP_NODELAY,
                          (char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_SNDBUF
               tcpOption = MAX_MSG_SIZE;
               setsockopt(connectionSocketArr[sockIdx],
                          SOL_SOCKET, SO_SNDBUF,
                          (char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_RCVBUF
               tcpOption = MAX_MSG_SIZE;
               setsockopt(connectionSocketArr[sockIdx],
                          SOL_SOCKET, SO_RCVBUF,
                          (char *) &tcpOption, sizeof (tcpOption));
#endif
#ifdef SO_LINGER
               tcpOption = 0; // Disable option = discard unsent data when closing
               setsockopt(connectionSocketArr[sockIdx],
                          SOL_SOCKET, SO_LINGER,
                          (char *) &tcpOption, sizeof (tcpOption));
#endif
               TRACELOG2("Connection accepted on slot %d\n", sockIdx);
            }
            break; // Leave for loop
         }
      }
   }

   //
   // Data received on socket
   //

   for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
   {
      if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
      {
         if (FD_ISSET (connectionSocketArr[sockIdx], &fdSet))
         {
            recvResult = recv (connectionSocketArr[sockIdx],
                               (char *) bufferArr, sizeof (bufferArr), 0);
            sendResult = 0;
            replyCnt = 0;

            //
            // Process client message
            //
            if (recvResult >= PREFIX_LEN) // Process only minimum message sizes
            {
               short dataLen;

               dataLen = (short) ((bufferArr[4] << 8) | (bufferArr[5] & 0xFF));
               // Validate length before processing message
               if ((dataLen + PREFIX_LEN) == recvResult)
               {
                  replyCnt = processMessage(&bufferArr[PREFIX_LEN],
                                            recvResult - PREFIX_LEN);

                  // The first two bytes (msg id) are returned untouched
                  bufferArr[2] = 0; // protocol identifier
                  bufferArr[3] = 0; // protocol identifier
                  bufferArr[4] = (char) ((replyCnt) >> 8);
                  bufferArr[5] = (char) ((replyCnt) & 0xFF);
                  sendResult = send(connectionSocketArr[sockIdx],
                                    (char *) bufferArr,
                                    replyCnt + PREFIX_LEN, 0);
               }
            }
            //
            // Check for disconnection and errors
            //
            if ((recvResult < PREFIX_LEN) ||
                (sendResult != replyCnt + PREFIX_LEN))
            {
               //
               // Free socket
               //
               shutdown(connectionSocketArr[sockIdx], SD_BOTH);
               closesocket(connectionSocketArr[sockIdx]);
               connectionSocketArr[sockIdx] = INVALID_SOCKET;
               if (recvResult == 0)
                  TRACELOG2("Disconnected slot %d nicely by other peer.\n",
                            sockIdx);
               else
                  TRACELOG2("Forced disconnection on slot %d!\n", sockIdx);
            }
         }
      }
   }
   return iReturnCode;
}

下面的代码可以解决我的问题吗?

int ModbusTCPSlave::serverLoop()
{
   int iReturnCode = (FTALK_SUCCESS);
   int result;
   int sockIdx;
   int recvResult;
   int sendResult;
   fd_set fdSet;
   timeval timeVal;
   SOCKET maxFileDes;
   int replyCnt;
   int tcpOption;

   //if (!isStarted())
   //   return (FTALK_ILLEGAL_STATE_ERROR);

   //
   // Prepare file descriptor set for select call
   //
//   FD_ZERO (&fdSet);
//#ifdef _MSC_VER
//#  pragma warning(push)
//#  pragma warning(disable: 4127)
//#endif
//   FD_SET (listenSocket, &fdSet);
//#ifdef _MSC_VER
//#  pragma warning(pop)
//#endif
//   maxFileDes = listenSocket;
//   for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
//  {
//      if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
//#ifdef _MSC_VER
//#  pragma warning(push)
//#  pragma warning(disable: 4127)
//#endif
//         FD_SET (connectionSocketArr[sockIdx], &fdSet);
//#ifdef _MSC_VER
//#  pragma warning(pop)
//#endif
//      if (connectionSocketArr[sockIdx] > maxFileDes)
//         maxFileDes = connectionSocketArr[sockIdx];
//   }

   //
   // Block until accept request or received data or time-out
   //
   timeVal.tv_sec = (long) timeOut / 1000L;
   timeVal.tv_usec = ((long) timeOut % 1000L) * 1000L;
   if (timeOut == 0)
      result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, NULL);
   else
      result = select((int) maxFileDes + 1, &fdSet, NULL, NULL, &timeVal);
//   if (result == SOCKET_ERROR)
//      return (FTALK_FILEDES_EXCEEDED);

   //
   // Check for time-out
   //
//   if (result == 0)
//   {
//      TRACELOG1("Slave poll time-out!\n");
//      dataTablePtr->timeOutHandler();
//
//    iReturnCode = (FTALK_REPLY_TIMEOUT_ERROR);
//   }

   //
   // Connection accept request
   //
 //  if (FD_ISSET (listenSocket, &fdSet))
   {
      // Search a free socket
 //     for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
      {
  //       if (connectionSocketArr[sockIdx] == INVALID_SOCKET)
         {
            struct sockaddr_in peerAddr;
            SOCK_LEN_TYPE peerAddrLen = sizeof(peerAddr);

            // Yes, socket is free, try to accept a connection on it
            connectionSocketArr[sockIdx] = accept(listenSocket,
                                                  (struct sockaddr *) &peerAddr,
                                                  &peerAddrLen);
//           if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
//           {
//               //
//               // Check id connection shall be accepted
//               //
//               if (!dataTablePtr->validateMasterIpAddr(inet_ntoa(peerAddr.sin_addr)))
//               {
//                  shutdown(connectionSocketArr[sockIdx], SD_BOTH);
//                  closesocket(connectionSocketArr[sockIdx]);
//                  connectionSocketArr[sockIdx] = INVALID_SOCKET;
//                  TRACELOG2("Connection rejected on slot %d\n", sockIdx);
//               }

               //
               // Set socket options (we ignore errors here, not critical)
               //
//#ifdef TCP_NODELAY
//               tcpOption = 1; // Enable option
//               setsockopt(connectionSocketArr[sockIdx],
//                          IPPROTO_TCP, TCP_NODELAY,
//                          (char *) &tcpOption, sizeof (tcpOption));
//#endif
//#ifdef SO_SNDBUF
//               tcpOption = MAX_MSG_SIZE;
//               setsockopt(connectionSocketArr[sockIdx],
//                          SOL_SOCKET, SO_SNDBUF,
//                          (char *) &tcpOption, sizeof (tcpOption));
//#endif
//#ifdef SO_RCVBUF
//               tcpOption = MAX_MSG_SIZE;
//               setsockopt(connectionSocketArr[sockIdx],
//                          SOL_SOCKET, SO_RCVBUF,
//                          (char *) &tcpOption, sizeof (tcpOption));
//#endif
//#ifdef SO_LINGER
//               tcpOption = 0; // Disable option = discard unsent data when closing
//               setsockopt(connectionSocketArr[sockIdx],
//                          SOL_SOCKET, SO_LINGER,
//                          (char *) &tcpOption, sizeof (tcpOption));
//#endif
//               TRACELOG2("Connection accepted on slot %d\n", sockIdx);
//            }
//           break; // Leave for loop
//         }
//      }
//   }

   //
   // Data received on socket
   //

//   for (sockIdx = 0; sockIdx < MAX_CONNECTIONS; sockIdx++)
//   {
//      if (connectionSocketArr[sockIdx] != INVALID_SOCKET)
//    {
//         if (FD_ISSET (connectionSocketArr[sockIdx], &fdSet))
//         {
            recvResult = recv (connectionSocketArr[sockIdx],
                               (char *) bufferArr, sizeof (bufferArr), 0);
            sendResult = 0;
            replyCnt = 0;

            //
            // Process client message
            //
            if (recvResult >= PREFIX_LEN) // Process only minimum message sizes
            {
               short dataLen;

               dataLen = (short) ((bufferArr[4] << 8) | (bufferArr[5] & 0xFF));
               // Validate length before processing message
               if ((dataLen + PREFIX_LEN) == recvResult)
               {
                  replyCnt = processMessage(&bufferArr[PREFIX_LEN],
                                            recvResult - PREFIX_LEN);

                  // The first two bytes (msg id) are returned untouched
                  bufferArr[2] = 0; // protocol identifier
                  bufferArr[3] = 0; // protocol identifier
                  bufferArr[4] = (char) ((replyCnt) >> 8);
                  bufferArr[5] = (char) ((replyCnt) & 0xFF);
                  sendResult = send(connectionSocketArr[sockIdx],
                                    (char *) bufferArr,
                                    replyCnt + PREFIX_LEN, 0);
               }
            }
            //
            // Check for disconnection and errors
            //
            if ((recvResult < PREFIX_LEN) ||
                (sendResult != replyCnt + PREFIX_LEN))
            {
               //
               // Free socket
               //
               shutdown(connectionSocketArr[sockIdx], SD_BOTH);
               closesocket(connectionSocketArr[sockIdx]);
               connectionSocketArr[sockIdx] = INVALID_SOCKET;
               if (recvResult == 0)
                  TRACELOG2("Disconnected slot %d nicely by other peer.\n",
                            sockIdx);
               else
                  TRACELOG2("Forced disconnection on slot %d!\n", sockIdx);
            }
//         }
//    }
//   }
   return iReturnCode;
}

谢谢瓦尔特,你是对的,我明白了。我在代码中还有一个查询,有两个数组 regdata[30][65535];和 bitarray[30][2000] 从文件读取数据后,我可以决定数组的第一个维度,即 [30]..如果文件中的数据用于两个从机 ID,那么我需要 regdata[2][65535] 和 bitarray[ 2][2000]..我如何在运行时管理这个分配?我尝试使用像

struct{                                                                      regdata[65535];                    bitarray[2000];                                                    }regstack;                                                            after reading file I
这样的向量尝试push_back()regstack,但它给出了堆错误..我如何在运行时调整这个数组的大小?

c++ tcp windows-ce modbus
1个回答
1
投票

不能让多个套接字侦听同一端口。但是,如果在 processMessage 内部验证了地址,您不能简单地更改该函数以接受不同从站 ID 的请求。

© www.soinside.com 2019 - 2024. All rights reserved.