fixed CTCPSocket::connect() to allow cancellation.

This commit is contained in:
crs 2002-06-21 16:19:08 +00:00
parent e2ee2371e0
commit 327af03d3d
4 changed files with 84 additions and 6 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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>(