octave-maintainers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Socket functions from octave


From: John Swensen
Subject: Socket functions from octave
Date: Tue, 02 May 2006 16:51:12 -0600
User-agent: Thunderbird 1.5.0.2 (Windows/20060308)

I noticed on the projects page that there was a request for
implementation of the basic socket functions (e.g. socket, connect,
bind, listen, etc.).  Attached is a first shot at it.  So far I have all
the client functions implemented.  I will work on the server ones next. 
Is this what you were looking for?

 * Only allows the sending of uint8 arrays or strings (This could be
changed to detect the size and perform endian swapping to network byte
order)
 * Only receives data into a uint8 RowVector
 * Needs more error checking for lost connections, etc.

Let me know what you think.  Also, I am not sure how I go about getting
this accepted into the code base.  Or, does this belong in octave-forge?

John Swensen
 
// C++ STL includes
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
using namespace std;

// Octave Includes
#include <octave/oct.h>
#include <octave/parse.h>
#include <octave/toplev.h>
#include <octave/cmd-hist.h>
#include <octave/symtab.h>
#include <octave/variables.h>
#include <octave/ov-struct.h>

// System includes
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

template <class T>
std::string to_string(T t, std::ios_base & (*f)(std::ios_base&))
{
        std::ostringstream oss;
        oss << f << t;
        return oss.str();
}

// Derive an octave_socket class from octave_base_value
class octave_socket : public octave_base_value
{
private:

  /**
   * Socket file descriptor
   */
  int sock_fd;

public:

  /**
   * Default constructor.  Must be defined, but never used.
   */
  octave_socket(); 

  /**
   * Constructor used to create the socket.
   */
  octave_socket( int domain, int type, int protocol ); 

  /**
   * Destructor.
   */
  ~octave_socket();

  bool is_constant (void) const { return true; }
        bool is_defined (void) const { return true; }

  // Still undefined.
  bool is_data_available() {};

  /** 
   * Overloaded to print the fd as the socket id
   */
  void print (ostream& os, bool pr_as_read_syntax = false) const;

  /**
   * Utility function for retrieving the socket fd.
   */
  int get_sock_fd(void) { return sock_fd; };
  
private:
  DECLARE_OCTAVE_ALLOCATOR
  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
};

DEFINE_OCTAVE_ALLOCATOR (octave_socket);
DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_socket, "octave_socket", 
"octave_socket");


//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::octave_socket() 
{
} 


//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::octave_socket( int domain, int type, int protocol ) 
{
  sock_fd = ::socket( domain, type, protocol );
  if( sock_fd == -1 )
  {
    octave_stdout << "Error creating socket" << endl;
  }
}


//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::~octave_socket() 
{
  ::close( sock_fd );
}


//////////////////////////////////////////////////////////////////////////////////////////
void octave_socket::print (ostream& os, bool pr_as_read_syntax ) const
{
  os << to_string<int>(sock_fd,std::dec) << endl;
}


// Define the following functions for socket classes
/*
bind
listen
accept 
*/

// Define the following functions for use otherwise
/*
getaddrinfo
others?
*/

// Function to create a socket
DEFUN_DLD(socket,args,nargout,"socket(string,string,string)\nSee the socket() 
man pages")
{
  int domain    = PF_INET;
  int type      = SOCK_STREAM;
  int protocol  = 0;

  // Convert the arguments to their #define'd value
  if( args.length() > 0 )
  {
    string s_domain   = args(0).string_value();
    if( s_domain == "PF_UNIX" || s_domain == "PF_LOCAL" )
    {
      s_domain = PF_LOCAL;
    }
    else if( s_domain == "PF_INET" )
    {
      s_domain = PF_INET;
    }
    else if( s_domain == "PF_APPLETALK" )
    {
      s_domain = PF_APPLETALK;
    }
    else
    {
      // Error about invalid domain
      octave_stdout << "Invalid socket domain: " << s_domain << endl;
    }
  }

  if( args.length() > 1 )
  {                  
    string s_type     = args(1).string_value();
    if( s_type == "SOCK_STREAM" )
    {
      type = SOCK_STREAM;
    }
    else if( s_type == "SOCK_DGRAM" )
    {
      type = SOCK_DGRAM;
    }
    else if( s_type == "SOCK_SEQPACKET" )
    {
      type = SOCK_SEQPACKET;
    }
    else if( s_type == "SOCK_RAW" )
    {
      type = SOCK_RAW;
    }
    else if( s_type == "SOCK_RDM" )
    {
      type = SOCK_RDM;
    }
    else
    {
      // Error about invlid type
      octave_stdout << "Invalid socket type: " << s_type << endl;
    }
  }

  if( args.length() > 2 )
  {
    string s_protocol = args(2).string_value();

    octave_stdout << "For now, protocol must always be 0 (zero)" << endl;
  }

  // Create the new socket
  octave_socket* retval = new octave_socket( domain, type, protocol );
  if ( nargout > 0 && retval->get_sock_fd() != -1 )
      return octave_value(retval);
  
  return octave_value();

}

// function to create an outgoing connection
DEFUN_DLD(connect,args,nargout, \
          "connect(octave_socket,struct)\nSee the connect() man pages")
{
  int retval = -1;
  struct sockaddr_in serverInfo;
  struct hostent*    hostInfo;

  if( args.length() < 2 )
  {
    octave_stdout << "You must specify 2 parameters" << endl;
    retval = -2;
  }

  // Extract information about the server to connect to.
  const octave_value& struct_serverInfo = args(1).get_rep();
  octave_struct& addrInfo = ((octave_struct&)struct_serverInfo);

  string addr = addrInfo.map_value().stringfield("addr");
  int port = addrInfo.map_value().intfield("port");

  // If the input parameters is a octave_socket object
  if ( args(0).type_id() == octave_socket::static_type_id() )
  {
    // Determine the socket on which to operate
    const octave_value& rep = args(0).get_rep();
    octave_socket& s = ((octave_socket &)rep);

    serverInfo.sin_family = AF_INET;

    // Determine the host address by attempting a gethostbyname() operation
    if( addr.length() > 0 )
    {
      hostInfo = gethostbyname( addr.c_str() );
      if( hostInfo )
      {
        serverInfo.sin_addr.s_addr = *((long*)hostInfo->h_addr_list[0]);
      }
      else
      {
        retval = -3;
        octave_stdout << "ERROR in gethostbyname()" << endl;
        return octave_value(retval);
      }
    }
    else
    {
      octave_stdout << "empty address" << endl;
      retval = -4;
      return octave_value(retval);
    }
  
    serverInfo.sin_port = htons(port);
  
    retval = connect( s.get_sock_fd(), (struct sockaddr*)&serverInfo, 
sizeof(struct sockaddr) );
  }
  // If using the fd integer value, look up the octave_socket object from a 
list by fd
  else 
  {
    // TODO
  }

  return octave_value(retval);
}

// function to get a host number from a host name
DEFUN_DLD(gethostbyname,args,nargout, \
          "gethostbyname(string)\nSee the gethostbyname() man pages")
{
  struct hostent*    hostInfo = NULL;
  string_vector host_list;


  if( args(0).is_string() )
  {
    string addr = args(0).string_value();
    hostInfo = gethostbyname( addr.c_str() );
    if( hostInfo )
    {
      for( int i = 0 ; i < hostInfo->h_length/4 ; i++ )
      {
        string temp_addr = string(  inet_ntoa( *(struct 
in_addr*)hostInfo->h_addr_list[i] ));
        host_list.append( temp_addr );
      }
    }
  }

  return octave_value(host_list);
}

// function to send data over a socket
DEFUN_DLD(send,args,nargout, \
          "send(octave_socket,octave_value)\nSee the send() man pages.  This 
will only allow the" \
          " user to send uint8 arrays or strings")
{
  int retval = 0;
  int flags = 0;

  if( args.length() < 2 )
  {
    octave_stdout << "You must specify 2 parameters" << endl;
    retval = -1;
    return octave_value(retval);
  }

  if( args.length() > 2 )
    flags = args(2).int_value();

  // TODO - allow the user to pass in the integer fd of the socket also
  const octave_value& rep = args(0).get_rep();
  octave_socket& s = ((octave_socket &)rep);

  const octave_value& data = args(1).get_rep();


  if( data.is_string() )
  {
    string buf = data.string_value();
    retval = ::send( s.get_sock_fd(), buf.c_str(), buf.length(), flags );
  }
  else if( data.byte_size() == data.numel() )
  {
    octave_value_list data_list = data.list_value();
    unsigned char* buf = new unsigned char[ data.byte_size() ];
    for( int i = 0 ; i < data.byte_size() ; i++ )
      buf[i] = data_list(i).int_value();
    retval = ::send( s.get_sock_fd(), (const char*)buf, data.byte_size(), 0 );
    delete buf;
  }
  else
  {
    octave_stdout << "You have specified an invalid data type to send.  Please 
format it prior to sending" << endl;
  }

  return octave_value(retval);
}

// function to receive data over a socket
DEFUN_DLD(recv,args,nargout, \
          "recv(octave_socket,int)\nSee the send() man pages.  This will only 
allow the" \
          " user to receive uint8 arrays or strings")
{
  int retval = 0;
  int flags = 0;

  if( args.length() < 2 )
  {
    octave_stdout << "You must specify 2 parameters" << endl;
    retval = -1;
  }

  if( args.length() > 2 )
    flags = args(2).int_value();

  // TODO - allow the user to pass in the integer fd of the socket also
  const octave_value& rep = args(0).get_rep();
  octave_socket& s = ((octave_socket &)rep);

  long len = args(1).int_value();

  unsigned char* buf = new unsigned char[ len ];
  retval = ::recv( s.get_sock_fd(), buf, len, flags ); 
  octave_stdout << "RECVD: " << buf << endl;


  RowVector return_buf(retval);
  octave_value_list return_list;
  for( int i = 0 ; i < retval ; i++ )
    return_buf(i) = buf[i];

  return_list(0) = return_buf;
  return_list(1) = retval;

  return return_list;
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]