📄 socketex.cpp
字号:
////////////////////////////////////////////////////////////////////////////////
//
////////////////////////////////////////////////////////////////////////////////
// Author : 黎达文
// Description :
//
// Version :
//
// Standard include files :
//
// Start Date : 2003年8月16日
//
// Change Log : 2003年8月16日 by 黎达文 -- Created
////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "socketex.h"
#include "patterns/util/util.h"
#include <sys/timeb.h>
#include <time.h>
#if defined( WIN32 )
# define timeb _timeb
# define ftime _ftime
#endif
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define M_Int2Str(x) #x
#define M_SymToStr(x) M_Int2Str(x)
#define remind(desc) message(__FILE__"("M_SymToStr(__LINE__)") :"#desc)
#define M_POS __FILE__ "(" M_SymToStr( __LINE__ ) "):"
namespace acl
{
//////////////////////////////////////////////////////////////////////////////
BufferedSocket::BufferedSocket()
{
m_cur = 0;
m_cnt = 0;
stk::bzero(m_buf, 0);
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : ctor
//////////////////////////////////////////////////////////////////////////////
AbstractSocket::AbstractSocket(SOCKET_TYPE type)
: m_type(type)
, m_socket(INVALID_SOCKET)
{
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : type conversion operator
// Return Value : SOCKET
//////////////////////////////////////////////////////////////////////////////
AbstractSocket::operator SOCKET() const
{
return m_socket;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : attach
// Description : attach an existing SOCKET to current object
// Input Parameters : the SOCKET
//////////////////////////////////////////////////////////////////////////////
void AbstractSocket::attach(SOCKET sock)
{
close();
m_socket = sock;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : detach
// Description : return the SOCKE to the user, current object will not own the SOCKET
// Return Value : the SOCKET
//////////////////////////////////////////////////////////////////////////////
SOCKET AbstractSocket::detach()
{
assert(m_socket != INVALID_SOCKET);
SOCKET tmp = m_socket;
m_socket = INVALID_SOCKET;
return tmp;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : create
// Description : create the SOCKET
// Return Value : success/fail
//////////////////////////////////////////////////////////////////////////////
bool AbstractSocket::create()
{
assert(m_socket == INVALID_SOCKET);
if(m_type == TCP_SOCKET)
m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
else if(m_type == UDP_SOCKET)
m_socket = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
else
m_socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
return m_socket != INVALID_SOCKET;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : close
// Description : close the socket if it had opened, I will not try to shutdown the socket!
//////////////////////////////////////////////////////////////////////////////
void AbstractSocket::close()
{
if(m_socket != INVALID_SOCKET)
{
::closesocket(m_socket);
m_socket = INVALID_SOCKET;
}
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : send
// Description : send buffer just like the SOCKET API
// Input Parameters : outbuf -- buffer will be send
// want -- how much byte you want to be send
// as you known, SOCKET API may not send all data you want!
// if you want to make sure send all data, use sendAll method
// Return Value : If no error occurs, recv returns the number of bytes received.
// If the connection has been gracefully closed, the return value is zero.
/// Otherwise, a value of SOCKET_ERROR is returned
//////////////////////////////////////////////////////////////////////////////
int AbstractSocket::send(const char *outbuf, int want) const
{
assert(m_socket!=INVALID_SOCKET);
assert(outbuf);
assert(want>0);
return ::send(m_socket, outbuf, want, 0);
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : recv
// Description : recv data just like the SOCKET API
// Input Parameters : inbuf -- where data will be store
// : size -- size of inbuf
// Return Value : If no error occurs, recv returns the number of bytes received.
// If the connection has been gracefully closed, the return value is zero.
/// Otherwise, a value of SOCKET_ERROR is returned
//////////////////////////////////////////////////////////////////////////////
int AbstractSocket::recv(char *inbuf, int size) const
{
assert(m_socket!=INVALID_SOCKET);
assert(inbuf);
assert(size>0);
return ::recv(m_socket, inbuf, size, 0);
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : sendAll
// Description : try to send all the outbuf
// Input Parameters : outbuf -- the data which want to be send
// want -- how many bytes you wan to send
// Return Value : If no error occurs, recv returns the number of bytes send.
// If the connection has been gracefully closed, the return value is zero.
/// Otherwise, a value of SOCKET_ERROR is returned
//////////////////////////////////////////////////////////////////////////////
int AbstractSocket::sendAll(const char *outbuf, int want) const
{
assert(m_socket!=INVALID_SOCKET);
assert(outbuf);
assert(want>0);
int left;
int idx;
int ret;
left = want;
idx = 0;
while(left > 0)
{
ret = ::send(m_socket, &outbuf[idx], left, 0);
// send error, or has been close by peer
if(ret == 0 || ret == SOCKET_ERROR)
break;
left -= ret;
idx += ret;
}
// left == 0 means we do the job successfully
return left == 0 ? idx : ret;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : recvAll
// Description : try to recv all data we want
// Input Parameters : inbuf -- where to store data
// want -- how much data we want
// Return Value : If no error occurs, recv returns the number of bytes received.
// If the connection has been gracefully closed, the return value is zero.
/// Otherwise, a value of SOCKET_ERROR is returned
//////////////////////////////////////////////////////////////////////////////
int AbstractSocket::recvAll(char *inbuf, int want) const
{
assert(m_socket!=INVALID_SOCKET);
assert(inbuf);
assert(want>0);
int ret;
int read = 0;
while(read < want)
{
ret = recv(&inbuf[read], want-read);
// send error, or has been close by peer
if(ret == 0 || ret == SOCKET_ERROR)
break;
read += ret;
}
// read == want means we do the job successfully
return read == want ? read : ret;
}
static unsigned long timediff(timeb &first, timeb &second)
{
unsigned long elap = (second.time - first.time) * 1000;
if ( second.millitm > first.millitm )
elap += second.millitm - first.millitm;
else
elap -= first.millitm - second.millitm;
return elap;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : recvAsyn
// Description : recv in asynchronous way, with timeout
// Input Parameters : inbuf -- where to store data
// want -- how much data we want
// timeout -- timeout(unit: s)
// Return Value : If no error occurs, recv returns the number of bytes received.
// If the connection has been gracefully closed, the return value is zero.
// Otherwise, a value of SOCKET_ERROR is returned
// Timeout will look as SOCKET_ERROR
//////////////////////////////////////////////////////////////////////////////
int AbstractSocket::recvAsyn(char *inbuf, int want, DWORD timeout) const
{
assert(m_socket!=INVALID_SOCKET);
assert(inbuf);
assert(want>0);
int ret;
int read;
timeb first_tick, second_tick;
timeval tv = {1, 0}; // default is check timeout per 1 second
timeval *ptv = &tv;
if(timeout == INFINITE)
ptv = NULL; // NULL indicate select recvReady wait for ever
ftime( &first_tick );
read = 0;
while(read < want)
{
if(recvReady(ptv))
{
ret = recv(inbuf+read, want-read);
if(ret == 0 || ret == SOCKET_ERROR)
break;
// if no error, ret is the real size we had recv
read += ret;
}
ftime( &second_tick ) ;
// check for timeout
unsigned long elap = timediff(second_tick, first_tick);
if(elap > timeout)
{
ret = SOCKET_ERROR;
break;
}
}
// read == want means we do the job successfully
return read == want ? read : ret;
}
//////////////////////////////////////////////////////////////////////////////
// Function Name : getline
// Description : get a line from socket
// method will try to read a block data from socket
// and save it into BufferedSocket. and then find a line
// and return. That method will avoid call recv function one by one
// Input Parameters : bs : must pass this object in a serial read action
// Return Value : like the ::recv
//////////////////////////////////////////////////////////////////////////////
int AbstractSocket::getline(BufferedSocket &bs,
std::string &line,
const char *eof) const
{
const char *eofBeg = eof;
const char *eofEnd = eof + strlen(eof);
char *pos;
char *end;
char *beg;
while(true)
{
// check whether we have any buffer
if(bs.m_cnt <= 0)
{
// recv a new buffer
bs.m_cnt = this->recv(bs.m_buf, BufferedSocket::BUF_SIZE);
// if socket error, return it directly
if(bs.m_cnt <= 0)
return bs.m_cnt;
// set the string
bs.m_cur = bs.m_buf;
}
// search the "eof" position
end = bs.m_cur+bs.m_cnt;
beg = bs.m_cur;
pos = std::search(beg, end,
eofBeg, eofEnd);
line.append(beg, pos);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -