Low-level sockets
John Swensen <[email protected]> Fri, 05 May 2006 13:09:23 -0600
| Newsgroups | gmane.comp.gnu.octave.sources |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format.
--------------060400080609040100040707
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Attached is the first go-around for sockets in octave and a test. I
haven't switched it to just an integer yet, but I think it works great
the way it is. It seems to work good, but since I am somewhat of a
octave novice, is there a better way to manage the send and receive
buffers? Currently, I am simply making a buffer and copying the data
to/from the octave_value. I have tested it on cygwin and Ubuntu Linux.
- John Swensen
--------------060400080609040100040707
Content-Type: text/plain;
name="octave_sockets.cpp"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="octave_sockets.cpp"
// build instructions
// mkoctfile octave_sockets.cpp
// ln -sf octave_sockets.oct socket.oct
// ln -sf octave_sockets.oct connect.oct
// ln -sf octave_sockets.oct disconnect.oct
// ln -sf octave_sockets.oct gethostbyname.oct
// ln -sf octave_sockets.oct send.oct
// ln -sf octave_sockets.oct recv.oct
// ln -sf octave_sockets.oct bind.oct
// ln -sf octave_sockets.oct listen.oct
// ln -sf octave_sockets.oct accept.oct
// ln -sf octave_sockets.oct load_socket_constants.oct
// C++ STL includes
#include <cstdio>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#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/Array.h>
#include <octave/ops.h>
#include <octave/ov-base.h>
#include <octave/ov-typeinfo.h>
#include <octave/ov.h>
#include <octave/ov-scalar.h>
#include <octave/ov-struct.h>
#include <octave/ov-uint8.h>
// System includes
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.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();
}
// `DEFCONST' from "defun.h"
#ifndef DEFCONST
#define DEFCONST(name, defn, doc) \
DEFCONST_INTERNAL(name, defn, doc)
#endif
#ifndef DEFCONSTX
#define DEFCONSTX(name, sname, defn, doc) \
DEFCONSTX_INTERNAL (name, sname, defn, doc)
#endif
#define OCTAVE_TYPE_CONV_HELPER(VAR_IN, VAR_OUT, NAME, MATRIX_RESULT_T, SCALAR_RESULT_T) \
\
int t_arg = VAR_IN.type_id (); \
\
int t_result = MATRIX_RESULT_T::static_type_id (); \
\
if (t_arg == t_result || VAR_IN.class_name () == #NAME) \
{ \
VAR_OUT = VAR_IN; \
} \
else \
{ \
type_conv_fcn cf \
= octave_value_typeinfo::lookup_type_conv_op (t_arg, t_result); \
\
if (cf) \
{ \
octave_value *tmp (cf (*(VAR_IN.internal_rep ()))); \
\
if (tmp) \
{ \
VAR_OUT = octave_value (tmp); \
\
VAR_OUT.maybe_mutate (); \
} \
} \
else \
{ \
std::string arg_tname = VAR_IN.type_name (); \
\
std::string result_tname = VAR_IN.numel () == 1 \
? SCALAR_RESULT_T::static_type_name () \
: MATRIX_RESULT_T::static_type_name (); \
\
gripe_invalid_conversion (arg_tname, result_tname); \
} \
}
#define OCTAVE_TYPE_CONV(VAR_IN, VAR_OUT, NAME) \
OCTAVE_TYPE_CONV_HELPER (VAR_IN, VAR_OUT, NAME, octave_ ## NAME ## _matrix, \
octave_ ## NAME ## _scalar)
// 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 set the fd on creation.
*/
octave_socket( int fd );
/**
* Constructor used to create the socket.
*/
octave_socket( int domain, int type, int protocol );
/**
* Destructor.
*/
~octave_socket();
/**
* Various properties of the octave_socket datatype.
*/
bool is_constant (void) const { return true;}
bool is_defined (void) const { return true;}
bool print_as_scalar (void) const { return true;}
// Still undefined.
bool is_data_available() {};
/**
* Overloaded methods to print the fd as the socket id
*/
void print (ostream& os, bool pr_as_read_syntax = false) const;
void octave_socket::print_raw (std::ostream& os, bool pr_as_read_syntax) const;
/**
* Utility function for retrieving the socket fd.
*/
int get_sock_fd(void) { return sock_fd;};
void remove_sock_fd(void);
double scalar_value (bool frc_str_conv = false) const
{
return(double)sock_fd;
}
private:
DECLARE_OCTAVE_ALLOCATOR
DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
};
DEFBINOP_OP (lt, scalar, scalar, <)
DEFBINOP_OP (le, scalar, scalar, <=)
DEFBINOP_OP (eq, scalar, scalar, ==)
DEFBINOP_OP (ge, scalar, scalar, >=)
DEFBINOP_OP (gt, scalar, scalar, >)
DEFBINOP_OP (ne, scalar, scalar, !=)
void install_socket_ops(void)
{
INSTALL_BINOP (op_lt, octave_socket, octave_scalar, lt);
INSTALL_BINOP (op_le, octave_socket, octave_scalar, le);
INSTALL_BINOP (op_eq, octave_socket, octave_scalar, eq);
INSTALL_BINOP (op_ge, octave_socket, octave_scalar, ge);
INSTALL_BINOP (op_gt, octave_socket, octave_scalar, gt);
INSTALL_BINOP (op_ne, octave_socket, octave_scalar, ne);
}
DEFINE_OCTAVE_ALLOCATOR (octave_socket);
DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_socket, "octave_socket", "octave_socket");
std::map< int, octave_socket * > socket_map;
static bool type_loaded = false;
//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::octave_socket()
{
// TODO - some of the following constants are only defined for Linux/*nix
// - for the most part, the first 3 will probably be used
// domain constants
DEFCONSTX("AF_UNIX", F_AF_UNIX, AF_UNIX, "Socket domain constant");
DEFCONSTX("AF_LOCAL", F_AF_LOCAL, AF_LOCAL, "Socket domain constant");
DEFCONSTX("AF_INET", F_AF_INET, AF_INET, "Socket domain constant");
//DEFCONSTX("AF_INET6", F_AF_INET6, AF_INET6, "Socket domain constant");
//DEFCONSTX("AF_IPX", F_AF_IPX, AF_IPX, "Socket domain constant");
//DEFCONSTX("AF_NETLINK", F_AF_NETLINK, AF_NETLINK, "Socket domain constant");
//DEFCONSTX("AF_X25", F_AF_X25, AF_X25, "Socket domain constant");
//DEFCONSTX("AF_AX25", F_AF_AX25, AF_AX25, "Socket domain constant");
//DEFCONSTX("AF_ATMPVC", F_AF_ATMPVC, AF_ATMPVC, "Socket domain constant");
DEFCONSTX("AF_APPLETALK", F_AF_APPLETALK, AF_APPLETALK, "Socket domain constant");
//DEFCONSTX("AF_PACKET", F_AF_PACKET, AF_PACKET, "Socket domain constant");
// type constants
DEFCONSTX("SOCK_STREAM", F_SOCK_STREAM, SOCK_STREAM, "Socket type constant");
DEFCONSTX("SOCK_DGRAM", F_SOCK_DGRAM, SOCK_DGRAM, "Socket type constant");
DEFCONSTX("SOCK_SEQPACKET", F_SOCK_SEQPACKET, SOCK_SEQPACKET, "Socket type constant");
DEFCONSTX("SOCK_RAW", F_SOCK_RAW, SOCK_RAW, "Socket type constant");
DEFCONSTX("SOCK_RDM", F_SOCK_RDM, SOCK_RDM, "Socket type constant");
//DEFCONSTX("SOCK_PACKET", F_SOCK_PACKET, SOCK_PACKET, "Socket type constant");
}
//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::octave_socket( int fd )
{
sock_fd = fd;
socket_map[sock_fd] = this;
}
//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::octave_socket( int domain, int type, int protocol )
{
sock_fd = ::socket( domain, type, protocol );
if ( sock_fd == -1 )
{
error( "octave_socket: Error creating socket" );
}
else
{
socket_map[sock_fd] = this;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
octave_socket::~octave_socket()
{
remove_sock_fd();
}
//////////////////////////////////////////////////////////////////////////////////////////
void octave_socket::print (ostream& os, bool pr_as_read_syntax ) const
{
print_raw (os, pr_as_read_syntax);
newline (os);
}
void octave_socket::print_raw (std::ostream& os, bool pr_as_read_syntax) const
{
os << sock_fd;
}
void octave_socket::remove_sock_fd(void)
{
::close( sock_fd );
socket_map.erase( sock_fd );
sock_fd = -1;
}
// Function to create a socket
DEFUN_DLD(socket,args,nargout,"socket(int,int,int)\nSee the socket() man pages")
{
int domain = AF_INET;
int type = SOCK_STREAM;
int protocol = 0;
if ( !type_loaded )
{
octave_socket::register_type ();
install_socket_ops();
type_loaded = true;
}
// Convert the arguments to their #define'd value
if ( args.length() > 0 )
{
domain = args(0).int_value();
}
if ( args.length() > 1 )
{
type = args(1).int_value();
}
if ( args.length() > 2 )
{
protocol = args(2).int_value();
if( protocol != 0 )
{
error( "For now, protocol must always be 0 (zero)" );
return octave_value(-1);
}
}
// 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 )
{
error("connect: you must specify 2 paramters");
return octave_value(-1);
}
// 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");
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("connect: expecting a octave_socket or integer");
return octave_value(-1);
}
// Fill in the server info struct
serverInfo.sin_family = AF_INET;
if ( addr.length() > 0 )
{
hostInfo = gethostbyname( addr.c_str() );
if ( hostInfo )
{
serverInfo.sin_addr.s_addr = *((long*)hostInfo->h_addr_list[0]);
}
else
{
error( "connect: error in gethostbyname()" );
return octave_value(-1);
}
}
else
{
error( "connect: empty address" );
return octave_value(-1);
}
serverInfo.sin_port = htons(port);
retval = connect( s->get_sock_fd(), (struct sockaddr*)&serverInfo, sizeof(struct sockaddr) );
return octave_value(retval);
}
// function to disconnect asocket
DEFUN_DLD(disconnect,args,nargout, \
"disconnect(octave_socket)\nSince we can't call fclose on the fd directly, use this to disconnect")
{
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("connect: expecting a octave_socket or integer");
return octave_value(-1);
}
s->remove_sock_fd();
return octave_value(0);
}
// 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 )
{
error( "send: you must specify 2 parameters");
return octave_value(-1);
}
if ( args.length() > 2 && args(2).is_scalar_type() )
flags = args(2).int_value();
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("connect: expecting a octave_socket or integer");
return octave_value(-1);
}
// Extract the data from the octave variable and send it
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() )
{
NDArray d1 = data.array_value();
unsigned char* buf = new unsigned char[ d1.length() ];
for ( int i = 0 ; i < d1.length() ; i++ )
buf[i] = (unsigned char)d1(i);
retval = ::send( s->get_sock_fd(), (const char*)buf, data.byte_size(), 0 );
delete buf;
}
else
{
error( "connect: you have specified an invalid data type to send. Please format it prior to sending" );
return octave_value(-1);
}
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 )
{
error( "recv: you must specify 2 parameters" );
return octave_value(-1);
}
if ( args.length() > 2 && args(2).is_scalar_type() )
flags = args(2).int_value();
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("connect: expecting a octave_socket or integer");
return octave_value(-1);
}
long len = args(1).int_value();
unsigned char* buf = new unsigned char[ len ];
retval = ::recv( s->get_sock_fd(), buf, len, flags );
Matrix return_buf(1,retval);
octave_value_list return_list;
for ( int i = 0 ; i < retval ; i++ )
return_buf(0,i) = buf[i];
octave_value in_buf(return_buf);
octave_value out_buf;
OCTAVE_TYPE_CONV( in_buf, out_buf, uint8 );
return_list(0) = out_buf;
return_list(1) = retval;
return return_list;
}
// function to bind a socket
DEFUN_DLD(bind,args,nargout, \
"bind(octave_socket,int)\nSee the bind() man pages. This will bind a socket to a" \
" specific port")
{
int retval = 0;
if ( args.length() < 2 )
{
error( "bind: you must specify 2 parameters" );
return octave_value(-1);
}
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("connect: expecting a octave_socket or integer");
return octave_value(-1);
}
long port = args(1).int_value();
struct sockaddr_in serverInfo;
serverInfo.sin_family = AF_INET;
serverInfo.sin_port = htons( port );
serverInfo.sin_addr.s_addr = INADDR_ANY;
retval = ::bind( s->get_sock_fd(), (struct sockaddr *)&serverInfo, sizeof(serverInfo) );
return octave_value(retval);
}
// function to listen on a socket
DEFUN_DLD(listen,args,nargout, \
"listen(octave_socket,int)\nSee the listen() man pages")
{
int retval = 0;
if ( args.length() < 2 )
{
error( "listen: you must specify 2 parameters" );
return octave_value(-1);
}
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("connect: expecting a octave_socket or integer");
return octave_value(-1);
}
int backlog = args(1).int_value();
octave_stdout << "BACKLOG: " << backlog << endl;
retval = ::listen( s->get_sock_fd(), backlog );
return octave_value(retval);
}
// function to accept on a listening socket
DEFUN_DLD(accept,args,nargout, \
"accept(octave_socket)\nSee the accept() man pages")
{
int retval = 0;
struct sockaddr_in clientInfo;
socklen_t clientLen = sizeof(struct sockaddr_in);
if ( args.length() < 1 )
{
error( "accept: you must specify 1 parameter" );
return octave_value(-1);
}
// Determine the socket on which to operate
octave_socket* s = NULL;
if ( args(0).type_id() == octave_socket::static_type_id() )
{
const octave_value& rep = args(0).get_rep();
s = &((octave_socket &)rep);
}
else if ( args(0).is_scalar_type() )
{
int fd = args(0).int_value();
s = socket_map[fd];
}
else
{
error("accept: expecting a octave_socket or integer");
return octave_value(-1);
}
int fd = ::accept( s->get_sock_fd(), (struct sockaddr *)&clientInfo, &clientLen );
if ( fd != -1 )
{
// create the octave_socket object and set the fd
octave_socket* retobj = new octave_socket(fd);
// place the client information into a structure
Octave_map client_info_map;
client_info_map.assign("sin_family", octave_value(clientInfo.sin_family));
client_info_map.assign("sin_port", octave_value(clientInfo.sin_port));
client_info_map.assign("sin_addr", octave_value( inet_ntoa(clientInfo.sin_addr)));
// returns the accepted socket and a clientinfo structure
octave_value_list return_list;
return_list(0) = octave_value(retobj);
return_list(1) = client_info_map;
return return_list;
}
else
{
ostringstream os;
os << "accept: failed with errno = " << errno;
error(os.str().c_str());
return octave_value(fd);
}
}
// function to load socket constants
DEFUN_DLD(load_socket_constants,args,nargout, \
"Loads socket constants like AF_INET, SOCK_STREAM, etc.")
{
octave_socket temp;
return octave_value();
}
--------------060400080609040100040707
Content-Type: text/plain;
name="test_octave_socket.m"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="test_octave_socket.m"
## Tests for octave sockets
##
function test_octave_socket()
page_screen_output = 0
load_socket_constants
fail = 0
# Create the sockets
## Server socket
server = socket(AF_INET, SOCK_STREAM, 0)
if( server < 0 )
++fail
return
end
rc = bind(server,9001)
if( rc ~= 0 )
++fail
return
end
rc = listen(server,1)
if( rc ~= 0 )
++fail
return
end
## Client socket
client = socket(AF_INET, SOCK_STREAM, 0)
if( client < 0 )
++fail
return
end
# Create the connection and accept the connection
server_info = struct("addr","127.0.0.1", "port",9001)
rc = connect(client, server_info)
if( rc ~= 0 )
++fail
return
end
server_data = accept(server)
if( server_data < 0 )
++fail
return
end
# Send and receive data
## Send as string from client
msg = "Hello socket-land!"
rc = send( client, msg )
if( rc ~= length(msg) )
++fail
return
end
## Receive at server
[msg_s, len_s] = recv( server_data, 100 )
if( msg_s == -1 || len_s ~= length(msg) )
++fail
return
end
## Send back out from server
rc = send( server_data, msg_s )
if( rc ~= length(msg_s) )
++fail
return
end
## Receive at client
[msg_c, len_c] = recv( client, 100 )
if( msg_c == -1 || len_c ~= length(msg) )
++fail
return
end
## Compare original string with recv string
msg_in = num2str( msg_c, '%c' )
if( msg_in ~= msg )
++fail
return
end
rc = disconnect( client )
rc = disconnect( server_data )
rc = disconnect( server )
printf( 'Number of failures: %d\n', fail )
end
--------------060400080609040100040707
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
_______________________________________________
Octave-sources mailing list
[email protected]
https://www.cae.wisc.edu/mailman/listinfo/octave-sources
--------------060400080609040100040707--