Sunday, 12 April 2015

UDP AF_UNIX, AF_LOCAL p2p C++ sample

Many samples many answers, many are not enough clear what to set-up and how to send data between to UDP sockets either being local  (AF_LOCAL), or over network AF_REMOTE.

I put together a small peer to peer app.

    * pay attention at bind function
    * pay attention at sizeof (), for AF_LOCAL

 use SUN_LEN(struct) not sizeof(struct)

A class:==============================================
  /* 

   [UDP LOCAL](IPL:PORT)<=========>[UDP REMOTE](IPR:PORT) 

   [UDP LOCAL](L_FILENAME)<=====>[UDP REMOTE](RFILENAME) 

  */ 

 #include <sys/socket.h> 

 #include <sys/un.h> 

 #include <resolv.h> 

 #include <sys/select.h> 

 #include <arpa/inet.h> 

 #include <sys/ioctl.h> 

 #include <sys/time.h> 

 class udp_p2p 

 { 

 public: 

   udp_p2p(); 

   virtual ~udp_p2p(); 

   int create(const char* local, const char* remote); 

   int receive(char* b, int l); 

   int send(const char* b, int l); 

   void destroy(); 

   int sock()const{return _socket;} 

 private: 

   int     _port[2];               // local remote 

   int     _socket;                // socket 

   struct   sockaddr_in _sin[2];     // sin local sin remote 

   struct   sockaddr_un _sun[2];    // sun local sun remote 

   struct   sockaddr*  _p_sad[2];   // pointer to sin or sun 

   socklen_t        _n_sad[2];      // length of rsin or sun 

   char    _sname[2][32];         // socket names or ips 

   bool    _blocal;               // local socket or AF_INET 

 }; 





The methods:
 udp_p2p::udp_p2p()

 {

   _socket=-1;

   _blocal = false;

   ::memset(&_sin, 0, sizeof(_sin));

   ::memset(&_sun, 0, sizeof(_sun));

 }

 udp_p2p::~udp_p2p()

 {

   destroy();

   if(_blocal)

   {

     ::unlink(_sname[0]);

   }

 }

 /*

   "127.0.0.1:5432"  "192.168.1.120:5555"  

                   or

    "/tmp/peer1" "/tmp/peer2"

 */

 int udp_p2p::create(const char* local, const char* remote)

 {

   int af_what = AF_LOCAL;

   ::strcpy(_sname[0], local);

   ::strcpy(_sname[1], remote);

   if(strchr(local,':')) //has port

   {

     char* lport = strchr(_sname[0],':');

     char* rport = strchr(_sname[1],':');

     _port[0] = atoi(lport+1);

     *lport=0;

     _port[1] = atoi(rport+1);

     *rport=0;

     _sin[0].sin_family = AF_INET; //local

     _sin[0].sin_addr.s_addr = htonl(INADDR_ANY);

     _sin[0].sin_port = htons(_port[0]);

     _p_sad[0] = (struct sockaddr*)&_sin[0];

     _sin[1].sin_family = AF_INET; // to send to

     _sin[1].sin_addr.s_addr = inet_addr(_sname[1]);

     _sin[1].sin_port = htons(_port[1]);

     _p_sad[1] = (struct sockaddr*)&_sin[1];

     _n_sad[0] = sizeof(sockaddr_in);

     _n_sad[1] = sizeof(sockaddr_in);

     ::printf("\n receving on %s:%d sending to %s:%d\n",

      _sname[0], _port[0], _sname[1], _port[1]);

     af_what = AF_INET;

   }

   else

   {

     ::unlink(_sname[0]);

     _sun[0].sun_family = AF_LOCAL;

     ::strcpy(_sun[0].sun_path, _sname[0]);

     _p_sad[0] = (struct sockaddr*)&_sun[0]; //local

     _sun[1].sun_family = AF_LOCAL;

     ::strcpy(_sun[1].sun_path, _sname[1]); //remote

     _p_sad[1] = (struct sockaddr*)&_sun[1];

     _n_sad[0] = SUN_LEN(&_sun[0]); // use SUN_LEN macro from /un.h

     _n_sad[1] = SUN_LEN(&_sun[1]);

     _blocal = true;

     printf("\n receving on %s sending to %s\n", _sname[0], _sname[1]);

   }

   _socket = socket(af_what, SOCK_DGRAM, 0); //locl receiver

   if(_socket < 0)

   {

     printf(" error create socket 0 %d", errno);

     return 0;

   }

   //

   // bind the receiver socket on local address

   //

   if(::bind(_socket, (struct sockaddr*)_p_sad[0], _n_sad[0]) < 0)

   {

     printf(" cannot bind socket: %d. Passed in address is invalid or port is in use",errno);

     return 0;

   }

   return _socket;

 }

 int udp_p2p::receive(char* b, int l)

 {

   if(_blocal)

   {

     // pass in the recfrom the sun address of the remote

     int rv = ::recvfrom(_socket, b, l, 0, (struct sockaddr *)_p_sad[1], &_n_sad[1]); //recfrom specific addr

     if(rv <= 0)

       return rv;

     b[rv] = 0;

     return rv;

   }

   // AF_INET

   int rv = ::recvfrom(_socket, b, l, 0, (struct sockaddr *)&_p_sad[1], &_n_sad[1]);

   if(rv <= 0)

   {

     return rv;

   }

   b[rv] = 0;

   return rv;

 }

 int udp_p2p::send(const char* b, int l)

 {

   // pass in the sendto the sun address of the remote

   int sd = sendto(_socket, b, l, 0, (struct sockaddr *)_p_sad[1], _n_sad[1]);

   return sd;

 }

 void udp_p2p::destroy()

 {

   ::shutdown (_socket, 0x02);

   usleep(10000);

   ::close(_socket);

   usleep(10000);

 }




Usage: Peer 1, Sample given for AF_LOCAL / AF_UNIX.
 #include <errno.h> 

 #include <stdlib.h> 

 #include <stdio.h> 

 #include <fcntl.h> 

 #include <unistd.h> 

 #include <iostream> 

 #include <string> 

 #include <unistd.h> 

 #include <sstream> 

 using namespace std; 

 static bool _alive=true; 

 void ControlC (int i) 

 { 

   _alive=false; 

 } 

 int main(int nargs, char* vargs[]) 

 { 

   signal(SIGPIPE, ControlC); 

   signal(SIGINT, ControlC); 

   signal(SIGABRT, ControlC); 

   signal(SIGKILL, ControlC); 

   if(nargs!=3) 

   { 

     cout << "pass in the local and remote file names for socket\n"; 

     return 1; 

   } 

   udp_p2p   u; 

   string    remote = vargs[1]; 

   string    local = vargs[2]; 

   cout << "local: " << local << " remote: " << remote << "\n"; 

   if(u.create(local.c_str(), remote.c_str())) 

   { 

     int   sel, bytes, rbytes; 

     int   nfd = u.sock()+1; 

     fd_set rfd;    // file descriptor read flags 

     struct timeval tv; 

     char  io[256]; 

     cout << "starting shell OK. Escape to exit\n"; 

     while(_alive) 

     { 

       FD_ZERO(&rfd); 

       FD_SET(u.sock(), &rfd); // add pipe to the read descriptor watch list 

       FD_SET(0, &rfd); // add pipe to the read descriptor watch list 

       tv.tv_sec = 0; 

       tv.tv_usec = 100000; 

       sel = select(nfd, &rfd, NULL, NULL, &tv); 

       if(sel==0) 

       { 

         ::usleep(10000); 

         continue; 

       } 

       if(sel==-1)break; 

       if(FD_ISSET(u.sock(),&rfd)) 

       { 

         bytes = u.receive(io, sizeof(io)-1); 

         if(bytes<=0) 

           break; 

         io[bytes]=0; 

         cout << io; 

       } 

       if(FD_ISSET(0,&rfd)) 

       { 

         bytes = read(0, io, sizeof(io)-1); 

         if(bytes==1 && io[0]==0x13) 

         { 

           cout << "bye\n"; 

           break; 

         } 

         io[bytes]=0; 

         rbytes = u.send(io, bytes); 

         if(rbytes!=bytes) 

         { 

           cout << "serror sendto:" << errno <<"\n"; 

           //break; 

         } 

       } 

       FD_CLR(0, &rfd); 

       FD_CLR(u.sock(), &rfd); 

       ::usleep(10000); 

     } 

     char quit[2]={0}; quit[0]=0x13; 

     u.send(quit,1); 

     u.destroy(); 

     sleep(2); 

   } 

   return 0; 

 } 




Runtime

 peeer /tmp/me /tmp/you # in terminal 1 

 peer /tmp/you /tmp/me # in terminal 2 




Picture







No comments:

Post a Comment