caConstants.h: removing needless declarations.
blockingUDPTransport.*: implementation - work in progress remote.h: added file inetAddressUtil.*: added 'inetAddressToString' function and fixed 'getSocketAddressList' declaration. inetAddressUtilsTest.c: using the new function. Makefile: added remote.h
This commit is contained in:
@@ -32,6 +32,7 @@ LIBSRCS += ChannelAccessFactory.cpp
|
||||
|
||||
|
||||
SRC_DIRS += $(PVACCESS)/remote
|
||||
INC += remote.h
|
||||
INC += blockingUDPTransport.h
|
||||
LIBSRCS += blockingUDPTransport.cpp
|
||||
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
#ifndef CONSTANTS_H_
|
||||
#define CONSTANTS_H_
|
||||
|
||||
#include <pvIntrospect.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
@@ -71,7 +67,7 @@ namespace epics {
|
||||
const int32 UNREASONABLE_CHANNEL_NAME_LENGTH = 500;
|
||||
|
||||
/** Invalid data type. */
|
||||
const int16 INVALID_DATA_TYPE = (short)0xFFFF;
|
||||
const int16 INVALID_DATA_TYPE = (int16)0xFFFF;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "blockingUDPTransport.h"
|
||||
|
||||
#include "caConstants.h"
|
||||
#include "inetAddressUtil.h"
|
||||
|
||||
/* pvData */
|
||||
#include <byteBuffer.h>
|
||||
@@ -18,9 +19,9 @@
|
||||
#include <osiSock.h>
|
||||
#include <errlog.h>
|
||||
|
||||
/* standard */
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
@@ -28,7 +29,7 @@ namespace epics {
|
||||
|
||||
BlockingUDPTransport::BlockingUDPTransport(SOCKET channel,
|
||||
osiSockAddr* bindAddress, osiSockAddr* sendAddresses,
|
||||
short remoteTransportRevision) {
|
||||
short remoteTransportRevision) {
|
||||
this->channel = channel;
|
||||
this->bindAddress = bindAddress;
|
||||
this->sendAddresses = sendAddresses;
|
||||
@@ -42,6 +43,7 @@ namespace epics {
|
||||
sendBuffer = new ByteBuffer(MAX_UDP_SEND);
|
||||
|
||||
ignoredAddresses = NULL;
|
||||
sendTo = NULL;
|
||||
closed = false;
|
||||
lastMessageStartPosition = 0;
|
||||
}
|
||||
@@ -56,14 +58,35 @@ namespace epics {
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::close(bool forced) {
|
||||
if (closed)
|
||||
return;
|
||||
if(closed) return;
|
||||
closed = true;
|
||||
|
||||
if (bindAddress != NULL)
|
||||
errlogSevPrintf( errlogInfo, "UDP connection to %d closed.", *bindAddress);
|
||||
if(bindAddress!=NULL) errlogSevPrintf(errlogInfo,
|
||||
"UDP connection to %s closed.", inetAddressToString(
|
||||
bindAddress).c_str());
|
||||
|
||||
//std::fclose(channel);
|
||||
// TODO: finish implementation
|
||||
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::enqueueSendRequest(TransportSender* sender) {
|
||||
// TODO implement
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::startMessage(int8 command,
|
||||
int ensureCapacity) {
|
||||
lastMessageStartPosition = sendBuffer->getPosition();
|
||||
sendBuffer->putShort(CA_MAGIC_AND_VERSION);
|
||||
sendBuffer->putByte(0); // data
|
||||
sendBuffer->putByte(command); // command
|
||||
sendBuffer->putInt(0); // temporary zero payload
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::endMessage() {
|
||||
int32 data = lastMessageStartPosition+(16/8+2);
|
||||
sendBuffer->put((char*)&data, sendBuffer->getPosition()
|
||||
-lastMessageStartPosition-CA_MESSAGE_HEADER_SIZE,
|
||||
sizeof(int32));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifndef BLOCKINGUDPTRANSPORT_H_
|
||||
#define BLOCKINGUDPTRANSPORT_H_
|
||||
|
||||
#include "remote.h"
|
||||
#include "caConstants.h"
|
||||
|
||||
#include <noDefaultMethods.h>
|
||||
#include <byteBuffer.h>
|
||||
|
||||
@@ -17,24 +20,120 @@
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
class BlockingUDPTransport : public epics::pvData::NoDefaultMethods {
|
||||
class BlockingUDPTransport : public epics::pvData::NoDefaultMethods,
|
||||
public Transport,
|
||||
public TransportSendControl {
|
||||
public:
|
||||
BlockingUDPTransport(SOCKET channel, osiSockAddr* bindAddress,
|
||||
osiSockAddr* sendAddresses, short remoteTransportRevision);
|
||||
|
||||
~BlockingUDPTransport();
|
||||
virtual ~BlockingUDPTransport();
|
||||
|
||||
bool isClosed() {
|
||||
virtual bool isClosed() const {
|
||||
return closed;
|
||||
}
|
||||
|
||||
virtual const osiSockAddr* getRemoteAddress() const {
|
||||
return socketAddress;
|
||||
}
|
||||
|
||||
virtual const String getType() const {
|
||||
return String("UDP");
|
||||
}
|
||||
|
||||
virtual int8 getMajorRevision() const {
|
||||
return CA_MAJOR_PROTOCOL_REVISION;
|
||||
}
|
||||
|
||||
virtual int8 getMinorRevision() const {
|
||||
return CA_MINOR_PROTOCOL_REVISION;
|
||||
}
|
||||
|
||||
virtual int getReceiveBufferSize() const {
|
||||
return receiveBuffer->getSize();
|
||||
}
|
||||
|
||||
virtual int getSocketReceiveBufferSize() const {
|
||||
// Get value of the SO_RCVBUF option for this DatagramSocket,
|
||||
// that is the buffer size used by the platform for input on
|
||||
// this DatagramSocket.
|
||||
|
||||
// TODO: real implementation
|
||||
return MAX_UDP_RECV;
|
||||
}
|
||||
|
||||
virtual int16 getPriority() const {
|
||||
return CA_DEFAULT_PRIORITY;
|
||||
}
|
||||
|
||||
virtual void setRemoteMinorRevision(int8 minor) {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void setRemoteTransportReceiveBufferSize(
|
||||
int receiveBufferSize) {
|
||||
// noop for UDP (limited by 64k; MAX_UDP_SEND for CA)
|
||||
}
|
||||
|
||||
virtual void setRemoteTransportSocketReceiveBufferSize(
|
||||
int socketReceiveBufferSize) {
|
||||
// noop for UDP (limited by 64k; MAX_UDP_SEND for CA)
|
||||
}
|
||||
|
||||
virtual void aliveNotification() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void changedTransport() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual bool isVerified() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void verified() {
|
||||
// noop
|
||||
}
|
||||
|
||||
virtual void enqueueSendRequest(TransportSender* sender);
|
||||
|
||||
void start();
|
||||
void close(bool forced);
|
||||
|
||||
virtual void close(bool forced);
|
||||
|
||||
virtual void ensureData(int size) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
virtual void startMessage(int8 command, int ensureCapacity);
|
||||
virtual void endMessage();
|
||||
|
||||
virtual void flush(bool lastMessageCompleted) {
|
||||
// noop since all UDP requests are sent immediately
|
||||
}
|
||||
|
||||
virtual void setRecipient(const osiSockAddr* sendTo) {
|
||||
this->sendTo = sendTo;
|
||||
}
|
||||
|
||||
virtual void flushSerializeBuffer() {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
virtual void ensureBuffer(int size) {
|
||||
// noop
|
||||
}
|
||||
|
||||
protected:
|
||||
bool closed;
|
||||
|
||||
virtual void processRead();
|
||||
|
||||
private:
|
||||
bool processBuffer(osiSockAddr* fromAddress,
|
||||
ByteBuffer* receiveBuffer);
|
||||
|
||||
// Context only used for logging in this class
|
||||
|
||||
/**
|
||||
@@ -62,6 +161,8 @@ namespace epics {
|
||||
*/
|
||||
osiSockAddr* ignoredAddresses;
|
||||
|
||||
const osiSockAddr* sendTo;
|
||||
|
||||
/**
|
||||
* Receive buffer.
|
||||
*/
|
||||
|
||||
188
pvAccessApp/remote/remote.h
Normal file
188
pvAccessApp/remote/remote.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* remote.h
|
||||
*
|
||||
* Created on: Dec 21, 2010
|
||||
* Author: user
|
||||
*/
|
||||
|
||||
#ifndef REMOTE_H_
|
||||
#define REMOTE_H_
|
||||
|
||||
#include <serialize.h>
|
||||
#include <pvType.h>
|
||||
#include <byteBuffer.h>
|
||||
|
||||
#include <osiSock.h>
|
||||
#include <osdSock.h>
|
||||
|
||||
namespace epics {
|
||||
namespace pvAccess {
|
||||
|
||||
using namespace epics::pvData;
|
||||
|
||||
enum ProtocolType {
|
||||
TCP, UDP, SSL
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining transport send control.
|
||||
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
|
||||
*/
|
||||
class TransportSendControl : public SerializableControl {
|
||||
public:
|
||||
virtual void startMessage(int8 command, int ensureCapacity) =0;
|
||||
virtual void endMessage() =0;
|
||||
|
||||
virtual void flush(bool lastMessageCompleted) =0;
|
||||
|
||||
virtual void setRecipient(const osiSockAddr* sendTo) =0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining transport sender (instance sending data over transport).
|
||||
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
|
||||
* @version $Id: TransportSender.java,v 1.1 2010/05/03 14:45:39 mrkraimer Exp $
|
||||
*/
|
||||
class TransportSender {
|
||||
public:
|
||||
/**
|
||||
* Called by transport.
|
||||
* By this call transport gives callee ownership over the buffer.
|
||||
* Calls on <code>TransportSendControl</code> instance must be made from
|
||||
* calling thread. Moreover, ownership is valid only for the time of call
|
||||
* of this method.
|
||||
* NOTE: these limitations allows efficient implementation.
|
||||
*/
|
||||
virtual void
|
||||
send(ByteBuffer* buffer, TransportSendControl* control) =0;
|
||||
|
||||
virtual void lock() =0;
|
||||
virtual void unlock() =0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface defining transport (connection).
|
||||
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
|
||||
* @version $Id: Transport.java,v 1.1 2010/05/03 14:45:39 mrkraimer Exp $
|
||||
*/
|
||||
class Transport : public DeserializableControl {
|
||||
public:
|
||||
/**
|
||||
* Get remote address.
|
||||
* @return remote address.
|
||||
*/
|
||||
virtual const osiSockAddr* getRemoteAddress() const =0;
|
||||
|
||||
/**
|
||||
* Get protocol type (tcp, udp, ssl, etc.).
|
||||
* @return protocol type.
|
||||
*/
|
||||
virtual const String getType() const =0;
|
||||
|
||||
/**
|
||||
* Get context transport is living in.
|
||||
* @return context transport is living in.
|
||||
*/
|
||||
//public Context getContext();
|
||||
|
||||
/**
|
||||
* Transport protocol major revision.
|
||||
* @return protocol major revision.
|
||||
*/
|
||||
virtual int8 getMajorRevision() const =0;
|
||||
|
||||
/**
|
||||
* Transport protocol minor revision.
|
||||
* @return protocol minor revision.
|
||||
*/
|
||||
virtual int8 getMinorRevision() const =0;
|
||||
|
||||
/**
|
||||
* Get receive buffer size.
|
||||
* @return receive buffer size.
|
||||
*/
|
||||
virtual int getReceiveBufferSize() const =0;
|
||||
|
||||
/**
|
||||
* Get socket receive buffer size.
|
||||
* @return socket receive buffer size.
|
||||
*/
|
||||
virtual int getSocketReceiveBufferSize() const =0;
|
||||
|
||||
/**
|
||||
* Transport priority.
|
||||
* @return protocol priority.
|
||||
*/
|
||||
virtual int16 getPriority() const =0;
|
||||
|
||||
/**
|
||||
* Set remote transport protocol minor revision.
|
||||
* @param minor protocol minor revision.
|
||||
*/
|
||||
virtual void setRemoteMinorRevision(int8 minor) =0;
|
||||
|
||||
/**
|
||||
* Set remote transport receive buffer size.
|
||||
* @param receiveBufferSize receive buffer size.
|
||||
*/
|
||||
virtual void setRemoteTransportReceiveBufferSize(
|
||||
int receiveBufferSize) =0;
|
||||
|
||||
/**
|
||||
* Set remote transport socket receive buffer size.
|
||||
* @param socketReceiveBufferSize remote socket receive buffer size.
|
||||
*/
|
||||
virtual void setRemoteTransportSocketReceiveBufferSize(
|
||||
int socketReceiveBufferSize) =0;
|
||||
|
||||
/**
|
||||
* Notification transport that is still alive.
|
||||
*/
|
||||
virtual void aliveNotification() =0;
|
||||
|
||||
/**
|
||||
* Notification that transport has changed.
|
||||
*/
|
||||
virtual void changedTransport() =0;
|
||||
|
||||
/**
|
||||
* Get introspection registry for transport.
|
||||
* @return <code>IntrospectionRegistry</code> instance.
|
||||
*/
|
||||
//virtual IntrospectionRegistry getIntrospectionRegistry() =0;
|
||||
|
||||
/**
|
||||
* Close transport.
|
||||
* @param force flag indicating force-full (e.g. remote disconnect) close.
|
||||
*/
|
||||
virtual void close(bool force) =0;
|
||||
|
||||
/**
|
||||
* Check connection status.
|
||||
* @return <code>true</code> if connected.
|
||||
*/
|
||||
virtual bool isClosed() const =0;
|
||||
|
||||
/**
|
||||
* Get transport verification status.
|
||||
* @return verification flag.
|
||||
*/
|
||||
virtual bool isVerified() const =0;
|
||||
|
||||
/**
|
||||
* Notify transport that it is has been verified.
|
||||
*/
|
||||
virtual void verified() =0;
|
||||
|
||||
/**
|
||||
* Enqueue send request.
|
||||
* @param sender
|
||||
*/
|
||||
virtual void enqueueSendRequest(TransportSender* sender) =0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* REMOTE_H_ */
|
||||
@@ -4,17 +4,23 @@
|
||||
* Created on: Nov 12, 2010
|
||||
* Author: Miha Vitorovic
|
||||
*/
|
||||
|
||||
/* pvAccess */
|
||||
#include "inetAddressUtil.h"
|
||||
|
||||
#include <vector>
|
||||
/* pvData */
|
||||
#include <byteBuffer.h>
|
||||
|
||||
/* EPICSv3 */
|
||||
#include <osiSock.h>
|
||||
#include <ellLib.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsException.h>
|
||||
|
||||
/* standard */
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <epicsAssert.h>
|
||||
#include <byteBuffer.h>
|
||||
#include <epicsException.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
@@ -204,5 +210,20 @@ namespace epics {
|
||||
return iav;
|
||||
}
|
||||
|
||||
const String inetAddressToString(const osiSockAddr *addr,
|
||||
bool displayHex) {
|
||||
stringstream saddr;
|
||||
|
||||
saddr<<(int)((addr->ia.sin_addr.s_addr)>>24)<<'.';
|
||||
saddr<<((int)((addr->ia.sin_addr.s_addr)>>16)&0xFF)<<'.';
|
||||
saddr<<((int)((addr->ia.sin_addr.s_addr)>>8)&0xFF)<<'.';
|
||||
saddr<<((int)(addr->ia.sin_addr.s_addr)&0xFF);
|
||||
if(addr->ia.sin_port>0) saddr<<":"<<addr->ia.sin_port;
|
||||
if(displayHex) saddr<<" ("<<hex<<((uint32_t)(
|
||||
addr->ia.sin_addr.s_addr))<<")";
|
||||
|
||||
return saddr.str();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ namespace epics {
|
||||
* @param address address to encode.
|
||||
*/
|
||||
void
|
||||
encodeAsIPv6Address(ByteBuffer* buffer,
|
||||
const osiSockAddr* address);
|
||||
encodeAsIPv6Address(ByteBuffer* buffer, const osiSockAddr* address);
|
||||
|
||||
/**
|
||||
* Convert an integer into an IPv4 INET address.
|
||||
@@ -69,17 +68,10 @@ namespace epics {
|
||||
* @return array of <code>InetSocketAddress</code>.
|
||||
*/
|
||||
InetAddrVector* getSocketAddressList(String list, int defaultPort,
|
||||
const InetAddrVector* appendList);
|
||||
const InetAddrVector* appendList = NULL);
|
||||
|
||||
/**
|
||||
* Parse space delimited addresss[:port] string and return array of <code>InetSocketAddress</code>.
|
||||
* @param list space delimited addresss[:port] string.
|
||||
* @param defaultPort port take if not specified.
|
||||
* @return array of <code>InetSocketAddress</code>.
|
||||
*/
|
||||
InetAddrVector* getSocketAddressList(String list, int defaultPort) {
|
||||
return getSocketAddressList(list, defaultPort, NULL);
|
||||
}
|
||||
const String inetAddressToString(const osiSockAddr *addr,
|
||||
bool displayHex = false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <pvType.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
using namespace epics::pvAccess;
|
||||
@@ -20,19 +19,6 @@ using std::endl;
|
||||
using std::stringstream;
|
||||
using std::hex;
|
||||
|
||||
String inetAddressToString(osiSockAddr *addr) {
|
||||
stringstream saddr;
|
||||
|
||||
saddr<<(int)((addr->ia.sin_addr.s_addr)>>24)<<'.';
|
||||
saddr<<((int)((addr->ia.sin_addr.s_addr)>>16)&0xFF)<<'.';
|
||||
saddr<<((int)((addr->ia.sin_addr.s_addr)>>8)&0xFF)<<'.';
|
||||
saddr<<((int)(addr->ia.sin_addr.s_addr)&0xFF);
|
||||
if(addr->ia.sin_port>0) saddr<<":"<<addr->ia.sin_port;
|
||||
saddr<<" ("<<hex<<((uint32_t)(addr->ia.sin_addr.s_addr))<<")";
|
||||
|
||||
return saddr.str();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
InetAddrVector *vec;
|
||||
@@ -48,19 +34,19 @@ int main(int argc, char *argv[]) {
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==555);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0x7F000001);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
addr = vec->at(1);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==1234);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0x0A0A0C0B);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
addr = vec->at(2);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==555);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0xC0A80304);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
cout<<"\nPASSED!\n";
|
||||
|
||||
@@ -73,25 +59,25 @@ int main(int argc, char *argv[]) {
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==6789);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0xAC1037A0);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
addr = vec1->at(1);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==555);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0x7F000001);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
addr = vec1->at(2);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==1234);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0x0A0A0C0B);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
addr = vec1->at(3);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
assert(addr->ia.sin_port==555);
|
||||
assert(addr->ia.sin_addr.s_addr==(uint32_t)0xC0A80304);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
cout<<"\nPASSED!\n";
|
||||
|
||||
@@ -107,12 +93,12 @@ int main(int argc, char *argv[]) {
|
||||
cout<<"Testing \"intToIPv4Address\""<<endl;
|
||||
addr = intToIPv4Address(0x7F000001);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
delete addr;
|
||||
|
||||
addr = intToIPv4Address(0x0A0A0C0B);
|
||||
assert(addr->ia.sin_family==AF_INET);
|
||||
cout<<'\t'<<inetAddressToString(addr)<<endl;
|
||||
cout<<'\t'<<inetAddressToString(addr, true)<<endl;
|
||||
|
||||
cout<<"\nPASSED!\n";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user