对于网络的调用的包装一直都是做为一个工程的支撑,使用ACE固然是不错的选择,但是对于一个小工程来说未免有些喧宾夺主的味道。ACE中使用面向对象的技术去避免很多接口的误用,却造成了整个工程的规模变得很庞大难用。下面给出一个简单的实现。
SocketImp.h
#ifndef _SOCKETIMP_H_
#define _SOCKETIMP_H_
#include <string>
typedef int SOCKET;
class SocketImp
{
public:
SOCKET sock;
SocketImp();
~SocketImp();
bool isBound();
bool close();
bool setReuseAddress(bool on);
void setTimeout(int timeout);
static const int STREAM;
static const int DGRAM;
void setSocket(SOCKET value)
{
sock = value;
}
SOCKET getSocket()
{
return sock;
}
void setLocalAddress(const char *addr)
{
localAddr = (addr != NULL) ? addr : "";
//StripIPv6ScopeID(addr, localAddr);
}
const char *getLocalAddress()
{
return localAddr.c_str();
}
void setLocalPort(int port)
{
localPort = port;
}
int getLocalPort()
{
return localPort;
}
int getType()
{
return type;
}
protected:
void setType(int value)
{
type = value;
}
private:
int type;
std::string localAddr;
int localPort;
};
void SocketStartup();
void SocketCleanup();
#endif
SocketImp.cpp
#include <net/SocketImp.h>
#include <net/SocketUtil.h>
#include <net/HostInterface.h>
#include <util/Mutex.h>
#include <util/StringUtil.h>
#include <fcntl.h>
#include <signal.h>
// Constants
const int SocketImp::STREAM = 1;
const int SocketImp::DGRAM = 2;
// SocketInit/Close
static int socketCnt = 0;
static Mutex sockMutex;
void CyberNet::SocketStartup()
{
sockMutex.lock();
if (socketCnt == 0) {
signal(SIGPIPE, SIG_IGN);
}
socketCnt++;
sockMutex.unlock();
}
void CyberNet::SocketCleanup()
{
sockMutex.lock();
socketCnt--;
if (socketCnt <= 0) {
signal(SIGPIPE, SIG_DFL);
}
sockMutex.unlock();
}
// Constructor/Destructor
SocketImp::SocketImp()
{
SocketStartup();
setType(0);
setLocalAddress("");
setLocalPort(0);
setSocket(-1);
}
SocketImp::~SocketImp()
{
SocketCleanup();
}
bool SocketImp::isBound()
{
return (0 <= sock) ? true : false;
}
// close
bool SocketImp::close()
{
if (isBound() == true)
return true;
int flag = fcntl(sock, F_GETFL, 0);
if (0 <= flag)
fcntl(sock, F_SETFL, flag | O_NONBLOCK);
shutdown(sock, 2);
::close(sock);
setSocket(-1);
return true;
}
// Socket Option
bool SocketImp::setReuseAddress(bool flag)
{
int sockOptRet;
int optval = (flag == true) ? 1 : 0;
sockOptRet = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval));
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (const char *)&optval, sizeof(optval));
return (sockOptRet == 0) ? true : false;
}
void SocketImp::setTimeout(int timeout)
{
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char *)&timeout, sizeof(timeout));
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout));
}
以上是Socket的一个代理的基类,下面是流式的Socket
Socket.h
#ifndef _SOCKET_H_
#define _SOCKET_H_
#include <net/SocketImp.h>
class Socket : public SocketImp
{
public:
Socket();
~Socket();
bool listen();
bool bind(int port, const char *addr);
bool accept(Socket *socket);
bool connect(const char *addr, int port);
int send(const char *cmd, int cmdLen);
int send(const char *cmd);
int recv(char *buffer, int bufferLen);
};
#endif
Socket.cpp
#include <net/Socket.h>
#include <util/StringUtil.h>
#include <util/TimeUtil.h>
#include <net/SocketUtil.h>
#include <stdio.h>
// Socket
Socket::Socket()
{
setType(STREAM);
}
Socket::~Socket()
{
close();
}
// listen
bool Socket::listen()
{
int ret = ::listen(sock, SOMAXCONN);
return (ret == 0) ? true: false;
}
// bind
bool Socket::bind(int bindPort, const char *bindAddr)
{
setLocalAddress("");
setLocalPort(0);
if (bindPort <= 0 || bindAddr == NULL)
return false;
struct addrinfo *addrInfo;
if (toSocketAddrInfo(SOCK_STREAM, bindAddr, bindPort, &addrInfo, true) == false)
return false;
sock = socket(addrInfo->ai_family, addrInfo->ai_socktype, 0);
if (sock == -1) {
close();
return false;
}
int ret = ::bind(sock, addrInfo->ai_addr, addrInfo->ai_addrlen);
freeaddrinfo(addrInfo);
if (ret != 0)
return false;
setLocalAddress(bindAddr);
setLocalPort(bindPort);
return true;
}
// accept
bool Socket::accept(Socket *socket)
{
SOCKET clientSock;
struct sockaddr_storage sockClientAddr;
socklen_t nLength = sizeof(sockClientAddr);
clientSock = ::accept(sock, (struct sockaddr *)&sockClientAddr, &nLength);
if (clientSock < 0)
return false;
socket->setSocket(clientSock);
socket->setLocalAddress(getLocalAddress());
socket->setLocalPort(getLocalPort());
return true;
}
// connect
bool Socket::connect(const char *addr, int port)
{
struct addrinfo *toaddrInfo;
if (toSocketAddrInfo(SOCK_STREAM, addr, port, &toaddrInfo, true) == false)
return false;
if (isBound() == false)
sock = socket(toaddrInfo->ai_family, toaddrInfo->ai_socktype, 0);
int ret = ::connect(sock, toaddrInfo->ai_addr, toaddrInfo->ai_addrlen);
freeaddrinfo(toaddrInfo);
return (ret == 0) ? true : false;
}
// recv
int Socket::recv(char *buffer, int bufferLen)
{
int recvLen;
recvLen = ::recv(sock, buffer, bufferLen, 0);
return recvLen;
}
// send
const int CG_NET_SOCKET_SEND_RETRY_CNT = 10;
const int CG_NET_SOCKET_SEND_RETRY_WAIT_MSEC = 1000;
int Socket::send(const char *cmd, int cmdLen)
{
if (cmdLen <= 0)
return 0;
int nTotalSent = 0;
int cmdPos = 0;
int retryCnt = 0;
do {
int nSent = ::send(sock, cmd + cmdPos, cmdLen, 0);
if (nSent <= 0) {
retryCnt++;
if (CG_NET_SOCKET_SEND_RETRY_CNT < retryCnt)
break;
WaitRandom(CG_NET_SOCKET_SEND_RETRY_WAIT_MSEC);
continue;
}
nTotalSent += nSent;
cmdPos += nSent;
cmdLen -= nSent;
retryCnt = 0;
} while (0 < cmdLen);
return nTotalSent;
}
int Socket::send(const char *cmd)
{
return send(cmd, StringLength(cmd));
}