home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / vsoup128.zip / socket.cc < prev    next >
C/C++ Source or Header  |  1997-02-12  |  11KB  |  465 lines

  1. //  $Id: socket.cc 1.21 1997/02/12 08:48:34 hardy Exp $
  2. //
  3. //  This progam/module was written by Hardy Griech based on ideas and
  4. //  pieces of code from Chin Huang (cthuang@io.org).  Bug reports should
  5. //  be submitted to rgriech@ibm.net.
  6. //
  7. //  This file is part of soup++ for OS/2.  Soup++ including this file
  8. //  is freeware.  There is no warranty of any kind implied.  The terms
  9. //  of the GNU Gernal Public Licence are valid for this piece of software.
  10. //
  11. //  simple socket class (everything is done in text mode)
  12. //
  13. //  attention:
  14. //  ----------
  15. //  TSocket::open requires semaphor support for multithreaded environment.
  16. //  For OS/2 it's implemented, but not for any other os!
  17. //
  18.  
  19.  
  20. #include <sys/types.h>
  21. #include <sys/socket.h>
  22. #include <ctype.h>
  23. #include <io.h>
  24. #include <fcntl.h>
  25. #include <signal.h>
  26. #include <stdarg.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. ////#include <unistd.h>
  31. #include <netdb.h>
  32. #include <arpa/inet.h>
  33. #include <netinet/in.h>
  34.  
  35. #include "mts.hh"
  36. #include "socket.hh"
  37. #include "sema.hh"
  38.  
  39.  
  40.  
  41. #define OPENSOCKETSIZE   256
  42. #define CHECKABORT( action )      { if (socketAbort) { action } }
  43. #define CHECKABORTANDKILL         { CHECKABORT( close(); raise(SIGUSR1); sleep(2); ) }
  44.  
  45. static TProtCounter bytesRcvd;
  46. static TProtCounter bytesXmtd;
  47. static char openSocket[OPENSOCKETSIZE];    // Holzhammer
  48. static int  socketAbort = 0;
  49.  
  50. extern TSemaphor sysSema;
  51.  
  52.  
  53.  
  54. TSocket::TSocket( void )
  55. {
  56.     State = init;
  57. }   // TSocket::TSocket
  58.  
  59.  
  60.  
  61. TSocket::~TSocket()
  62. {
  63.     if (State == connected)
  64.     close();
  65. }   // TSocket::~TSocket
  66.  
  67.  
  68.  
  69. unsigned long TSocket::getBytesRcvd( void )
  70. {
  71.     return bytesRcvd;
  72. }   // TSocket::getBytesRcvd
  73.  
  74.  
  75.  
  76. unsigned long TSocket::getBytesXmtd( void )
  77. {
  78.     return bytesXmtd;
  79. }   // TSocket::getBytesXmtd
  80.  
  81.  
  82.  
  83. const char *TSocket::getLocalhost( void )
  84. //
  85. //  Returns a pointer to the local host name.  The buffer for the name resides
  86. //  on the heap and must be freed after usage...
  87. //
  88. {
  89.     static TSemaphor sema;
  90.     struct sockaddr_in local;
  91.     int addrLen;
  92.     struct hostent *hp;
  93.     const char *localhost;
  94.     char *buf;
  95.  
  96. #ifdef TRACE_ALL
  97.     printfT("TSocket::getLocalHost()\n" );
  98. #endif
  99.     sema.Request();
  100.     addrLen = sizeof( local );
  101.     getsockname( sock, (struct sockaddr *)&local, &addrLen );
  102.     hp = gethostbyaddr( (const char *)&local.sin_addr, sizeof(local.sin_addr),
  103.             AF_INET );
  104.     localhost =  hp ? hp->h_name : inet_ntoa(local.sin_addr);
  105.     buf = new char [strlen(localhost)+1];
  106.     strcpy( buf,localhost );
  107.     sema.Release();
  108.  
  109.     return buf;
  110. }   // TSocket::getLocalhost
  111.  
  112.  
  113.  
  114. void TSocket::abortAll( void )
  115. {
  116.     int c, h, res;
  117.  
  118. #ifdef TRACE
  119.     printfT( "TSocket::abortAll()\n" );
  120. #endif
  121.  
  122.     socketAbort = 1;
  123.  
  124.     for (h = 0;  h < OPENSOCKETSIZE;  ++h) {
  125.     if (openSocket[h]) {
  126.         res = ::fcntl( h, F_SETFL, O_NONBLOCK );
  127. #ifdef DEBUG
  128.         printfT( "TSocket::abortAll(), fcntl: %d,%d\n",h,res );
  129. #endif
  130.     }
  131.     }
  132.  
  133.     for (c = 0;  c < 20;  ++c) {
  134.     int found = 0;
  135.     _sleep2( 100 );
  136.     for (h = 0;  h < OPENSOCKETSIZE;  ++h) {
  137.         if (openSocket[h]) {
  138.         openSocket[h] = 0;
  139.         res = ::close( h );
  140. #ifdef DEBUG
  141.         printfT( "TSocket::abortAll(), close: %d,%d\n",h,res );
  142. #endif
  143.         found = 1;
  144.         }
  145.     }
  146.     if ( !found)
  147.         break;
  148.     }
  149. }   // TSocket::abortAll
  150.  
  151.  
  152.  
  153. void TSocket::close( void )
  154. {
  155. #ifdef TRACE_ALL
  156.     printfT( "TSocket::close()\n" );
  157. #endif
  158.  
  159.     while (State == connecting) {
  160.     CHECKABORT( break; );
  161.     _sleep2( 100 );
  162.     }
  163.     
  164.     if (State == connected  &&  openSocket[sock]) {
  165. #ifdef DEBUG_ALL
  166.     printfT( "closing %s:%s/%s\n", ipAdr,service,protocol );
  167. #endif
  168. ////    ::shutdown( sock,2 );
  169.     ::close( sock );
  170.     openSocket[sock] = 0;
  171.     sock = -1;
  172.     
  173.     delete ipAdr;    ipAdr = NULL;
  174.     delete service;  service = NULL;
  175.     delete protocol; protocol = NULL;
  176.     delete Buffer;   Buffer = NULL;
  177.     }
  178.     State = closed;
  179. }   // TSocket::close
  180.  
  181.  
  182.  
  183. int TSocket::open( const char *ipAdr, const char *service, const char *protocol,
  184.            int portno, int buffSize )
  185. //
  186. //  Socket öffnen:  Parameter sind wohl halbwegs klar...
  187. //  portno <= 0 -> aus %ETC%/SERVICES den Port holen (ist portno angegeben, so sind
  188. //                 service/protocol unwichtig)
  189. //  - Es muß beim Return aufgepaßt werden, daß State != connecting gesetzt wird - sonst
  190. //    wartet der close() u.U. endlos...
  191. //  - Open() muß im MT-Fall mit Semaphoren abgesichert werden, da diverse Socket-Fkts
  192. //    Zeiger auf statische Struct zurückgeben!
  193. //  - der einfachheithalber wird im Fehlerfall mit einem GOTO ans Ende gesprungen.  Dort
  194. //    wird dann u.a. das Semaphor freigegeben
  195. //  
  196. //  Return: >=0 -> ok
  197. //          < 0 -> failed (im Moment keine weiteren Angaben)
  198. //
  199. {
  200.     int Result;
  201.     int port;
  202.     unsigned long inaddr;
  203.     struct sockaddr_in ad;
  204.     static TSemaphor OpenSema;
  205.  
  206. #ifdef TRACE_ALL
  207.     printfT( "TSocket::open(%s,%s,%s,%d,%d)\n",ipAdr,service,protocol,portno,buffSize );
  208. #endif
  209.  
  210.     if (State != closed  &&  State != init)
  211.     close();
  212.  
  213.     State = connecting;
  214.     Result = -1;
  215.     sock = -1;
  216.     memset(&ad, 0, sizeof(ad));
  217.  
  218.     //
  219.     //  Ist die Adresse als 32bit-IP-Adresse oder als Name angegeben ?
  220.     //
  221.     inaddr = inet_addr(ipAdr);
  222.     if (inaddr != INADDR_NONE) {
  223.     ad.sin_family = AF_INET;
  224.     ad.sin_addr.s_addr = inaddr;
  225.     }
  226.     else {
  227.     struct hostent *host;
  228.  
  229.     OpenSema.Request();
  230. #ifdef DEBUG_ALL
  231.     printfT( "TSocket::open:  before gethostbyname\n" );
  232. #endif
  233.     host = gethostbyname( ipAdr );
  234.     if (host == NULL) {
  235.         OpenSema.Release();
  236.         goto OpenEnd;
  237.     }
  238.     ad.sin_family = host->h_addrtype;
  239.     memcpy(&ad.sin_addr, host->h_addr, host->h_length);
  240. #ifdef DEBUG_ALL
  241.     printfT( "TSocket::open:  behind gethostbyname\n" );
  242. #endif
  243.     OpenSema.Release();
  244.     }
  245.  
  246.     //
  247.     //  Service auseinanderfieseln und Port# bestimmen
  248.     //
  249.     if (portno > 0)
  250.     port = htons( portno);
  251.     else if (isdigit(service[0]))
  252.     port = htons( atoi(service) );
  253.     else {
  254.     struct servent *serv;
  255.  
  256.     OpenSema.Request();
  257.     serv = getservbyname (service,protocol);
  258.     if (serv == NULL) {
  259.         OpenSema.Release();
  260.         goto OpenEnd;
  261.     }
  262.     port = serv->s_port;
  263.     OpenSema.Release();
  264.     }
  265.     ad.sin_port = port;
  266.  
  267.     //
  268.     //  Verbindung aufbauen
  269.     //
  270.     sysSema.Request();        // bug in emxrev <= 52:  socket()/open() etc are not threadsafe (_fd_init())
  271.     sock = socket( AF_INET, SOCK_STREAM, 0 );
  272.     sysSema.Release();
  273.     if (sock < 0)
  274.     goto OpenEnd;
  275.     if (connect(sock,(struct sockaddr *)&ad,sizeof(ad)) < 0)
  276.     goto OpenEnd;
  277.     setmode( sock, O_TEXT );      // only applicable for read()/write()
  278.     
  279.     Result = sock;
  280.  
  281.     //
  282.     //  setup buffer
  283.     //
  284.     Buffer = new unsigned char [buffSize+10];
  285.     TSocket::BuffSize = buffSize;
  286.     BuffNdx = BuffEnd = 0;
  287.  
  288.     //
  289.     //  store ip&protocol
  290.     //
  291.     TSocket::ipAdr = xstrdup(ipAdr);
  292.     TSocket::service = xstrdup(service);
  293.     TSocket::protocol = xstrdup(protocol);
  294.  
  295. OpenEnd:
  296.     if (Result >= 0) {
  297. #ifdef DEBUG_ALL
  298.     printfT( "socket opened successfully %s,%s/%s\n",
  299.          TSocket::ipAdr,TSocket::service,TSocket::protocol );
  300. #endif
  301.     State = connected;
  302.     openSocket[sock] = 1;
  303.     }
  304.     else {
  305.     if (sock != -1)
  306.         ::close( sock );
  307.     sock = -1;
  308.     State = closed;
  309.     }
  310.  
  311.     CHECKABORTANDKILL;
  312.     
  313.     return Result;
  314. }   // TSocket::open
  315.  
  316.  
  317.  
  318. int TSocket::send( const void *src, int len )
  319. {
  320.     int n;
  321.     char *buf = (char *)src;
  322.  
  323. #ifdef DEBUG
  324.     *(buf+len) = '\0';
  325. #endif
  326. #ifdef DEBUG_ALL
  327.     printfT( "TSocket::send(%s,.): %d\n", buf,sock );
  328. #endif
  329.     bytesXmtd += len;
  330.     while (len) {
  331.     CHECKABORTANDKILL;
  332. #ifdef DEBUG_ALL
  333.     if ( !openSocket[sock]) {
  334.         printfT( "TSocket::send():  socket closed\n" );
  335.         return -1;
  336.     }
  337. #endif
  338.         n = ::write( sock, buf, len );
  339.     CHECKABORTANDKILL;
  340.         if (n <= 0) {
  341. #ifdef DEBUG
  342.         printfT( "TSocket::send(): error '%s',%d,%d,%d\n", buf,sock,errno,n );
  343. #endif
  344.             return -1;
  345.     }
  346.         len -= n;
  347.         buf += n;
  348.     }
  349.     return 0;
  350. }   // TSocket::send
  351.  
  352.  
  353.  
  354. int TSocket::printf( const char *fmt, ... )
  355. {
  356.     va_list ap;
  357.     char buf[BUFSIZ];
  358.     int res;
  359.  
  360.     res = -1;
  361.     if (State == connected) {
  362.     va_start( ap, fmt );
  363.     vsprintfT( buf, fmt, ap );
  364.     va_end( ap );
  365.     res = send( buf, strlen(buf) );
  366.     }
  367.     return res;
  368. }   // TSocket::printf
  369.  
  370.  
  371.  
  372. int TSocket::nextchar( void )
  373. {
  374.     if (State != connected  ||  BuffEnd < 0) {
  375. #ifdef DEBUG_ALL
  376.     printfT( "TSocket::nextchar():  State %d, BuffEnd %d\n", State, BuffEnd );
  377. #endif
  378.     return -1;
  379.     }
  380.  
  381.     if (BuffNdx >= BuffEnd) {
  382.     CHECKABORTANDKILL;
  383. #ifdef DEBUG_ALL
  384.     if ( !openSocket[sock]) {
  385.         printfT( "TSocket::nextchar():  socket %d closed\n",sock );
  386.         return -1;
  387.     }
  388. #endif
  389.     BuffEnd = ::read( sock, Buffer, BuffSize );
  390.     CHECKABORTANDKILL;
  391.     if (BuffEnd <= 0) {
  392. #ifdef DEBUG_ALL
  393.         printfT( "TSocket::nextchar() error: %d,%d,%d\n",sock,errno,BuffEnd );
  394. #endif
  395.         BuffEnd = -1;
  396.         return -1;
  397.     }
  398. #ifdef DEBUG_ALL
  399.     printfT( "TSocket::nextchar():  %d,%d\n", sock,BuffEnd );
  400. #endif
  401.     BuffNdx = 0;
  402.     bytesRcvd += BuffEnd;
  403.     }
  404.     return Buffer[BuffNdx++];
  405. }   // TSocket::nextchar
  406.  
  407.  
  408.  
  409. char *TSocket::gets( char *buff, int bufflen )
  410. //
  411. //  gets from socket ('\n' is not contained in return buff)
  412. //  on EOF NULL is returned, otherwise buff
  413. //  If line is too long to fit into buff, characters will be fetched til EOL
  414. //
  415. {
  416.     char *p;     // Zeiger auf gelesene Zeichen
  417.     int  n;      // Anzahl der gelesenen Zeichen
  418.     int  c;      // gelesenes Zeichen
  419.  
  420.     if (bufflen <= 1) {             // buff muß min. 2 Zeichen lang sein!
  421. #ifdef DEBUG
  422.     printfT( "TSocket::gets():  ill bufflen %d\n",bufflen );
  423. #endif
  424.     return NULL;
  425.     }
  426.  
  427.     p = buff;
  428.     n = 0;
  429.     for (;;) {
  430.     if (n >= bufflen-2) {       // if Buffer exhausted, read til '\n'
  431.         do
  432.         c = nextchar();
  433.         while (c != '\n'  &&  c != -1);
  434.         break;
  435.     }
  436.  
  437.     c = nextchar();
  438.     if (c == -1) {              // EOF!
  439.         if (n == 0) {
  440.         *p = '\0';
  441. #ifdef DEBUG
  442.         strcpy( buff,"!!EOF!!" );
  443.         printfT( "TSocket::gets():  !!EOF!!\n" );
  444. #endif
  445.         return( NULL );
  446.         }
  447.         else
  448.         break;
  449.     }
  450.     else if (c == '\r'  ||  c == '\0')         // skip '\r', '\0'
  451.         continue;
  452.     else if (c == '\n')         // Zeile fertig !
  453.         break;
  454.     else {
  455.         *(p++) = c;
  456.         ++n;
  457.     }
  458.     }
  459.     *p = '\0';
  460. #ifdef DEBUG_ALL
  461.     printfT( "TSocket::gets(): %d,'%s'\n", sock,buff );
  462. #endif
  463.     return buff;
  464. }   // TSocket::gets
  465.