ROS-I simple_message 源码分析:SimpleSocket

分析了SimpleMessage和更上一层的数据结构,再来看看底层的通信是怎么实现的。前面已分析过SmplMsgConnection这个基类,它是对SimpleMessage底层通信的一层抽象。而SimpleSocket类是SmplMsgConnection的派生类,它定义了当采用socket做通信时的一些socket方法接口,后面编写的TCP server,TCP client,UDP server,UDP client均是基于SimpleSocket来实现的。

namespace industrial
{
namespace simple_socket
{

namespace StandardSocketPorts
{
enum StandardSocketPort
{
  MOTION = 11000, SYSTEM = 11001, STATE = 11002, IO = 11003
};
}
typedef StandardSocketPorts::StandardSocketPort StandardSocketPort;

class SimpleSocket : public industrial::smpl_msg_connection::SmplMsgConnection
{
public:

  SimpleSocket()
  {
    this->setSockHandle(this->SOCKET_FAIL);
    memset(&this->sockaddr_, 0, sizeof(this->sockaddr_));
    this->setConnected(false);
  }

  virtual ~SimpleSocket(){}

  bool isConnected()
  {
    return connected_;
  }
  
  virtual void setDisconnected()
  {
    setConnected(false);
  }
  
  bool isReadyReceive(int timeout)
  {
    bool r, e;
    rawPoll(timeout, r, e);
    return r;
  }

protected:
  //接收和发送数据的socket句柄
  int sock_handle_;
  //远程socket地址和端口号
  sockaddr_in sockaddr_;
  //socket连接状态
  bool connected_;
  //socket错误返回值
  static const int SOCKET_FAIL = -1;
  //接收数据最大字节数
  static const int MAX_BUFFER_SIZE = 1024;
  //查询socket是否ready的超时参数,1000ms
  static const int SOCKET_POLL_TO = 1000;
  //用于接收数据的内部缓冲区
  char buffer_[MAX_BUFFER_SIZE + 1];
 
  //获取socket句柄
  int  getSockHandle() const
  {
    return sock_handle_;
  }
  
  //设置socket句柄
  void setSockHandle(int sock_handle_)
  {
    this->sock_handle_ = sock_handle_;
  }

 //错误报告
  __attribute__((deprecated(
                   "Please use: logSocketError(const char* msg, const int rc, const int error_no)")))
  void logSocketError(const char* msg, int rc)
  {
    logSocketError(msg, rc, errno);
  }

  void logSocketError(const char* msg, const int rc, const int error_no)
  {
    LOG_ERROR("%s, rc: %d. Error: '%s' (errno: %d)", msg, rc, strerror(error_no), error_no);
  }
  
  // 发送和接收方法,需派生类重写 rawSendBytes 和 rawReceiveBytes
  // Virtual
  bool sendBytes(industrial::byte_array::ByteArray & buffer);
  bool receiveBytes(industrial::byte_array::ByteArray & buffer,
      industrial::shared_types::shared_int num_bytes);
  // Virtual
  virtual int rawSendBytes(char *buffer,
      industrial::shared_types::shared_int num_bytes)=0;
  virtual int rawReceiveBytes(char *buffer,
      industrial::shared_types::shared_int num_bytes)=0;
 
  //查询socket上是否有数据
  virtual bool rawPoll(int timeout, bool & ready, bool & error)=0;

  virtual void setConnected(bool connected)
  {
    this->connected_ = connected;
  }

};

} //simple_socket
} //industrial

以上代码定义了socket通信的接口,数据成员包括socket句柄(也就是文件描述符),地址和端口号,连接状态,内部数据缓冲区等。可以看出rawSendBytes、rawReceiveBytes,rawPoll并没有做实现,这3个方法是放到其派生类TcpSocket中实现了,此处只是定义了接口的功能。

sendBytes
bool SimpleSocket::sendBytes(ByteArray & buffer)
    {
      int rc = this->SOCKET_FAIL;
      bool rtn = false;

      if (this->isConnected())
      {
        // Nothing restricts the ByteArray from being larger than the what the socket
        // can handle.
        if (this->MAX_BUFFER_SIZE > (int)buffer.getBufferSize())
        {

          // copy to local array, since ByteArray no longer supports
          // direct pointer-access to data values
          std::vector localBuffer;
          buffer.copyTo(localBuffer);
          rc = rawSendBytes(&localBuffer[0], localBuffer.size());
          if (this->SOCKET_FAIL != rc)
          {
            rtn = true;
          }
          else
          {
            rtn = false;
            logSocketError("Socket sendBytes failed", rc, errno);
          }

        }
        else
        {
          LOG_ERROR("Buffer size: %u, is greater than max socket size: %u", buffer.getBufferSize(), this->MAX_BUFFER_SIZE);
          rtn = false;
        }

      }
      else
      {
        rtn = false;
        LOG_WARN("Not connected, bytes not sent");
      }

      if (!rtn)
      {
        this->setConnected(false);
      }

      return rtn;

    }

sendBytes实现的是SmplMsgConnection中的虚方法,功能是发送ByteArray类型的数据。首先确保socket能发送的字节数比待发送数据的数据字节数要大,然后拷贝数据至临时变量,再调用rawSendBytes将数据发送出去,并返回发送结果至上层。

receiveBytes
bool SimpleSocket::receiveBytes(ByteArray & buffer, shared_int num_bytes)
    {
      int rc = this->SOCKET_FAIL;
      bool rtn = false;
      shared_int remainBytes = num_bytes;
      bool ready, error;

      memset(&this->buffer_, 0, sizeof(this->buffer_));

      if (this->MAX_BUFFER_SIZE > buffer.getMaxBufferSize())
      {
        LOG_WARN("Socket buffer max size: %u, is larger than byte array buffer: %u",
            this->MAX_BUFFER_SIZE, buffer.getMaxBufferSize());
      }
      if (this->isConnected())
      {
        buffer.init();
        while (remainBytes > 0)
        {
          // Polling the socket results in an "interruptable" socket read.  This
          // allows Control-C to break out of a socket read.  Without polling,
          // a sig-term is required to kill a program in a socket read function.
          if (this->rawPoll(this->SOCKET_POLL_TO, ready, error))
          {
            if(ready)
            {
              rc = rawReceiveBytes(this->buffer_, remainBytes);
              if (this->SOCKET_FAIL == rc)
              {
                this->logSocketError("Socket received failed", rc, errno);
                remainBytes = 0;
                rtn = false;
                break;
              }
              else if (0 == rc)
              {
                LOG_WARN("Recieved zero bytes: %u", rc);
                remainBytes = 0;
                rtn = false;
                break;
              }
              else
              {
                remainBytes = remainBytes - rc;
                LOG_COMM("Byte array receive, bytes read: %u, bytes reqd: %u, bytes left: %u",
                    rc, num_bytes, remainBytes);
                buffer.load(&this->buffer_, rc);
                rtn = true;
              }
            }
            else if(error)
            {
              LOG_ERROR("Socket poll returned an error");
              rtn = false;
              break;
            }
            else
            {
              LOG_ERROR("Uknown error from socket poll");
              rtn = false;
              break;
            }
          }
          else
          {
            LOG_COMM("Socket poll timeout, trying again");
          }
        }
      }
      else
      {
        LOG_WARN("Not connected, bytes not sent");
        rtn = false;
      }

      if (!rtn)
      {
        this->setConnected(false);
      }
      return rtn;
    }

receiveBytes实现的是SmplMsgConnection中的虚方法,功能是从socket上接收数据至ByteArray类型的变量。首先会检查socket能接收的最大字节数是否大于ByteArray配置的最大字节数,如果大于,则给出警告,因为可能会丢失数据。接着会调用rawPoll查询socket上的数据是否可读,最长阻塞时间为1000ms。如果可读,则调用rawReceiveBytes接收数据,接收到有效数据后调用buffer.load将buffer_内的数据加载到传进来的buffer中,并返回true给上层。

下一篇将介绍TcpSocket这个类,看一下上面的虚方法rawSendBytes,rawReceiveBytes,rawPoll是如何实现的。

你可能感兴趣的:(ROS-I simple_message 源码分析:SimpleSocket)