ROS-Industrial 之 simple_message 實現分析八

目錄

simple_socket

命名空間simple_socket中枚舉了標準的通信端口信息,此類是對industrial::smpl_msg_connection::SmplMsgConnection類的繼承,而industrial::smpl_msg_connection::SmplMsgConnection類是一個虛基類,提供了socket通信的簡化接口。並且simple_socket是其它socket通信類的基礎,所以與simple_socket對應的文件中,也定義了相關的信息。由於不使用motoplus所以與motoplus相關的宏定義已刪除。

#ifndef SIMPLE_SOCKET_H
#define SIMPLE_SOCKET_H
//定義包含的頭文件,尤其是smpl_msg_connection.h,因爲其中含有實現通信的虛基類
#ifndef FLATHEADERS
#include "simple_message/log_wrapper.h"
#include "simple_message/shared_types.h"
#include "simple_message/smpl_msg_connection.h"
#else
#include "log_wrapper.h"
#include "shared_types.h"
#include "smpl_msg_connection.h"
#endif

#ifdef LINUXSOCKETS
//Linux中進行通信所需要包含的頭文件
#include "sys/socket.h"
#include "arpa/inet.h"
#include "string.h"
#include "unistd.h"
#include "netinet/tcp.h"
#include "errno.h"

#define SOCKET(domain, type, protocol) socket(domain, type, protocol)
#define BIND(sockfd, addr, addrlen) bind(sockfd, addr, addrlen)
#define SET_NO_DELAY(sockfd, val) setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val))
#define SET_REUSE_ADDR(sockfd, val) setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val))
#define LISTEN(sockfd, n) listen(sockfd, n)
#define ACCEPT(sockfd, addr, addrlen) accept(sockfd, addr, addrlen)
#define CONNECT(sockfd, dest_addr ,addrlen) connect(sockfd, dest_addr, addrlen)
#define SEND_TO(sockfd, buf, len, flags, dest_addr, addrlen) sendto(sockfd, buf, len, flags, dest_addr, addrlen)
#define SEND(sockfd, buf, len, flags) send(sockfd, buf, len, flags)
#define RECV_FROM(sockfd, buf, len, flags, src_addr, addrlen) recvfrom(sockfd, buf, len, flags, src_addr, addrlen)
#define RECV(sockfd, buf, len, flags) recv(sockfd, buf, len, flags)
#define SELECT(n, readfds, writefds, exceptfds, timeval) select(n, readfds, writefds, exceptfds, timeval)
#define CLOSE(fd) close(fd)
#ifndef HTONS // OSX defines HTONS
#define HTONS(num) htons(num)
#endif
#define INET_ADDR(str) inet_addr(str)
#define SOCKLEN_T socklen_t
#define GETHOSTBYNAME(str) gethostbyname(str)
#endif

namespace industrial
{
  namespace simple_socket
  {
    //枚舉標準socket通信端口(支持所有平臺),對於雙臂機器人而言,用同一控制器時要增加對應的端口
     namespace StandardSocketPorts
    {
      enum StandardSocketPort
      { MOTION = 11000, SYSTEM = 11001, STATE = 11002, IO = 11003 };
    }
    typedef StandardSocketPorts::StandardSocketPort StandardSocketPort;

    //定義socket功能,繼承自SmplMsgConnection虛基類
    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);
      }
      //測試連通可用性
      bool isConnected()
      { return connected_; }
      //內部設置要斷開連接的狀態。在UDP連接中,這是需要的,以便在超時時發出信號,並且需要使用握手協議重新建立連接。
      virtual void setDisconnected()
      { setConnected(false); }
      //如果socket已經準備好接收數據,則返回真值
      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;
    //套接字就緒輪詢超時(Ms)
    static const int SOCKET_POLL_TO = 1000;
    //內部數據接收定義的接收變量
    char buffer_[MAX_BUFFER_SIZE + 1];
    //獲取通信句柄
    int  getSockHandle() const
    { return sock_handle_; }
    //設置通信句柄
    void setSockHandle(int sock_handle_)
    { this->sock_handle_ = sock_handle_;  }
    //錯誤報告
    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);}

    // 發送接收功能
    // 繼承自SmplMsgConnection類的Virtual虛擬函數
    bool sendBytes(industrial::byte_array::ByteArray & buffer);
    bool receiveBytes(industrial::byte_array::ByteArray & buffer,
        industrial::shared_types::shared_int num_bytes);
    // 虛擬函數,所有繼承自此類的類都要進行函數重寫
    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;
    //輪詢測試
    virtual bool rawPoll(int timeout, bool & ready, bool & error)=0;
    //返回連接狀態
    virtual void setConnected(bool connected)
    {this->connected_ = connected;}
    };

} //simple_socket
} //industrial
#endif /* SIMPLE_SOCKET_H */

所以通過以上的代碼的理解,可以得知,在這個類的定義中相對於SmplMsgConnection類又增加了新的虛函數,用於在通信的實際建立過程中進一步對已經定義好的功能進行具體實現。
simple_socket
其中rawSendBytes()與rawReceiveBytes()函數是通信實現中的信息交互函數,要在後面的實現中進行重寫。

tcp_socket

此類的結構對上面類結構中的虛基函數進行了進一步的實現,所以從代碼結構上來說相對簡單,複雜的和基本的函數與功能都在基類中進行了實現,剩下的具體的執行功能要在實際實現的類中進行進一步的重寫。

#ifndef TCP_SOCKET_H
#define TCP_SOCKET_H
#ifndef FLATHEADERS
#include "simple_message/socket/simple_socket.h"
#include "simple_message/shared_types.h"
#else
#include "simple_socket.h"
#include "shared_types.h"
#endif
#ifdef LINUXSOCKETS
#include "sys/socket.h"
#include "netdb.h"
#include "arpa/inet.h"
#include "string.h"
#include "unistd.h"
#endif
#ifdef MOTOPLUS
#include "motoPlus.h"
#endif
namespace industrial
{
namespace tcp_socket
{

class TcpSocket : public industrial::simple_socket::SimpleSocket
{
public:
  TcpSocket();
  virtual ~TcpSocket();
private:
  // Virtual
  int rawSendBytes(char *buffer,
      industrial::shared_types::shared_int num_bytes);
  int rawReceiveBytes(char *buffer,
      industrial::shared_types::shared_int num_bytes);
  bool rawPoll(int timeout, bool & ready, bool & error);
};
} //tcp_socket
} //industrial
#endif /* TCP_SOCKET_H */

tcp_socket

tcp_client

實現tcp client的功能。

#ifndef TCP_CLIENT_H
#define TCP_CLIENT_H
#ifndef FLATHEADERS
#include "simple_message/socket/tcp_socket.h"
#else
#include "tcp_socket.h"
#endif
namespace industrial
{
namespace tcp_client
{
class TcpClient : public industrial::tcp_socket::TcpSocket
{
public:
  TcpClient();
  ~TcpClient();
  bool init(char *buff, int port_num);
  bool makeConnect();
};
} //tcp_client
} //industrial
#endif /* TCP_CLIENT_H */

tcp_client
用服務器的IP地址與對應的端口號進行初始化,然後由makeConnect()函數實現連接,並將連接之後的句柄信息存放在simple_socket對象的成員變量中,表示連接已經建立,後面可以進行數據通信。

tcp_server

#ifndef TCP_SERVER_H
#define TCP_SERVER_H
#ifndef FLATHEADERS
#include "simple_message/socket/tcp_socket.h"
#else
#include "tcp_socket.h"
#endif
namespace industrial
{
namespace tcp_server
{
class TcpServer : public industrial::tcp_socket::TcpSocket
{
public:
  TcpServer();
  ~TcpServer();
  //初始化TCP服務器,傳遞的參數爲端口參數,這個參數要同時匹配服務器與客戶端
  bool init(int port_num);
  // Overrides
  bool makeConnect();
protected:
  //服務器句柄,每次建立一個連接,都會生成一個用於接收與發送的新的句柄,並將其存儲到對應的變量中,由此可以在通信丟失的時候進行通信恢復
  int srvr_handle_;
  //獲取服務器句柄
  int getSrvrHandle() const
  { return srvr_handle_; }
  //設置服務器句柄
  void setSrvrHandle(int srvr_handle_)
  {this->srvr_handle_ = srvr_handle_;  }
};
} //simple_socket
} //industrial
#endif /* TCP_SERVER_H */

TCP_SERVER_H
服務器的主要代碼是集中在實現中,通過初始化函數將通信進程推進到監聽狀態,然後通過建立連接函數實現互連。

udp_socket

UdpSocket類,類似於TcpSocket類。

#ifndef UDP_SOCKET_H
#define UDP_SOCKET_H
#ifndef FLATHEADERS
#include "simple_message/socket/simple_socket.h"
#include "simple_message/shared_types.h"
#include "simple_message/smpl_msg_connection.h"
#else
#include "simple_socket.h"
#include "shared_types.h"
#include "smpl_msg_connection.h"
#endif
#ifdef LINUXSOCKETS
#include "sys/socket.h"
#include "arpa/inet.h"
#include "string.h"
#include "unistd.h"
#endif

#ifdef MOTOPLUS
#include "motoPlus.h"
#endif

namespace industrial
{
  namespace udp_socket
  {
    class UdpSocket : public industrial::simple_socket::SimpleSocket
    {
    public:
      UdpSocket();
      ~UdpSocket();
    protected:
      //連接握手標識
      static const char CONNECT_HANDSHAKE = 142;
      //數據接收變量
      char udp_read_buffer_[MAX_BUFFER_SIZE + 1];
      //讀取變量
      char* udp_read_head_;
      //長度變量
      size_t udp_read_len_;
      // Virtual 重寫
      int rawSendBytes(char *buffer,industrial::shared_types::shared_int num_bytes);
      int rawReceiveBytes(char *buffer,industrial::shared_types::shared_int num_bytes);
      bool rawPoll(int timeout, bool & ready, bool & error);
    };
} //udp_socket
} //industrial
#endif

udp_socket

udp_client

#ifndef UDP_CLIENT_H
#define UDP_CLIENT_H
#ifndef FLATHEADERS
#include "simple_message/socket/udp_socket.h"
#else
#include "udp_socket.h"
#endif

namespace industrial
{
  namespace udp_client
  {
    class UdpClient : public industrial::udp_socket::UdpSocket
    {
    public:
      UdpClient();
      ~UdpClient();
      bool makeConnect();
      //初始化UDP client,參數爲服務器的IP地址
      bool init(char *buff, int port_num);
    private:
    };
} //udp_client
} //industrial
#endif

UDP client

udp_server

#ifndef UDP_SERVER_H
#define UDP_SERVER_H
#ifndef FLATHEADERS
#include "simple_message/socket/udp_socket.h"
#else
#include "udp_socket.h"
#endif

namespace industrial
{
namespace udp_server
{
class UdpServer : public industrial::udp_socket::UdpSocket
{
public:
  UdpServer();
  ~UdpServer();
  bool makeConnect();
  //初始化
  bool init(int port_num);
private:
};
} //udp_server
} //industrial
#endif

udp_server
類似於tcp_server。

到此整個simple_message package的內容都理解了一遍,對於協議的理解以及上一層通訊的接口應該都可以很容易的進行理解,這整個內容就構成了ROS-I倒數第二層的內容,接下來對於底層,只差最後一層。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章