[Patches] WinSocks...

Jukka Santala donwulff@nic.fi
Sun, 25 Feb 2001 21:23:03 +0200


This is a multi-part message in MIME format.

------=_NextPart_000_0007_01C09F71.22F00510
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

This is more of just a FYI at this time due to the lack of clarity in
Microsoft documentation, but a patch against Python 2.0 socketmodule.c is
included for testing etc. anyway.

However, according to aforementioned documentation, there are two conditions
when Microsoft WinSock could return with an indication that there was an
error with the socket, for recv() (and send()) when there in fact was not:

WSAEWOULDBLOCK
    See the Microsoft knowledge-base entry
http://support.microsoft.com/support/kb/articles/q177/3/46.asp for the
"detailed" coverage of this inssue. There is no indication on that
bug-report that the behavior is limited to non-blocking sockets, and Windows
programming sites recommend programs to be prepared to handle the
WSAEWOULDBLOCK error even when one is not expected as a matter of course.

A slight problem in the patch to fix this is that the KB article calls for
"retrying after a while". The attached patch allows going into a tight loop
waiting for change in status, because we can't return an unambigious "No
data yet, but try again later" with present API.Windows non-blocking sockets
should probably pass the  WSAEWOULDBLOCK straight to application instead, as
is presently happening, and have applications just be aware they may get
this error. But there's no good reason applications should expect to get
WSAEWOULDBLOCK on blocking sockets, nor a good way to determine whether we
should really be getting WSAEWOULDBLOCK or not.

WSAEMSGSIZE
    Again, fairly confusing situation. According to Windows Sockets 2
Reference, "If the datagram _or message_ is larger than the buffer supplied,
the buffer is filled with the first part of the datagram, and recv generates
the error WSAEMSGSIZE". Emphasis mine. While quite a few Windows
programming-sites say WSAEMSGSIZE applies only to datagrams, which is
logical, the Reference documentation seems to imply this is not neccessarily
so.

Since there are multiple vendors of WinSock stacks, it might not be safe to
trust even in the behavior of invidual stack when the reference says
something else. Using large enough recv() buffer might be one
application-level solution, but one mans "large enough" is another's "not
even near", so to allow for all contingencies and optimize performance, a
patch to skip this situation is included.

Ps. There's been some discussion over FD_SETSIZE for Python 2.1; according
to some Windows programming sites W9X can support only 256 concurrent
sockets, and some invidual WinSock implementations are reported to be
limited to 64 entries in select(). However, in their high wisdom Microsoft
has removed the feature for querying maximum number of sockets supported in
WSAStartup in WinSocks 2 (thereby breaking many programs using WinSocks).
However, since Windows uses lists instead of true masks in select(), the
applications can remain happily using selectmodule with higher FD_SETSIZE as
long as they don't actually use more sockets than the system provides.
Making the Windows default FD_SETSIZE 256 might still save some grievances,
though.

 -Donwulff

------=_NextPart_000_0007_01C09F71.22F00510
Content-Type: application/octet-stream;
	name="socketmodule.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="socketmodule.patch"

--- socketmodule.old	Thu Oct 19 13:37:05 2000
+++ socketmodule.c	Sun Feb 25 20:59:49 2001
@@ -1173,13 +1173,31 @@
 	buf = PyString_FromStringAndSize((char *) 0, len);
 	if (buf == NULL)
 		return NULL;
-	Py_BEGIN_ALLOW_THREADS
-	n = recv(s->sock_fd, PyString_AsString(buf), len, flags);
+	Py_BEGIN_ALLOW_THREADS
+	/* No WSAEWOULDBLOCK for blockwing WinSocks; fix for issue detailed at 
+	 * http://support.microsoft.com/support/kb/articles/q177/3/46.asp
+	 */
+#ifdef MS_WINDOWS
+	do {
+#endif
+		n = recv(s->sock_fd, PyString_AsString(buf), len, flags);
+#ifdef MS_WINDOWS
+	} while (n<0 && WSAGetLastError()==WSAEWOULDBLOCK);
+#endif
 	Py_END_ALLOW_THREADS
-	if (n < 0) {
-		Py_DECREF(buf);
-		return PySocket_Err();
-	}
+		if (n < 0) {
+			if(WSAGetLastError==WSAEMSGSIZE) {
+			/* According to Windows Sockets 2 Reference:
+			 * If the datagram or message is larger than the buffer supplied, 
+			 * the buffer is filled with the first part of the datagram, 
+			 * and recv generates the error WSAEMSGSIZE.
+			 */
+				n = len;
+			} else
+				Py_DECREF(buf);
+			return PySocket_Err();
+        }
+}
 	if (n != len && _PyString_Resize(&buf, n) < 0)
 		return NULL;
 	return buf;
@@ -1259,9 +1277,18 @@
 	if (!PyArg_ParseTuple(args, "s#|i:send", &buf, &len, &flags))
 		return NULL;
 	Py_BEGIN_ALLOW_THREADS
-	n = send(s->sock_fd, buf, len, flags);
+	/* No WSAEWOULDBLOCK for blockwing WinSocks; fix for issue detailed at 
+	 * http://support.microsoft.com/support/kb/articles/q177/3/46.asp
+	 */
+#ifdef MS_WINDOWS
+	do {
+#endif
+		n = send(s->sock_fd, buf, len, flags);
+#ifdef MS_WINDOWS
+	} while (n<0 && WSAGetLastError()==WSAEWOULDBLOCK);
+#endif
 	Py_END_ALLOW_THREADS
-	if (n < 0)
+		if (n < 0)
 		return PySocket_Err();
 	return PyInt_FromLong((long)n);
 }

------=_NextPart_000_0007_01C09F71.22F00510--