Files
epics-base/modules/libcom/test/blockingSockTest.cpp
Andrew Johnson 3c99391d93 Added SPDX License ID to all EPICS-original source files
In some cases the license-identification header was missing,
so I added that as well. Replaced the remaining headers that
specifically identified "Versions 3.13.7 and higher".

Makefiles and the build system were deliberately excluded.
2020-08-03 11:53:01 -05:00

302 lines
7.6 KiB
C++

/*************************************************************************\
* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* SPDX-License-Identifier: EPICS
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <string.h>
#include <stdio.h>
#include "osiSock.h"
#include "osiWireFormat.h"
#include "epicsThread.h"
#include "epicsSignal.h"
#include "epicsUnitTest.h"
#include "testMain.h"
union address {
struct sockaddr_in ia;
struct sockaddr sa;
};
class circuit {
public:
circuit ( SOCKET );
void recvTest ();
void shutdown ();
void close ();
bool recvWakeupDetected () const;
bool sendWakeupDetected () const;
virtual const char * pName () = 0;
protected:
SOCKET sock;
epicsThreadId id;
bool recvWakeup;
bool sendWakeup;
protected:
virtual ~circuit() {}
};
class serverCircuit : public circuit {
public:
serverCircuit ( SOCKET );
private:
const char * pName ();
};
class clientCircuit : public circuit {
public:
clientCircuit ( const address & );
private:
const char * pName ();
};
class server {
public:
server ( const address & );
void start ();
void daemon ();
void stop ();
address addr () const;
protected:
address srvaddr;
SOCKET sock;
epicsThreadId id;
bool exit;
};
circuit::circuit ( SOCKET sockIn ) :
sock ( sockIn ),
id ( 0 ),
recvWakeup ( false ),
sendWakeup ( false )
{
testOk ( this->sock != INVALID_SOCKET, "Socket valid" );
}
bool circuit::recvWakeupDetected () const
{
return this->recvWakeup;
}
bool circuit::sendWakeupDetected () const
{
return this->sendWakeup;
}
void circuit::shutdown ()
{
int status = ::shutdown ( this->sock, SHUT_RDWR );
testOk ( status == 0, "Shutdown() returned Ok" );
}
void circuit::close ()
{
epicsSocketDestroy ( this->sock );
}
void circuit::recvTest ()
{
char buf [1];
while ( true ) {
int status = recv ( this->sock,
buf, (int) sizeof ( buf ), 0 );
if ( status == 0 ) {
testDiag ( "%s was disconnected", this->pName () );
this->recvWakeup = true;
break;
}
else if ( status > 0 ) {
testDiag ( "%s received %i characters", this->pName (), status );
}
else {
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
testDiag ( "%s socket recv() error was \"%s\"",
this->pName (), sockErrBuf );
this->recvWakeup = true;
break;
}
}
}
extern "C" void socketRecvTest ( void * pParm )
{
circuit * pCir = reinterpret_cast < circuit * > ( pParm );
pCir->recvTest ();
}
clientCircuit::clientCircuit ( const address & addrIn ) :
circuit ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) )
{
address tmpAddr = addrIn;
int status = ::connect (
this->sock, & tmpAddr.sa, sizeof ( tmpAddr ) );
testOk ( status == 0, "Client end connected" );
circuit * pCir = this;
this->id = epicsThreadCreate (
"client circuit", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
socketRecvTest, pCir );
testOk ( this->id != 0, "Client thread created" );
}
const char * clientCircuit::pName ()
{
return "client circuit";
}
extern "C" void serverDaemon ( void * pParam ) {
server * pSrv = reinterpret_cast < server * > ( pParam );
pSrv->daemon ();
}
server::server ( const address & addrIn ) :
srvaddr ( addrIn ),
sock ( epicsSocketCreate ( AF_INET, SOCK_STREAM, IPPROTO_TCP ) ),
id ( 0 ), exit ( false )
{
testOk ( this->sock != INVALID_SOCKET, "Server socket valid" );
// setup server side
osiSocklen_t slen = sizeof ( this->srvaddr );
int status = bind ( this->sock, & this->srvaddr.sa, slen );
if ( status ) {
testDiag ( "bind to server socket failed, status = %d", status );
}
if ( getsockname(this->sock, & this->srvaddr.sa, & slen) != 0 ) {
testAbort ( "Failed to read socket address" );
}
status = listen ( this->sock, 10 );
testOk ( status == 0, "Server socket listening" );
}
void server::start ()
{
this->id = epicsThreadCreate (
"server daemon", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
serverDaemon, this );
testOk ( this->id != 0, "Server thread created" );
}
void server::daemon ()
{
while ( ! this->exit ) {
// accept client side
address addr;
osiSocklen_t addressSize = sizeof ( addr );
SOCKET ns = accept ( this->sock,
& addr.sa, & addressSize );
if ( this->exit )
break;
testOk ( ns != INVALID_SOCKET, "Accepted socket valid" );
circuit * pCir = new serverCircuit ( ns );
testOk ( pCir != 0, "Server circuit created" );
}
}
void server::stop ()
{
this->exit = true;
epicsSocketDestroy ( this->sock );
}
address server::addr () const
{
return this->srvaddr;
}
serverCircuit::serverCircuit ( SOCKET sockIn ) :
circuit ( sockIn )
{
circuit * pCir = this;
epicsThreadId threadId = epicsThreadCreate (
"server circuit", epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
socketRecvTest, pCir );
testOk ( threadId != 0, "Server circuit thread created" );
}
const char * serverCircuit::pName ()
{
return "server circuit";
}
static const char *mechName(int mech)
{
static const struct {
int mech;
const char *name;
} mechs[] = {
{-1, "Unknown shutdown mechanism" },
{esscimqi_socketCloseRequired, "esscimqi_socketCloseRequired" },
{esscimqi_socketBothShutdownRequired, "esscimqi_socketBothShutdownRequired" },
{esscimqi_socketSigAlarmRequired, "esscimqi_socketSigAlarmRequired" }
};
for (unsigned i=0; i < (sizeof(mechs) / sizeof(mechs[0])); ++i) {
if (mech == mechs[i].mech)
return mechs[i].name;
}
return "Unknown shutdown mechanism value";
}
MAIN(blockingSockTest)
{
testPlan(13);
osiSockAttach();
address addr;
memset ( (char *) & addr, 0, sizeof ( addr ) );
addr.ia.sin_family = AF_INET;
addr.ia.sin_addr.s_addr = htonl ( INADDR_LOOPBACK );
addr.ia.sin_port = 0;
server srv ( addr );
srv.start ();
addr = srv.addr ();
clientCircuit client ( addr );
epicsThreadSleep ( 1.0 );
testOk ( ! client.recvWakeupDetected (), "Client is asleep" );
testDiag("Trying Shutdown mechanism");
client.shutdown ();
epicsThreadSleep ( 1.0 );
int mech = -1;
if ( client.recvWakeupDetected () ) {
mech = esscimqi_socketBothShutdownRequired;
testDiag("Shutdown succeeded");
}
else {
testDiag("Trying Close mechanism");
client.close ();
epicsThreadSleep ( 1.0 );
if ( client.recvWakeupDetected () ) {
mech = esscimqi_socketCloseRequired;
testDiag("Close succeeded");
}
}
testDiag("This OS behaves like \"%s\".", mechName(mech));
int query = epicsSocketSystemCallInterruptMechanismQuery ();
if (! testOk(mech == query, "Declared mechanism works") )
testDiag("epicsSocketSystemCallInterruptMechanismQuery returned \"%s\"",
mechName(query));
srv.stop ();
epicsThreadSleep ( 1.0 );
osiSockRelease();
return testDone();
}