diff host/tools/ecostest/common/eCosTestSocket.cpp @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/host/tools/ecostest/common/eCosTestSocket.cpp
@@ -0,0 +1,780 @@
+//####COPYRIGHTBEGIN####
+//                                                                          
+// ----------------------------------------------------------------------------
+// Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
+//
+// This program is part of the eCos host tools.
+//
+// This program is free software; you can redistribute it and/or modify it 
+// under the terms of the GNU General Public License as published by the Free 
+// Software Foundation; either version 2 of the License, or (at your option) 
+// any later version.
+// 
+// This program is distributed in the hope that it will be useful, but WITHOUT 
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for 
+// more details.
+// 
+// You should have received a copy of the GNU General Public License along with
+// this program; if not, write to the Free Software Foundation, Inc., 
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+//
+// ----------------------------------------------------------------------------
+//                                                                          
+//####COPYRIGHTEND####
+//=================================================================
+//
+//        eCosTestSocket.cpp
+//
+//        Socket test class
+//
+//=================================================================
+//=================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     sdf
+// Contributors:  sdf
+// Date:          1999-04-01
+// Description:   This class abstracts tcp/ip sockets for use in the testing infrastructure
+// Usage:
+//
+//####DESCRIPTIONEND####
+
+#include "eCosStd.h"
+#include "eCosTestSocket.h"
+#include "eCosTestSerial.h"
+#include "eCosTrace.h"
+
+enum {ERR_TIMEOUT=20000, ERR_READ_AFTER_CLOSE=20001};
+
+// Blocking read on one or other of the data sources:
+// Result:  0 - error occurred
+//          1 - data read from socket
+//          2 - data read from serial
+int CeCosTestSocket::SSRead (CeCosTestSerial &serial,CeCosTestSocket &socket,void *pBuf,unsigned int nSize,unsigned int &nRead,bool *pbStop)
+{
+    int rc=0;
+    bool bBlocking=serial.GetBlockingReads();
+    bool bBlockingModified=false;
+    while(0==pbStop || !(*pbStop)){
+        if(!socket.Peek(nRead)){
+            break;
+        } else if(nRead){
+            nRead=MIN(nRead,nSize);
+            rc=socket.recv(pBuf,nRead)?1:0;
+            break;
+        } else {
+            if(bBlocking){
+                serial.SetBlockingReads(false);
+                bBlockingModified=true;
+                bBlocking=false;
+            }
+            if(serial.Read(pBuf,nSize,nRead)){
+                if(nRead>0){
+                    rc=2;
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+        Sleep(1);
+    }
+    if(bBlockingModified){
+        serial.SetBlockingReads(true);
+    }
+    return rc;
+}
+
+// ctors and dtors
+
+CeCosTestSocket::CeCosTestSocket ():
+    m_nDefaultTimeout(10*1000),
+	m_nSock(-1),
+    m_nClient(0)
+{
+    TRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
+}
+
+CeCosTestSocket::CeCosTestSocket (int sock /*result of previous call of Listen*/, bool *pbStop):
+    m_nDefaultTimeout(10*1000),
+	m_nSock(-1),
+    m_nClient(0)
+{
+    TRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
+    Accept(sock,pbStop);
+}
+
+CeCosTestSocket::CeCosTestSocket (String strHost,int port,Duration dTimeout):
+    m_nDefaultTimeout(10*1000),
+	m_nSock(-1),
+    m_nClient(0)
+{
+    TRACE(_T("Create socket instance %08x\n"),(unsigned int)this);
+    Connect(strHost,port,dTimeout);
+}
+
+bool CeCosTestSocket::Accept(int sock /*result of previous call of Listen*/, bool *pbStop)
+{
+    m_nSock=-1;
+    while(0==pbStop||!*pbStop){
+        struct sockaddr cli_addr;
+        #ifndef _WIN32
+        unsigned 
+        #endif
+	    int clilen=sizeof(struct sockaddr);
+        m_nSock=::accept(sock, (struct sockaddr *) &cli_addr, &clilen);
+        SaveError();
+        if(-1==m_nSock){ 
+            if(WOULDBLOCK==SocketError()){
+                Sleep(100);
+                continue;
+            }
+        } else {
+            memcpy(&m_nClient,cli_addr.sa_data+2,4);
+            TRACE(_T("Connection accepted from %s - socket %d\n"),(LPCTSTR )ClientName(m_nClient),m_nSock);
+            SetSocketOptions();
+            break;
+        }
+	} 
+    return -1!=m_nSock;
+}
+
+int CeCosTestSocket::Listen(int nTcpPort)
+{
+   // Create socket
+    int sock=::socket(AF_INET, SOCK_STREAM, 0);
+    if (sock == -1) {
+        ERROR(_T("Couldn't create socket\n"));
+    } else {
+        TRACE(_T("Created socket %d listening on port %d\n"),sock,nTcpPort);
+        // Bind socket to address
+        struct sockaddr_in serv_addr;
+        memset(&serv_addr, 0, sizeof serv_addr);
+
+        serv_addr.sin_family = AF_INET;
+        serv_addr.sin_port=htons((short)nTcpPort);
+        serv_addr.sin_addr.s_addr = INADDR_ANY;
+        
+        if (::bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
+            TRACE(_T("Couldn't bind socket on port %d\n"),nTcpPort);
+            CloseSocket(sock);
+        } else if (-1==::listen(sock, SOMAXCONN)){
+            CloseSocket(sock);
+            TRACE(_T("socket error on listen - port %d\n"),nTcpPort);
+        } else {
+	        #ifdef _WIN32
+            int nTrue=1;
+            bool rc=(0==::ioctlsocket(sock, FIONBIO, (unsigned long *)&nTrue));
+	        #else //UNIX
+            int flags=::fcntl(sock,F_GETFL);
+	          flags|=O_NONBLOCK;
+            bool rc=(0==::fcntl (sock, F_SETFL, flags));
+	        #endif
+	        if(!rc){
+                TRACE(_T("Failed to set socket options on socket %d\n"),sock);
+            }
+        }
+    }
+    return sock;
+}
+
+bool CeCosTestSocket::Connect(String strHost,int port,Duration dTimeout)
+{
+    dTimeout=TimeoutDuration(dTimeout);
+    struct sockaddr_in serv_addr;
+
+    struct hostent* host_dat;
+
+    char ip[16]; // for nnn.nnn.nnn.nnn
+
+    VTRACE(_T("Connect: %s:%d timeout=%d\n"),(LPCTSTR )strHost,port,dTimeout);
+
+    // Get the target host address
+    String strErr;
+    char *pszHost=strHost.GetCString();
+    if (0==(host_dat=::gethostbyname(pszHost))){
+        SaveError();
+        TRACE(_T("Could not get IP address for host %s - %s\n"),(LPCTSTR )strHost,(LPCTSTR )SocketErrString());
+    } else {
+        char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] )  );
+        if(0==c){
+            SaveError();
+            TRACE(_T("Could inet_ntoa %s - %s\n"),(LPCTSTR )strHost,(LPCTSTR )SocketErrString());
+        } else {
+
+            strcpy(ip, c);
+            memset(&serv_addr, 0, sizeof serv_addr);
+
+            // Create socket
+            m_nSock = ::socket(AF_INET, SOCK_STREAM, 0);
+            if (-1 == m_nSock) {
+                TRACE(_T("Could not create socket [%s:%d]\n"),(LPCTSTR )strHost,port);
+            } else {
+                #ifdef _WIN32
+                SetSocketOptions();
+                #endif
+                TRACE(_T("Created socket %d connected to %s:%d\n"),m_nSock,(LPCTSTR )strHost,port);
+                // Bind socket to address
+                serv_addr.sin_family = AF_INET;
+                serv_addr.sin_port=htons((short)port);
+                SaveError();
+                serv_addr.sin_addr.s_addr = inet_addr(ip);
+
+                // Connect to server
+                VTRACE(_T("Connect() : connecting to server\n"));
+                int cc=::connect(m_nSock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
+                SaveError();
+                String strMsg;
+                if(-1==cc){
+                    if(
+    #ifdef _WIN32
+                       WOULDBLOCK==SocketError()
+    #else // UNIX
+                       EINPROGRESS==SocketError()
+    #endif
+                       ){
+                        // Allow dTimeout milliseconds for connect to complete
+                        fd_set set;
+                        FD_ZERO(&set);
+    #ifdef _WIN32
+                        #pragma warning( push )
+                        #pragma warning( disable : 4127 ) // conditional expression is constant
+    #endif
+                        FD_SET((unsigned)m_nSock, &set);
+    #ifdef _WIN32
+                        #pragma warning( pop )
+    #endif
+                        struct timeval tv;
+                        tv.tv_sec = dTimeout/1000;  
+                        tv.tv_usec = 1000*(dTimeout % 1000);
+                        switch(::select(m_nSock, NULL, &set , NULL, &tv)){
+                            case 0:
+                                m_nErr=ERR_TIMEOUT;
+                                strMsg.Format(_T("attempt timed out after %d seconds"),dTimeout/1000);
+                                break;
+                            case -1:
+                                SaveError();
+                                strMsg=SocketErrString();
+                                break;
+                            default:
+                                cc=0;
+                        }
+                    } else {
+                        strMsg=SocketErrString();
+                    }
+                }
+
+                if(-1==cc){
+                    TRACE(_T("Could not connect to %s:%d - %s\n"),(LPCTSTR )strHost,port,(LPCTSTR )strMsg);
+                    CloseSocket(m_nSock);
+                }  else {
+                    #ifndef _WIN32
+                    SetSocketOptions();
+                    #endif
+                }
+            }
+        }
+    }
+    delete [] pszHost;
+    return -1!=m_nSock;
+}
+
+bool CeCosTestSocket::sendrecv(bool bSend,const void *pData,unsigned int nLength,
+	LPCTSTR pszMsg,Duration dTimeout,CeCosTestSocket::StopFunc pFnStop,void *pParam)
+{
+    
+  dTimeout=TimeoutDuration(dTimeout);
+  
+  LPCTSTR pszSR=(bSend?_T("sending"):_T("receiving"));
+  LPTSTR c=(LPTSTR )pData;
+  Time ft0=Now();
+  int nTodo=nLength;
+  while((nTodo>0) && ((0==pFnStop) || (!pFnStop(pParam)))){
+    int s=bSend?::send(m_nSock, (const char *)c, nTodo, 0): ::recv(m_nSock, (char *)c, nTodo, 0);
+    if(0==s && !bSend){
+      m_nErr=ERR_READ_AFTER_CLOSE;
+    } else {
+      SaveError();
+    }
+    if(-1==s && WOULDBLOCK==SocketError()){
+      Duration d=Duration(Now()-ft0);
+      if(d>dTimeout){
+        TRACE(_T("%d/%d mSec timeout on socket %d %s %s - processed %d/%d bytes\n") ,
+          d,dTimeout,m_nSock,pszSR,pszMsg, 
+          nLength-nTodo,nLength);
+        m_nErr=ERR_TIMEOUT;
+        break;
+      }
+      Sleep(100);
+    } else if (s>0) {
+      c+=s;
+      nTodo-=s;
+      ft0=Now();
+    } else {
+      TRACE(_T("Error on socket %d %s %s - %s\n") ,m_nSock, pszSR, pszMsg, (LPCTSTR )SocketErrString());
+      break;
+    }
+  }
+  return 0==nTodo;
+}
+
+// Graceful socket closedown
+CeCosTestSocket::~CeCosTestSocket()
+{
+	Close();
+    TRACE(_T("Delete socket instance %08x\n"),(unsigned int)this);
+}
+
+bool CeCosTestSocket::CloseSocket(int &sock)
+{
+    bool rc=false;
+    if(-1!=sock){
+        TRACE(_T("Closing socket %d\n"),sock);
+        try{
+            shutdown(sock,0);// SD_BOTH
+    #ifdef _WIN32
+            rc=(0==closesocket(sock));
+    #else // UNIX
+            rc=(0==close(sock));
+    #endif
+        }
+        catch(...) {
+            TRACE(_T("!!! Exception caught in CeCosTestSocket::CloseSocket!!!\n"));
+        }
+        sock=-1;
+    }
+	return rc;
+}
+
+bool CeCosTestSocket::SetSocketOptions()
+{
+    bool rc;
+    #ifdef _WIN32
+      int nTrue=1;
+      rc=(0==::ioctlsocket(m_nSock, FIONBIO, (unsigned long *)&nTrue));
+      SaveError();
+    #else // UNIX
+      int flags=::fcntl(m_nSock,F_GETFL);
+      SaveError();
+      flags|=O_NONBLOCK;
+      rc=(0==::fcntl (m_nSock, F_SETFL, flags));
+      SaveError();
+    #endif
+    //LINGER linger;
+    //setsockopt(m_nSock,SOL_SOCKET,SO_LINGER,(LPCTSTR )linger, sizeof(LINGER));
+	if(!rc){
+        TRACE(_T("Failed to set socket options socket %d - %s\n"),m_nSock,(LPCTSTR )SocketErrString());
+    }
+    return rc;
+}
+
+String CeCosTestSocket::SocketErrString(int nErr)
+{
+    String str;
+    #ifdef _WIN32
+      switch(nErr){
+        case ERR_TIMEOUT: str=_T("Read operation timed out");break;
+        case ERR_READ_AFTER_CLOSE: str=_T("Read operation after socket closed");break;
+
+        case WSAEACCES: str=_T("Permission denied");break;
+        case WSAEADDRINUSE: str=_T("Address already in use");break;
+        case WSAEADDRNOTAVAIL: str=_T("Cannot assign requested address");break;
+        case WSAEAFNOSUPPORT: str=_T("Address family not supported by protocol family");break;
+        case WSAEALREADY: str=_T("Operation already in progress");break;
+        case WSAECONNABORTED: str=_T("Software caused connection abort");break;
+        case WSAECONNREFUSED: str=_T("Connection refused");break;
+        case WSAECONNRESET: str=_T("Connection reset by peer");break;
+        case WSAEDESTADDRREQ: str=_T("Destination address required");break;
+        case WSAEFAULT: str=_T("Bad address");break;
+        case WSAEHOSTDOWN: str=_T("Host is down");break;
+        case WSAEHOSTUNREACH: str=_T("No route to host");break;
+        case WSAEINPROGRESS: str=_T("Operation now in progress");break;
+        case WSAEINTR: str=_T("Interrupted function call");break;
+        case WSAEINVAL: str=_T("Invalid argument");break;
+        case WSAEISCONN: str=_T("Socket is already connected");break;
+        case WSAEMFILE: str=_T("Too many open files");break;
+        case WSAEMSGSIZE: str=_T("Message too long");break;
+        case WSAENETDOWN: str=_T("Network is down");break;
+        case WSAENETRESET: str=_T("Network dropped connection on reset");break;
+        case WSAENETUNREACH: str=_T("Network is unreachable");break;
+        case WSAENOBUFS: str=_T("No buffer space available");break;
+        case WSAENOPROTOOPT: str=_T("Bad protocol option");break;
+        case WSAENOTCONN: str=_T("Socket is not connected");break;
+        case WSAENOTSOCK: str=_T("Socket operation on non-socket");break;
+        case WSAEOPNOTSUPP: str=_T("Operation not supported");break;
+        case WSAEPFNOSUPPORT: str=_T("Protocol family not supported");break;
+        case WSAEPROCLIM: str=_T("Too many processes");break;
+        case WSAEPROTONOSUPPORT: str=_T("Protocol not supported");break;
+        case WSAEPROTOTYPE: str=_T("Protocol wrong type for socket");break;
+        case WSAESHUTDOWN: str=_T("Cannot send after socket shutdown");break;
+        case WSAESOCKTNOSUPPORT: str=_T("Socket type not supported");break;
+        case WSAETIMEDOUT: str=_T("Connection timed out");break;
+        case WSATYPE_NOT_FOUND: str=_T("Class type not found");break;
+        case WSAEWOULDBLOCK: str=_T("Resource temporarily unavailable");break;
+        case WSAHOST_NOT_FOUND: str=_T("Host not found");break;
+        case WSA_INVALID_HANDLE: str=_T("Specified event object handle is invalid");break;
+        case WSA_INVALID_PARAMETER: str=_T("One or more parameters are invalid");break;
+        //case WSAINVALIDPROCTABLE: str=_T("Invalid procedure table from service provider");break;
+        //case WSAINVALIDPROVIDER: str=_T("Invalid service provider version number");break;
+        case WSA_IO_INCOMPLETE: str=_T("Overlapped I/O event object not in signaled state");break;
+        case WSA_IO_PENDING: str=_T("Overlapped operations will complete later");break;
+        case WSA_NOT_ENOUGH_MEMORY: str=_T("Insufficient memory available");break;
+        case WSANOTINITIALISED: str=_T("Successful case WSAStartup not yet:performed");break;
+        case WSANO_DATA: str=_T("Valid name, no data record of requested type");break;
+        case WSANO_RECOVERY: str=_T("This is a non-recoverable error");break;
+        //case WSAPROVIDERFAILEDINIT: str=_T("Unable to initialize a service provider");break;
+        case WSASYSCALLFAILURE: str=_T("System call failure");break;
+        case WSASYSNOTREADY: str=_T("Network subsystem is unavailable");break;
+        case WSATRY_AGAIN: str=_T("Non-authoritative host not found");break;
+        case WSAVERNOTSUPPORTED: str=_T("WINSOCK.DLL version out of range");break;
+        case WSAEDISCON: str=_T("Graceful shutdown in progress");break;
+        case WSA_OPERATION_ABORTED: str=_T("Overlapped operation aborted");break;
+        default:
+            str.Format(_T("Unknown error %d (0x%08x)"),nErr,nErr);
+    }
+#else // UNIX
+   switch(nErr){
+        case ERR_TIMEOUT: str=_T("Read operation timed out");break;
+        case ERR_READ_AFTER_CLOSE: str=_T("Read operation after socket closed");break;
+        default:
+            str=strerror(errno);
+   }
+#endif
+    return str;
+}
+
+bool CeCosTestSocket::sendInteger(int n,LPCTSTR pszMsg,Duration dTimeout)
+{
+    // This has to support cross-architectural endianness
+    unsigned char c[sizeof(int)];
+    for(unsigned int i=0;i<sizeof(int);i++){
+        c[i]=(unsigned char)(n&0xff);
+        n>>=8;
+    }
+    return send (c, sizeof(int),pszMsg,dTimeout);
+}
+
+bool CeCosTestSocket::recvInteger(int & n,LPCTSTR pszMsg,Duration dTimeout)
+{
+    // This has to support cross-architectural endianness
+    unsigned char c[sizeof(int)];
+    bool rc=recv (c, sizeof(int),pszMsg,dTimeout);
+    n=0;
+    if(rc){
+        for(int i=sizeof(int)-1;i>=0;--i){
+            n<<=8;
+            n|=c[i];
+        }
+    }
+    return rc;
+}
+
+// Socket communications for strings are always non-UNICODE:
+bool CeCosTestSocket::recvString  (String &str,LPCTSTR pszMsg,Duration dTimeout)
+{
+    int nLength;
+    bool rc=false;
+    if(recvInteger(nLength,pszMsg,dTimeout)){
+        if(0==nLength){
+            rc=true;
+        } else {
+            Buffer b(1+nLength);
+            char *c=(char *)b.Data();
+            if(c){
+                rc=recv(c,nLength,pszMsg,dTimeout);
+                c[nLength]='\0';
+                str=String::CStrToUnicodeStr(c);
+            }
+        }
+    }
+    return rc;
+}
+
+// Socket communications for strings are always non-UNICODE:
+bool CeCosTestSocket::sendString  (const String &str,LPCTSTR pszMsg,Duration dTimeout)
+{
+    char *psz=str.GetCString();
+    int nLength=strlen(psz); 
+    bool rc=sendInteger(nLength,pszMsg,dTimeout) && (0==nLength || send(psz,nLength,pszMsg,dTimeout));
+    delete [] psz;
+    return rc;
+}
+
+
+// Give indication of bytes available to be read (but don't read them)
+bool CeCosTestSocket::Peek (unsigned int &nAvail)
+{
+    char buf[8192];
+    int n=::recv(m_nSock, buf, sizeof buf, MSG_PEEK);
+    nAvail=0;
+    bool rc=false;
+    switch(n) {
+        case -1:
+            SaveError();
+            if(WOULDBLOCK==SocketError()){
+                rc=true; // nAvail stays==0
+            } else {
+                ERROR(_T("Peek: err=%d %s\n"),SocketError(),(LPCTSTR )SocketErrString());
+            }
+            break;
+        case 0:
+            m_nErr=ERR_READ_AFTER_CLOSE;
+            break;
+        default:
+            rc=true;
+            nAvail=n;
+    }
+    return rc;
+}
+
+// Connect tcp/ip port and serial port together.
+// Traffic is passed through pFunc, passed parameter pParam.
+// The pFunc function:
+//    may reallocate pBuf (using malloc/realloc etc...)
+//    must leave pBuf allocated on exit
+//    should not close either serial or socket
+//    should leave writing to its caller
+//    should return false if it wishes to terminate the connection (after caller has written output)
+bool CeCosTestSocket::ConnectSocketToSerial (CeCosTestSocket &socket,CeCosTestSerial &serial,FilterFunc *pSerialToSocketFilterFunc/*=0*/,void *pSerialParam/*=0*/,FilterFunc *pSocketToSerialFilterFunc/*=0*/,void *pSocketParam/*=0*/,bool *pbStop/*=0*/)
+{
+    serial.ClearError();
+    enum {BUFSIZE=8192};
+    void *pBuf=malloc(BUFSIZE);
+    TRACE(_T("ConnectSocketToSerial: connected\n"));
+    bool rc=true;
+    try {
+        /*
+        { //hack
+            unsigned int nWritten;//hack
+            serial.Write(_T("+"),1,nWritten);//hack
+        }//hack
+        */
+        while(rc && (0==pbStop || !(*pbStop))){
+            unsigned int nRead=0;
+            switch(SSRead (serial,socket,pBuf,BUFSIZE,nRead,pbStop)){
+                case 2: 
+                    VTRACE(_T("Serial:%d\n"),nRead);
+                    if(pSerialToSocketFilterFunc){
+                        rc=pSerialToSocketFilterFunc(pBuf,nRead,serial,socket,pSerialParam);
+                    }
+                    if(nRead && !socket.send(pBuf,nRead)){
+                        TRACE(_T("Failed to write to socket\n"));
+                        rc=false;
+                    }
+                    break;
+                case 1:
+                    unsigned int nWritten;
+                    VTRACE(_T("Socket:%d\n"),nRead);
+                    if(pSocketToSerialFilterFunc){
+                        rc=pSocketToSerialFilterFunc(pBuf,nRead,serial,socket,pSocketParam);
+                    }
+                    {
+                        LPTSTR c=(LPTSTR )pBuf;
+                        int nToWrite=nRead;
+                        while(nToWrite>0){
+                            if(!serial.Write(pBuf,nRead,nWritten)){
+                                TRACE(_T("Failed to write to serial\n"));
+                                rc=false;
+                                break;
+                            }
+                            nToWrite-=nWritten;
+                            c+=nWritten;
+                        }
+                    }
+                    break;
+                case 0:
+                    TRACE(_T("SSRead returned error indication\n"));
+                    rc=false;
+                    break;
+                    // Error
+            }
+        }
+    }
+	catch (...){
+        TRACE(_T("!!! ConnectSocketToSerial exception caught!!!\n"));
+        free(pBuf);
+        throw;
+    }
+    free(pBuf);
+    return rc;
+}
+
+// Connect two tcp/ip ports together.
+// Traffic is passed through pFunc, passed parameter pParam.
+// The pFunc function:
+//    may reallocate pBuf (using malloc/realloc etc...)
+//    must leave pBuf allocated on exit
+//    should not close either serial or socket
+//    should leave writing to its caller
+//    should return false if it wishes to terminate the connection (after caller has written output)
+bool CeCosTestSocket::ConnectSocketToSocket (CeCosTestSocket &o,FilterFunc *pSocketToSocketFilterFunc1,FilterFunc *pSocketToSocketFilterFunc2,void *pParam,bool *pbStop)
+{
+  enum {BUFSIZE=8192};
+  void *pBuf=malloc(BUFSIZE);
+  TRACE(_T("ConnectSocketToSocket: connected\n"));
+  bool rc=true;
+  try {
+    while(rc && (0==pbStop || !(*pbStop))){
+      fd_set set;
+      FD_ZERO(&set);
+      FD_SET((unsigned)m_nSock, &set);
+      FD_SET((unsigned)o.m_nSock, &set);
+      struct timeval tv;
+      tv.tv_sec = 1;
+      tv.tv_usec = 0;
+      switch(::select(m_nSock,&set,0,0,&tv)){
+      case -1:
+        rc=false;
+        break;
+      case 1:
+      case 2:
+        {
+          unsigned int nAvail=0;
+          if(FD_ISSET((unsigned)m_nSock, &set) && Peek(nAvail) && recv(pBuf,nAvail)){
+            //rc=pSocketToSocketFilterFunc1(pBuf,nAvail,socket,this,o);
+            o.send(pBuf,nAvail);
+          }
+          if(FD_ISSET((unsigned)o.m_nSock, &set) && o.Peek(nAvail) && o.recv(pBuf,nAvail)){
+            //rc=pSocketToSocketFilterFunc2(pBuf,nAvail,socket,o,this);
+            send(pBuf,nAvail);
+          }
+        }
+      case 0:
+        break;
+      }
+    }
+  }
+  catch (...){
+    TRACE(_T("!!! ConnectSocketToSocket exception caught!!!\n"));
+    rc=false;
+  }
+  free(pBuf);
+  return rc;
+}
+
+bool CeCosTestSocket::ConnectSocketToSerial (
+    int nListenSock,LPCTSTR pszPort, int nBaud,
+    FilterFunc *pSerialToSocketFilterFunc/*=0*/,void *pSerialParam/*=0*/,FilterFunc *pSocketToSerialFilterFunc/*=0*/,void *pSocketParam/*=0*/,
+    bool *pbStop)
+{
+    bool rc=false;
+    try{
+        TRACE(_T("ConnectSocketToSerial : socket %d <--> %s\n"),nListenSock,pszPort);
+
+        CeCosTestSerial serial;
+        serial.SetBlockingReads(false);
+        // Open serial device.
+        if (!serial.Open(pszPort,nBaud)){
+            ERROR(_T("Couldn't open port %s\n"),pszPort);
+        } else {
+            // Flush the serial buffer.
+            serial.Flush();
+
+            TRACE(_T("ConnectSocketToSerial: waiting for connection...\n"));
+            CeCosTestSocket socket;
+            if(!socket.Accept(nListenSock,pbStop)){
+                ERROR(_T("ConnectSocketToSerial - couldn't accept\n"));
+            } else {    
+                rc=ConnectSocketToSerial (socket,serial,pSerialToSocketFilterFunc,pSerialParam,pSocketToSerialFilterFunc,pSocketParam,pbStop);
+            }
+        }
+        TRACE(_T("ConnectSocketToSerial : done\n"));
+    }
+    catch(...){
+        TRACE(_T("ConnectSocketToSerial !!!exception handled!!!\n"));
+    }
+    return rc;
+}
+    
+String CeCosTestSocket::ClientName(int nClient) 
+{
+    char ip[4];
+    memcpy(ip,&nClient,4);
+    struct hostent *he=::gethostbyaddr((const char *)ip,4,AF_INET);
+    String str;
+    if(he){
+        str=String::CStrToUnicodeStr(he->h_name);
+    } else {
+        str.Format(_T("%u.%u.%u.%u"),ip[0],ip[1],ip[2],ip[3]);
+    }
+    return str;
+}
+
+String CeCosTestSocket::HostPort(LPCTSTR pszHost,int nPort) 
+{
+    String str;
+    str.Format(_T("%s:%d"),pszHost,nPort);
+    return str;
+}
+
+// Split the string into host:port parts.  Result tells us whether it was successful.
+bool CeCosTestSocket::ParseHostPort (LPCTSTR pszHostPort, String &strHost, int &nPort)
+{
+  int n=_stscanf(pszHostPort,_T("%[^:]:%d"),strHost.GetBuffer(_tcslen(pszHostPort)),&nPort);
+  strHost.ReleaseBuffer();
+  return 2==n && nPort>0 && nPort<=0xffff;
+}
+
+// Is the string in the form host:port?
+bool CeCosTestSocket::IsLegalHostPort (LPCTSTR pszHostPort)
+{
+	int nPort=0;
+    String strHost;
+	return ParseHostPort(pszHostPort,strHost,nPort);
+}
+
+// Translate a timeout that may be one of the special values DEFAULTTIMEOUT or NOTIMEOUT to a value in milliseconds.
+Duration CeCosTestSocket::TimeoutDuration(Duration dTimeout)
+{
+    switch(dTimeout){
+        case DEFAULTTIMEOUT:
+            dTimeout=m_nDefaultTimeout;
+            break;
+        case NOTIMEOUT:
+            dTimeout=0x7fffffff;
+            break;
+        default:
+            break;
+    }
+    return dTimeout;
+}
+
+bool CeCosTestSocket::IsSocketError(int n)
+{
+  #ifdef _WIN32
+    return n>=WSABASEERR && n<30000; // include our own (e.g. ERR_READ_AFTER_CLOSE)
+  #else // UNIX
+    return true;
+  #endif
+}
+
+String CeCosTestSocket::SocketErrString() { 
+    return SocketErrString(m_nErr); 
+}
+
+
+bool CeCosTestSocket::SameHost(LPCTSTR host1, LPCTSTR host2)
+{
+  char *h1=String(host1).GetCString();
+  char *h2=String(host2).GetCString();
+  char ip1[16],ip2[16];
+  struct hostent* host_dat;
+  bool rc=(0==_tcscmp(host1,host2));
+  if (0!=(host_dat=::gethostbyname(h1))){
+    char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] )  );
+    if(c){
+      strcpy(ip1,c);
+      if (0!=(host_dat=::gethostbyname(h2))){
+        char *c=inet_ntoa( *( (struct in_addr *)host_dat->h_addr_list[0] )  );
+        if(c){
+          strcpy(ip2,c);
+        }
+      }
+      rc=(0==strcmp(ip1,ip2));
+    }
+  }
+  delete [] h1;
+  delete [] h2;
+  return rc;
+}