basix_doc 0.1
|
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