Support non-blocking send and recv

This commit is contained in:
Kirigaya Kazuto 2018-09-09 23:34:11 +08:00
parent 34ab73a7c4
commit 6c46214f6b
2 changed files with 244 additions and 11 deletions

222
gsock.cpp
View File

@ -129,6 +129,7 @@ enum gerrno
InProgress,
Already,
IsConnected,
Interrupted,
};
gerrno TranslateNativeErrToGErr(int native_errcode)
@ -144,7 +145,10 @@ gerrno TranslateNativeErrToGErr(int native_errcode)
return gerrno::Already;
case WSAEISCONN:
return gerrno::IsConnected;
case WSAEINTR:
return gerrno::Interrupted;
#else
case EAGAIN:
case EWOULDBLOCK:
return gerrno::WouldBlock;
case EINPROGRESS:
@ -153,6 +157,8 @@ gerrno TranslateNativeErrToGErr(int native_errcode)
return gerrno::Already;
case EISCONN:
return gerrno::IsConnected;
case EINTR:
return gerrno::Interrupted;
#endif
default:
myliblog("Unknown Error Code: %d\n", native_errcode);
@ -393,9 +399,11 @@ void NBConnectResult::_impl::update()
{
status = 2;
}
else
else // ret == -1
{
gerrno err = TranslateNativeErrToGErr(GetNativeErrCode());
errcode = err;
if (err == gerrno::InProgress || err == gerrno::WouldBlock || err == gerrno::Already)
{
status = 1;
@ -410,7 +418,7 @@ void NBConnectResult::_impl::update()
}
}
myliblog("Status updated to %d\n", status);
myliblog("NBConnectResult Status updated to %d\n", status);
}
NBConnectResult::NBConnectResult() : _p(new _impl)
@ -426,7 +434,6 @@ bool NBConnectResult::isFinished()
bool NBConnectResult::isConnected()
{
_p->update();
return (_p->status == 2);
}
@ -440,20 +447,196 @@ void NBConnectResult::wait()
while (!isFinished());
}
struct NBTransferResult::_impl
struct NBSendResult::_impl
{
int sfd;
void* ptr;
int datasz;
const char* ptr;
int total;
int done;
// 0: Not started.
// 1: Data is being sent
// 2: Data sent without error.
// 3: Error occurs.
int status;
int errcode;
void update();
};
NBTransferResult::NBTransferResult() : _p(new _impl)
void NBSendResult::_impl::update()
{
int ret = send(sfd, ptr + done, total - done, 0);
if (ret > 0)
{
done += ret;
if (done == total)
{
status = 2;
}
else
{
status = 1;
}
}
else if (ret == 0)
{
status = 3;
errcode = 0;
}
else // ret == -1
{
gerrno err = TranslateNativeErrToGErr(GetNativeErrCode());
errcode = err;
if (err == gerrno::WouldBlock)
{
status = 1;
}
else
{
status = 3;
}
}
myliblog("NBSendResult status updated to %d\n", status);
}
NBSendResult::NBSendResult() : _p(new _impl)
{
_p->status = 0;
}
bool NBSendResult::isFinished()
{
_p->update();
return (_p->status > 1);
}
void NBSendResult::wait()
{
while (!isFinished());
}
bool NBSendResult::isSuccess()
{
return (_p->status == 2);
}
int NBSendResult::getBytesDone()
{
return _p->done;
}
int NBSendResult::getErrCode()
{
return _p->errcode;
}
struct NBRecvResult::_impl
{
int sfd;
char* ptr;
int maxsz;
int done;
// When work together with epoll at ET mode, setting this flag can avoid infinite EAGAIN recv loop.
bool stopAtEdge;
// 0: Not started.
// 1: Data is being sent
// 2: Data sent without error.
// 3: Error occurs.
int status;
int errcode;
void update();
};
void NBRecvResult::_impl::update()
{
int ret = recv(sfd, ptr + done, maxsz - done, 0);
if (ret > 0)
{
done += ret;
if (done == maxsz)
{
status = 2;
}
else
{
status = 1;
}
}
else if (ret == 0)
{
status = 3;
errcode = 0;
}
else // ret == -1
{
gerrno err = TranslateNativeErrToGErr(GetNativeErrCode());
errcode = err;
if (err == gerrno::WouldBlock)
{
if (stopAtEdge)
{
status = 2;
}
else
{
status = 1;
}
}
else
{
status = 3;
}
}
myliblog("NBRecvResult status updated to %d\n", status);
}
NBRecvResult::NBRecvResult() : _p(new _impl)
{
_p->status = 0;
}
void NBRecvResult::setStopAtEdge(bool flag)
{
_p->stopAtEdge = flag;
}
bool NBRecvResult::isFinished()
{
_p->update();
return (_p->status > 1);
}
void NBRecvResult::wait()
{
while (!isFinished());
}
bool NBRecvResult::isSuccess()
{
return (_p->status == 2);
}
int NBRecvResult::getBytesDone()
{
return _p->done;
}
int NBRecvResult::getErrCode()
{
return _p->errcode;
}
NBConnectResult sock::connect_nb(const std::string& IPStr, int Port)
{
NBConnectResult res;
@ -523,6 +706,31 @@ int sock::recv(void* Buffer,int MaxToRecv)
return ::recv(_vp->sfd,(char*)Buffer,MaxToRecv,0);
}
NBSendResult sock::send_nb(const void* Buffer, int Length)
{
NBSendResult res;
res._p->ptr = (const char*)Buffer;
res._p->total = Length;
res._p->done = 0;
res._p->sfd = _vp->sfd;
res._p->update();
return res;
}
NBRecvResult sock::recv_nb(void* Buffer, int MaxToRecv)
{
NBRecvResult res;
res._p->ptr = (char*)Buffer;
res._p->maxsz = MaxToRecv;
res._p->done = 0;
res._p->stopAtEdge = false;
res._p->sfd = _vp->sfd;
res._p->update();
return res;
}
int sock::getsendtime(int& _out_Second, int& _out_uSecond)
{
#ifdef _WIN32

33
gsock.h
View File

@ -71,12 +71,37 @@ private:
friend class sock;
};
class NBTransferResult
class NBSendResult
{
public:
NBTransferResult();
NBSendResult();
// Is the operation finished.
bool isFinished();
// Wait until all data is sent.
void wait();
// Is all data sent successfully.
bool isSuccess();
int getBytesDone();
int getErrCode();
private:
struct _impl;
std::shared_ptr<_impl> _p;
friend class sock;
};
class NBRecvResult
{
public:
NBRecvResult();
void setStopAtEdge(bool flag);
bool isFinished();
void wait();
bool isSuccess();
int getBytesDone();
int getErrCode();
@ -104,8 +129,8 @@ public:
// return what send() and recv() call returns.
int send(const void* Buffer,int Length);
int recv(void* Buffer, int MaxToRecv);
NBTransferResult send_nb(const void* Buffer, int Length);
NBTransferResult recv_nb(void* Buffer, int MaxToRecv);
NBSendResult send_nb(const void* Buffer, int Length);
NBRecvResult recv_nb(void* Buffer, int MaxToRecv);
// Return:
// GSOCK_OK