#FEATURE by Stefan Reise
authorsr
Fri, 31 Jan 2020 14:00:51 +0100
changeset 5439 a94cc6a90ff0
parent 5438 2f5749ce202a
child 5440 ad4949b2bd09
#FEATURE by Stefan Reise added support for websockets class: Socket added: #nonBlockingNextPutAll: #primNonBlockingNextPutAll: #setNonBlocking comment/format in: #makeNonBlocking
Socket.st
--- a/Socket.st	Thu Jan 30 12:50:19 2020 +0100
+++ b/Socket.st	Fri Jan 31 14:00:51 2020 +0100
@@ -1,3 +1,5 @@
+"{ Encoding: utf8 }"
+
 "
  COPYRIGHT (c) 1992 by Claus Gittinger
 	      All Rights Reserved
@@ -4037,7 +4039,6 @@
     "
 ! !
 
-
 !Socket methodsFor:'specials'!
 
 linger:anIntegerOrNil
@@ -4374,6 +4375,159 @@
     ^ self setSocketOption:#'TCP_NODELAY' argument:aBoolean argument:nil.
 ! !
 
+!Socket methodsFor:'support websocket windows'!
+
+nonBlockingNextPutAll:someBytes
+    "all sockets created under windows os are blocking.
+
+     in case for web sockets, 
+     concurrent write actions needs to be supported properly,
+     therefor the socket must be forced to be non blocking.
+
+     especially if the client and the server are running within the same stx, 
+     because stx can not read from the socket, when the write is blocking -> freeze forever
+
+     it does not help to just wait before a blocking write action,
+     because the other web socket side could do write actions between our wait and our write action,
+     in this case (the usual case) our write action will block even with the pre called wait
+
+     by forcing the socket to be non blocking, we simply call the write action, 
+     the primitiv #send returns a 'would block' flag if it would block, then we wait and try again"
+
+    |bytes countRemainingBytesToWrite result|
+
+    OperatingSystem isMSWINDOWSlike ifFalse:[
+        self error:'this method is for windows os only'.
+    ].
+
+    bytes := someBytes asByteArray.
+    countRemainingBytesToWrite := bytes size.
+
+    [
+        result := self primNonBlockingNextPutAll:bytes. "/ may just writes a subset or may 0 -> would block
+        result >= 0 "/ 0 or more bytes has been written
+        and:[result ~= countRemainingBytesToWrite] "/ there are remaining bytes to write 
+    ] whileTrue:[
+        result > 0 ifTrue:[
+            bytes := bytes copyFrom:result + 1.
+            countRemainingBytesToWrite := bytes size.
+        ].
+
+        "/ timeout does not matter, we wait infinitv    
+        self writeWaitWithTimeoutMs:1000.
+    ].
+
+    "Created: / 30-01-2020 / 16:35:08 / Stefan Reise"
+    "Modified (comment): / 31-01-2020 / 13:59:12 / Stefan Reise"
+!
+
+primNonBlockingNextPutAll:someBytes
+    "all sockets created under windows os are blocking.
+
+     in case for web sockets, 
+     concurrent write actions needs to be supported properly,
+     therefor the socket must be forced to be non blocking.
+
+     especially if the client and the server are running within the same stx, 
+     because stx can not read from the socket, when the write is blocking -> freeze forever
+
+     it does not help to just wait before a blocking write action,
+     because the other web socket side could do write actions between our wait and our write action,
+     in this case (the usual case) our write action will block even with the pre called wait
+
+     by forcing the socket to be non blocking, we simply call the write action, 
+     the primitiv #send returns a 'would block' flag if it would block, then we wait and try again"
+
+    |bytes byteLength returnValue|
+
+    OperatingSystem isMSWINDOWSlike ifFalse:[
+        self error:'this method is for windows os only'.
+    ].
+
+    bytes := someBytes asExternalBytes.
+    byteLength := bytes size.
+
+    "
+        -1      error
+        0       0 bytes sent or would block -> try again after wait
+        > 0     bytes sent (recall myself with the remaining bytes)
+    "
+    returnValue := -1. 
+
+    %{
+        int sendResult;
+        int wsaErrorNo;
+        char *pBytes = __externalAddressVal(bytes);
+        SOCKET socket = SOCKET_FROM_FILE_OBJECT(__INST(handle));
+
+        sendResult = send(socket, pBytes, __intVal(byteLength), 0);
+        if (sendResult == SOCKET_ERROR) {
+            wsaErrorNo = WSAGetLastError();
+            if (wsaErrorNo == WSAEWOULDBLOCK) {
+                returnValue = __MKSMALLINT(0);
+            } else {
+                console_printf("send failed with: %d\n", wsaErrorNo);
+            }
+        } else {
+            returnValue = __MKSMALLINT(sendResult);
+        }
+    %}.
+
+    returnValue < 0 ifTrue:[
+        self primitiveFailed.
+    ].
+
+    ^ returnValue
+
+    "Created: / 30-01-2020 / 16:36:01 / Stefan Reise"
+    "Modified: / 31-01-2020 / 11:53:01 / Stefan Reise"
+    "Modified (format): / 31-01-2020 / 13:59:56 / Stefan Reise"
+!
+
+setNonBlocking
+    "all sockets created under windows os are blocking.
+
+     in case for web sockets, 
+     concurrent write actions needs to be supported properly,
+     therefor the socket must be forced to be non blocking.
+
+     especially if the client and the server are running within the same stx, 
+     because stx can not read from the socket, when the write is blocking -> freeze forever
+
+     it does not help to just wait before a blocking write action,
+     because the other web socket side could do write actions between our wait and our write action,
+     in this case (the usual case) our write action will block even with the pre called wait
+
+     by forcing the socket to be non blocking, we simply call the write action, 
+     the primitiv #send returns a 'would block' flag if it would block, then we wait and try again"
+
+    |succeeded|
+
+    OperatingSystem isMSWINDOWSlike ifFalse:[
+        self error:'this method is for windows os only'.
+    ].
+
+    succeeded := false.
+
+    %{
+        int result;
+        u_long nonBlocking = 1;
+        SOCKET socket = SOCKET_FROM_FILE_OBJECT(__INST(handle));
+
+        result = ioctlsocket(socket, FIONBIO, &nonBlocking);
+        if (result == SOCKET_ERROR) {
+            console_printf("ioctlsocket failed with %d\n", WSAGetLastError());
+        } else {
+            succeeded = true;
+        }
+    %}.
+
+    succeeded ifFalse:[
+        self primitiveFailed.
+    ].
+
+    "Created: / 31-01-2020 / 11:23:48 / Stefan Reise"
+! !
 
 !Socket methodsFor:'waiting'!