basix_doc 0.1
/Users/mourrain/Devel/mmx/basix/src/socket_port.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : socket_port.cpp
00004 * DESCRIPTION: Ports for sockets
00005 * COPYRIGHT  : (C) 2010  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license and comes WITHOUT
00008 * ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
00009 * If you don't have this file, write to the Free Software Foundation, Inc.,
00010 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00011 ******************************************************************************/
00012 
00013 #include <basix/posix_port.hpp>
00014 #include <unistd.h>
00015 #include <fcntl.h>
00016 #include <sys/wait.h>
00017 #include <sys/types.h>
00018 #include <sys/socket.h>
00019 #include <netinet/in.h>
00020 #include <netdb.h>
00021 #include <arpa/inet.h>
00022 
00024 
00025 namespace mmx {
00026 
00027 /******************************************************************************
00028 * Socket ports
00029 ******************************************************************************/
00030 
00031 class socket_port_rep: public posix_port_rep {
00032   string host;
00033   int    pnr;
00034   int    role;
00035 
00036 public:
00037   syntactic expression () const {
00038     if (role == 0)
00039       return syn ("socket_server_port", syntactic (host), syntactic (pnr));
00040     else if (role == 1)
00041       return syn ("socket_accept_port", syntactic (host), syntactic (pnr));
00042     else
00043       return syn ("socket_client_port", syntactic (host), syntactic (pnr));
00044   }
00045 
00046   void send (const char* s, nat len) {
00047     if (this->alive) {
00048       nat total= 0;          // how many bytes we've sent
00049       nat bytes_left= len;   // how many we have left to send
00050       nat n= 0;
00051       while (total < len) {
00052         n= ::send (this->fd, s+total, bytes_left, 0);
00053         if (n == ((nat) -1)) break;
00054         total += n;
00055         bytes_left -= n;
00056       }
00057     }
00058   }
00059 
00060   void feed () {
00061     if (this->pos > ((N(this->buffer) >> 1) + 1024)) {
00062       this->buffer= this->buffer (this->pos, N(this->buffer));
00063       this->pos= 0;
00064     }
00065     while (this->alive && wait (0)) {
00066       char tempout[1024];
00067       int r= recv (this->fd, tempout, 1024, 0);
00068       if (r <= 0) { this->alive= false; break; }
00069       else this->buffer << string (tempout, r);
00070     }
00071   }
00072 
00073   port accept ();
00074 
00075 public:
00076   inline socket_port_rep (const string& host2, int pnr2, int role2, int fd):
00077     posix_port_rep (3, fd),
00078     host (host2), pnr (pnr2), role (role2) {}
00079   inline ~socket_port_rep () {
00080     close (this->fd); }
00081 };
00082 
00083 port
00084 socket_port (const string& host, int pnr, int role, int fd) {
00085   return (port_rep*) new socket_port_rep (host, pnr, role, fd);
00086 }
00087 
00088 /******************************************************************************
00089 * Socket servers
00090 ******************************************************************************/
00091 
00092 port
00093 socket_server_port (const string& host, int pnr) {
00094   // get the server
00095   int fd;
00096   if ((fd = socket (PF_INET, SOCK_STREAM, 0)) == -1)
00097     return error_port ("call to 'socket' failed");
00098 
00099   // lose the pesky "address already in use" error message
00100   int yes= 1;
00101   if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
00102                   &yes, sizeof (int)) == -1)
00103     return error_port ("call to 'setsockopt' failed");
00104 
00105   // bind
00106   struct sockaddr_in local_address;
00107   local_address.sin_family = AF_INET;
00108   local_address.sin_addr.s_addr = INADDR_ANY;
00109   local_address.sin_port = htons (pnr);
00110   memset (local_address.sin_zero, '\0', sizeof local_address.sin_zero);
00111   if (bind (fd, (struct sockaddr *) &local_address,
00112             sizeof (local_address)) == -1)
00113     return error_port ("call to 'bind' failed");
00114 
00115   // listen
00116   if (::listen (fd, 10) == -1)
00117     return error_port ("call to 'listen' failed");
00118   return socket_port (host, pnr, 0, fd);
00119 }
00120 
00121 /******************************************************************************
00122 * Accepting a client
00123 ******************************************************************************/
00124 
00125 port
00126 socket_port_rep::accept () {
00127   ASSERT (role == 0, "socket server port expected");
00128   if (!this->alive || !wait (0)) return error_port ("no incoming connection");
00129   struct sockaddr_in remote_address;
00130   socklen_t addrlen= sizeof (remote_address);
00131   int client=
00132     ::accept (this->fd, (struct sockaddr *) &remote_address, &addrlen);
00133   if (client == -1)
00134     return error_port ("Call to 'accept' failed");
00135   else {
00136     string addr= inet_ntoa (remote_address.sin_addr);
00137     return socket_port (host, pnr, 1, client);
00138   }
00139 }
00140 
00141 /******************************************************************************
00142 * Socket clients
00143 ******************************************************************************/
00144 
00145 port
00146 socket_client_port (const string& host, int pnr) {
00147   // getting host
00148   char* _host= as_charp (host);
00149   struct hostent *hp = gethostbyname (_host);
00150   free_charp (_host);
00151   if (hp == NULL) return error_port ("no connection for '" * host * "'");
00152 
00153   // creating socket
00154   int fd= socket (AF_INET, SOCK_STREAM, 0);
00155   if (fd < 0) return error_port ("socket could not be created");
00156 
00157   // connecting to socket
00158   struct sockaddr_in insock;
00159   string where= host * ":" * as_string (pnr);
00160   memset ((char*) &insock, 0, sizeof (insock));
00161   insock.sin_family = AF_INET;
00162   insock.sin_port = htons ((unsigned short) pnr);
00163   memcpy ((char*) &insock.sin_addr, hp->h_addr, hp->h_length);
00164   if (connect (fd, (struct sockaddr*) &insock, sizeof (insock)) < 0)
00165     return error_port ("refused connection to '" * where * "'");
00166 
00167   // testing whether it works
00168   int flags = O_NONBLOCK;
00169   if (fcntl (fd, F_SETFL, flags) < 0)
00170     return error_port ("non working connection to '" * where * "'");
00171   return socket_port (host, pnr, 2, fd);
00172 }
00173 
00174 } // namespace mmx
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines