diff --git a/src/syncterm/rlogin.c b/src/syncterm/rlogin.c
index f1b6f5821e6fb8e49f22e79690bdbc1aacff2533..bdcb5719c26eb483d03991e1596804334f89c687 100644
--- a/src/syncterm/rlogin.c
+++ b/src/syncterm/rlogin.c
@@ -17,7 +17,6 @@ SOCKET rlogin_sock=INVALID_SOCKET;
 #endif
 void rlogin_input_thread(void *args)
 {
-	fd_set	rds;
 	int		rd;
 	int	buffered;
 	size_t	buffer;
@@ -25,24 +24,7 @@ void rlogin_input_thread(void *args)
 	SetThreadName("RLogin Input");
 	conn_api.input_thread_running=1;
 	while(rlogin_sock != INVALID_SOCKET && !conn_api.terminate) {
-		FD_ZERO(&rds);
-		FD_SET(rlogin_sock, &rds);
-#ifdef __linux__
-		{
-			struct timeval tv;
-			tv.tv_sec=0;
-			tv.tv_usec=500000;
-			rd=select(rlogin_sock+1, &rds, NULL, NULL, &tv);
-		}
-#else
-		rd=select(rlogin_sock+1, &rds, NULL, NULL, NULL);
-#endif
-		if(rd==-1) {
-			if(errno==EBADF)
-				break;
-			rd=0;
-		}
-		if(rd==1) {
+		if (socket_readable(rlogin_sock, -1)) {
 			rd=recv(rlogin_sock, conn_api.rd_buf, conn_api.rd_buf_size, 0);
 			if(rd <= 0)
 				break;
@@ -79,24 +61,7 @@ void rlogin_output_thread(void *args)
 			pthread_mutex_unlock(&(conn_outbuf.mutex));
 			sent=0;
 			while(sent < wr) {
-				FD_ZERO(&wds);
-				FD_SET(rlogin_sock, &wds);
-#ifdef __linux__
-				{
-					struct timeval tv;
-					tv.tv_sec=0;
-					tv.tv_usec=500000;
-					ret=select(rlogin_sock+1, NULL, &wds, NULL, &tv);
-				}
-#else
-				ret=select(rlogin_sock+1, NULL, &wds, NULL, NULL);
-#endif
-				if(ret==-1) {
-					if(errno==EBADF)
-						break;
-					ret=0;
-				}
-				if(ret==1) {
+				if (socket_writable(rlogin_sock, -1)) {
 					ret=sendsocket(rlogin_sock, conn_api.wr_buf+sent, wr-sent);
 					if(ret==-1)
 						break;
@@ -173,22 +138,16 @@ int rlogin_connect(struct bbslist *bbs)
 	if(bbs->conn_type == CONN_TYPE_MBBS_GHOST) {
 		char	sbuf[80];
 		char	rbuf[10];
-		struct timeval tv;
 		int		idx, ret;
-		fd_set	rds;
-
-		FD_ZERO(&rds);
-		FD_SET(rlogin_sock, &rds);
-
-		tv.tv_sec=1;
-		tv.tv_usec=0;
 
 		/* Check to make sure GHost is actually listening */
 		sendsocket(rlogin_sock, "\r\nMBBS: PING\r\n", 14);
 
 		idx = 0;
-		while ((ret = select(rlogin_sock+1, &rds, NULL, NULL, &tv))==1) {
-			recv(rlogin_sock, rbuf+idx, 1, 0);
+		while (socket_readable(rlogin_sock, 1000)) {
+			ret = recv(rlogin_sock, rbuf+idx, 1, 0);
+			if (ret == -1)
+				break;
 			rbuf[++idx] = 0;
 
 			/* It says ERROR, but this is a good response to PING. */
@@ -216,8 +175,10 @@ int rlogin_connect(struct bbslist *bbs)
 		sendsocket(rlogin_sock, sbuf, strlen(sbuf));
 
 		idx = 0;
-		while ((ret = select(rlogin_sock+1, &rds, NULL, NULL, &tv))==1) {
-			recv(rlogin_sock, rbuf+idx, 1, 0);
+		while (socket_readable(rlogin_sock, 1000)) {
+			ret = recv(rlogin_sock, rbuf+idx, 1, 0);
+			if (ret == -1)
+				break;
 			rbuf[++idx] = 0;
 
 			/* GHost says it's launching the program, so pass terminal to user. */
diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c
index 1dcaae8b8edd61cc671034622cadd5f148f78ea2..0b45edaac5ecb2c67fa09a37f663e3352256d986 100644
--- a/src/syncterm/ssh.c
+++ b/src/syncterm/ssh.c
@@ -47,28 +47,15 @@ void cryptlib_error_message(int status, const char * msg)
 
 void ssh_input_thread(void *args)
 {
-	fd_set	rds;
 	int status;
 	int rd;
 	size_t	buffered;
 	size_t	buffer;
-	struct timeval tv;
 
 	SetThreadName("SSH Input");
 	conn_api.input_thread_running=1;
 	while(ssh_active && !conn_api.terminate) {
-		FD_ZERO(&rds);
-		FD_SET(ssh_sock, &rds);
-		tv.tv_sec = 0;
-		tv.tv_usec = 100;
-
-		rd=select(ssh_sock+1, &rds, NULL, NULL, &tv);
-		if(rd==-1) {
-			if(errno==EBADF)
-				break;
-			rd=0;
-		}
-		if(rd == 0)
+		if (!socket_readable(ssh_sock, 10))
 			continue;
 
 		pthread_mutex_lock(&ssh_mutex);
diff --git a/src/xpdev/sockwrap.c b/src/xpdev/sockwrap.c
index 24cd894b100ee75e0cddba03acded4b48e1d96ad..d45fd475a3bf37f4d4b5068cbec8470b4b5b70cb 100644
--- a/src/xpdev/sockwrap.c
+++ b/src/xpdev/sockwrap.c
@@ -329,6 +329,138 @@ BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
 	return(FALSE);
 }
 
+/*
+ * Return TRUE if recv() will not block on socket
+ * Will block for timeout ms or forever if timeout is negative
+ *
+ * This means it will return true if recv() will return an error
+ * as well as if the socket is closed (and recv() will return 0)
+ */
+BOOL socket_readable(SOCKET sock, int timeout)
+{
+#ifdef _WIN32
+	fd_set rd_set;
+	struct timeval tv = {0};
+	struct timeval *tvp = &tv;
+
+	FD_ZERO(&rd_set);
+	FD_SET(sock, &rd_set);
+	if (timeout < 0)
+		tvp = NULL;
+	else {
+		tv.tv_sec = timeout / 1000;
+		tv.tv_usec = (timeout % 1000) * 1000;
+	}
+
+	switch (select(sock+1, &rd_set, NULL, NULL, tvp)) {
+		case 0:		// Nothing to read
+			return FALSE;
+		case 1:
+			return TRUE;
+	}
+	// Errors and unexpected cases
+	return TRUE;
+#else
+	struct pollfd pfd = {0};
+	pfd.fd = sock;
+	pfd.events = POLLIN;
+
+	if (poll(&pfd, 1, timeout) == 1)
+		return TRUE;
+	return FALSE;
+#endif
+}
+
+/*
+ * Return TRUE if send() will not block on socket
+ * Will block for timeout ms or forever if timeout is negative
+ *
+ * This means it will return true if send() will return an error
+ * as well as if the socket is closed (and send() will return 0)
+ */
+BOOL socket_writable(SOCKET sock, int timeout)
+{
+#ifdef _WIN32
+	fd_set wr_set;
+	struct timeval tv = {0};
+	struct timeval *tvp = &tv;
+
+	FD_ZERO(&wr_set);
+	FD_SET(sock, &wr_set);
+	if (timeout < 0)
+		tvp = NULL;
+	else {
+		tv.tv_sec = timeout / 1000;
+		tv.tv_usec = (timeout % 1000) * 1000;
+	}
+
+	switch (select(sock+1, NULL, &wr_set, NULL, tvp)) {
+		case 0:		// Nothing to read
+			return FALSE;
+		case 1:
+			return TRUE;
+	}
+	// Errors and unexpected cases
+	return TRUE;
+#else
+	struct pollfd pfd = {0};
+	pfd.fd = sock;
+	pfd.events = POLLOUT;
+
+	if (poll(&pfd, 1, timeout) == 1)
+		return TRUE;
+	return FALSE;
+#endif
+}
+
+/*
+ * Return TRUE if recv() will not block and will return zero
+ * or an error. This is *not* a test if a socket is
+ * disconnected, but rather that it is disconnected *AND* all
+ * data has been recv()ed.
+ */
+BOOL socket_recvdone(SOCKET sock, int timeout)
+{
+#ifdef _WIN32
+	fd_set rd_set;
+	struct timeval tv = {0};
+	struct timeval *tvp = &tv;
+	char ch;
+	int rd;
+
+	FD_ZERO(&rd_set);
+	FD_SET(sock, &rd_set);
+	if (timeout < 0)
+		tvp = NULL;
+	else {
+		tv.tv_sec = timeout / 1000;
+		tv.tv_usec = (timeout % 1000) * 1000;
+	}
+
+	switch (select(sock+1, &rd_set, NULL, NULL, tvp)) {
+		case -1:	// Error, call this disconnected
+			return TRUE;
+		case 0:		// Nothing to read
+			return FALSE;
+	}
+	rd = recv(sock,&ch,1,MSG_PEEK);
+	if (rd == 1 || (rd==SOCKET_ERROR && ERROR_VALUE==EMSGSIZE))
+		return FALSE;
+	return TRUE;
+#else
+	struct pollfd pfd = {0};
+	pfd.fd = sock;
+	pfd.events = POLLIN;
+
+	if (poll(&pfd, 1, timeout) == 1) {
+		if (pfd.revents & POLLIN)
+			return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+#endif
+}
+
 int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
 			   ,uint retries, uint wait_secs
 			   ,const char* prot
diff --git a/src/xpdev/sockwrap.h b/src/xpdev/sockwrap.h
index b1bfd0aa0445d74089a69c620f1304c7e8aadbd5..79f394669312d72e57e184a14894df07bfdf4d3f 100644
--- a/src/xpdev/sockwrap.h
+++ b/src/xpdev/sockwrap.h
@@ -232,6 +232,32 @@ DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst);
 	#define inet_pton	xp_inet_pton
 #endif
 
+/*
+ * Return TRUE if recv() will not block on socket
+ * Will block for timeout ms or forever if timeout is negative
+ *
+ * This means it will return true if recv() will return an error
+ * as well as if the socket is closed (and recv() will return 0)
+ */
+DLLEXPORT BOOL socket_readable(SOCKET sock, int timeout);
+
+/*
+ * Return TRUE if send() will not block on socket
+ * Will block for timeout ms or forever if timeout is negative
+ *
+ * This means it will return true if send() will return an error
+ * as well as if the socket is closed (and send() will return 0)
+ */
+DLLEXPORT BOOL socket_writable(SOCKET sock, int timeout);
+
+/*
+ * Return TRUE if recv() will not block and will return zero
+ * or an error. This is *not* a test if a socket is
+ * disconnected, but rather that it is disconnected *AND* all
+ * data has been recv()ed.
+ */
+DLLEXPORT BOOL socket_recvdone(SOCKET sock, int timeout);
+
 #ifdef __cplusplus
 }
 #endif