fixed CTCPSocket::connect() to allow cancellation.
This commit is contained in:
parent
e2ee2371e0
commit
327af03d3d
|
@ -36,6 +36,7 @@ struct protoent FAR * (PASCAL FAR *CNetwork::getprotobynumber)(int proto);
|
|||
struct protoent FAR * (PASCAL FAR *CNetwork::getprotobyname)(const char FAR * name);
|
||||
int (PASCAL FAR *CNetwork::getsockerror)(void);
|
||||
int (PASCAL FAR *CNetwork::gethosterror)(void);
|
||||
int (PASCAL FAR *CNetwork::setblocking)(CNetwork::Socket s, bool blocking);
|
||||
|
||||
#if WINDOWS_LIKE
|
||||
|
||||
|
@ -210,6 +211,7 @@ CNetwork::init2(
|
|||
poll = poll2;
|
||||
read = read2;
|
||||
write = write2;
|
||||
setblocking = setblocking2;
|
||||
|
||||
s_networkModule = module;
|
||||
}
|
||||
|
@ -295,11 +297,19 @@ CNetwork::write2(Socket s, const void FAR* buf, size_t len)
|
|||
return send(s, buf, len, 0);
|
||||
}
|
||||
|
||||
int PASCAL FAR
|
||||
CNetwork::setblocking2(CNetwork::Socket s, bool blocking)
|
||||
{
|
||||
int flag = blocking ? 0 : 1;
|
||||
return ioctlsocket(s, FIONBIO, &flag);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if UNIX_LIKE
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
|
@ -352,6 +362,26 @@ mygethostname(char* name, int namelen)
|
|||
return gethostname(name, namelen);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
mysetblocking(CNetwork::Socket s, bool blocking)
|
||||
{
|
||||
int mode = fcntl(s, F_GETFL, 0);
|
||||
if (mode == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (blocking) {
|
||||
mode &= ~O_NDELAY;
|
||||
}
|
||||
else {
|
||||
mode |= O_NDELAY;
|
||||
}
|
||||
if (fcntl(s, F_SETFL, mode) < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int CNetwork::Error = -1;
|
||||
const CNetwork::Socket CNetwork::Null = -1;
|
||||
|
||||
|
@ -388,6 +418,7 @@ CNetwork::init()
|
|||
setfunc(getprotobyname, getprotobyname, struct protoent FAR * (PASCAL FAR *)(const char FAR * name));
|
||||
setfunc(getsockerror, myerrno, int (PASCAL FAR *)(void));
|
||||
setfunc(gethosterror, myherrno, int (PASCAL FAR *)(void));
|
||||
setfunc(setblocking, mysetblocking, int (PASCAL FAR *)(Socket, bool));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -84,8 +84,10 @@ public:
|
|||
enum {
|
||||
#if WINDOWS_LIKE
|
||||
kEADDRINUSE = WSAEADDRINUSE,
|
||||
kECONNECTING = WSAEWOULDBLOCK,
|
||||
#elif UNIX_LIKE
|
||||
kEADDRINUSE = EADDRINUSE,
|
||||
kECONNECTING = EINPROGRESS,
|
||||
#endif
|
||||
kNone = 0
|
||||
};
|
||||
|
@ -139,12 +141,17 @@ public:
|
|||
static int (PASCAL FAR *getsockerror)(void);
|
||||
static int (PASCAL FAR *gethosterror)(void);
|
||||
|
||||
// convenience functions (only available after init())
|
||||
|
||||
static int (PASCAL FAR *setblocking)(CNetwork::Socket s, bool blocking);
|
||||
|
||||
#if WINDOWS_LIKE
|
||||
private:
|
||||
static void init2(HMODULE);
|
||||
static int PASCAL FAR poll2(PollEntry[], int nfds, int timeout);
|
||||
static ssize_t PASCAL FAR read2(Socket s, void FAR * buf, size_t len);
|
||||
static ssize_t PASCAL FAR write2(Socket s, const void FAR * buf, size_t len);
|
||||
static int PASCAL FAR setblocking2(CNetwork::Socket s, bool blocking);
|
||||
static int (PASCAL FAR *WSACleanup)(void);
|
||||
static int (PASCAL FAR *__WSAFDIsSet)(CNetwork::Socket, fd_set FAR *);
|
||||
static int (PASCAL FAR *select)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout);
|
||||
|
|
|
@ -45,6 +45,7 @@ CTCPListenSocket::bind(const CNetworkAddress& addr)
|
|||
IDataSocket*
|
||||
CTCPListenSocket::accept()
|
||||
{
|
||||
// accept asynchronously so we can check for cancellation
|
||||
CNetwork::PollEntry pfds[1];
|
||||
pfds[0].fd = m_fd;
|
||||
pfds[0].events = CNetwork::kPOLLIN;
|
||||
|
|
|
@ -109,13 +109,52 @@ CTCPSocket::close()
|
|||
void
|
||||
CTCPSocket::connect(const CNetworkAddress& addr)
|
||||
{
|
||||
CThread::testCancel();
|
||||
// connect asynchronously so we can check for cancellation
|
||||
CNetwork::setblocking(m_fd, false);
|
||||
if (CNetwork::connect(m_fd, addr.getAddress(),
|
||||
addr.getAddressLength()) == CNetwork::Error) {
|
||||
CThread::testCancel();
|
||||
// check for failure
|
||||
if (CNetwork::getsockerror() != CNetwork::kECONNECTING) {
|
||||
CNetwork::setblocking(m_fd, true);
|
||||
throw XSocketConnect();
|
||||
}
|
||||
|
||||
// wait for connection or failure
|
||||
CNetwork::PollEntry pfds[1];
|
||||
pfds[0].fd = m_fd;
|
||||
pfds[0].events = CNetwork::kPOLLOUT;
|
||||
for (;;) {
|
||||
CThread::testCancel();
|
||||
const int status = CNetwork::poll(pfds, 1, 10);
|
||||
if (status > 0) {
|
||||
if ((pfds[0].revents & (CNetwork::kPOLLERR |
|
||||
CNetwork::kPOLLNVAL)) != 0) {
|
||||
// connection failed
|
||||
CNetwork::setblocking(m_fd, true);
|
||||
throw XSocketConnect();
|
||||
}
|
||||
if ((pfds[0].revents & CNetwork::kPOLLOUT) != 0) {
|
||||
int error;
|
||||
CNetwork::AddressLength size = sizeof(error);
|
||||
if (CNetwork::getsockopt(m_fd, SOL_SOCKET, SO_ERROR,
|
||||
reinterpret_cast<char*>(&error),
|
||||
&size) == CNetwork::Error ||
|
||||
error != 0) {
|
||||
// connection failed
|
||||
CNetwork::setblocking(m_fd, true);
|
||||
throw XSocketConnect();
|
||||
}
|
||||
|
||||
// connected!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// back to blocking
|
||||
CNetwork::setblocking(m_fd, true);
|
||||
|
||||
// start servicing the socket
|
||||
m_connected = kReadWrite;
|
||||
m_thread = new CThread(new TMethodJob<CTCPSocket>(
|
||||
|
|
Loading…
Reference in New Issue