Socket.st
changeset 5467 914f6955a10a
parent 5466 50ae296c9744
child 5472 b1822258a4be
equal deleted inserted replaced
5466:50ae296c9744 5467:914f6955a10a
    14 "{ Package: 'stx:libbasic2' }"
    14 "{ Package: 'stx:libbasic2' }"
    15 
    15 
    16 "{ NameSpace: Smalltalk }"
    16 "{ NameSpace: Smalltalk }"
    17 
    17 
    18 NonPositionableExternalStream subclass:#Socket
    18 NonPositionableExternalStream subclass:#Socket
    19         instanceVariableNames:'domain socketType protocol port peer peerName listening'
    19 	instanceVariableNames:'domain socketType protocol port peer peerName listening'
    20         classVariableNames:''
    20 	classVariableNames:''
    21         poolDictionaries:''
    21 	poolDictionaries:''
    22         category:'Streams-External'
    22 	category:'Streams-External'
    23 !
    23 !
    24 
    24 
    25 !Socket primitiveDefinitions!
    25 !Socket primitiveDefinitions!
    26 %{
    26 %{
    27 /* #define DGRAM_DEBUG /* */
    27 /* #define DGRAM_DEBUG /* */
  4373     ^ self setSocketOption:#'TCP_NODELAY' argument:aBoolean argument:nil.
  4373     ^ self setSocketOption:#'TCP_NODELAY' argument:aBoolean argument:nil.
  4374 ! !
  4374 ! !
  4375 
  4375 
  4376 !Socket methodsFor:'support websocket'!
  4376 !Socket methodsFor:'support websocket'!
  4377 
  4377 
  4378 nonBlockingNextPutAll:someBytes
  4378 blockingNextPutAllForNonBlockingSocket:someBytes
  4379     "using #primNonBlockingNextPutAll: 
  4379     "explanation of the method name:
  4380      because #nextPutAll: handles WSAEWOULDBLOCK / EWOULDBLOCK incorrect (at least for windows)
  4380      blocking -> this method blocks its STX process until all bytes have been written
  4381      only called by WebSocketStream #criticalSocketNextPutAll:"
  4381      ForNonBlockingSocket -> this method only works with non-blocking sockets
       
  4382 
       
  4383      the write is done within the current thread, 
       
  4384      but the primitiv #send returns because the socket is non-blocking (in case of would block). 
       
  4385      of cause if you write big data at once and the socket never would block, STX will freeze,
       
  4386      so please split big data into small packages"
       
  4387 
       
  4388     "PROBLEM \ BUG:
       
  4389         currently STX will freeze when writing and reading concurrently on the same blocking socket from both ends.
       
  4390         even when all those writers and readers have their own thread (by calling via __NOINT_CALL).
       
  4391 
       
  4392      WORKAROUND (or Other Concept):
       
  4393         using a non-blocking socket
       
  4394 
       
  4395         NEW PROBLEM: 
       
  4396         with non-blocking sockets you can not use the default #nextPut: 
       
  4397         because #nextPut: ends up in __NOINT_CALL('send') and probably due to until now not supported non-blocking sockets,
       
  4398         it does not set __threadErrno correctly to WOULDBLOCK in case of would block.
       
  4399         so by using default #nextPut: with an non-blocking socket you will miss some bytes or 
       
  4400         falsely get an error instead of WOULBLOCK
       
  4401 
       
  4402         NEXT WORKAROUND:
       
  4403         use a your own #nextPut: method (#blockingNextPutAllForNonBlockingSocket:) and handle WOULDBLOCK correctly
       
  4404 
       
  4405      Notes:
       
  4406      - all sockets under windows are created as blocking sockets by default
       
  4407      - there is NO query in windows to ask if a socket is blocking or non-blocking"
  4382 
  4408 
  4383     |bytes countRemainingBytesToWrite result|
  4409     |bytes countRemainingBytesToWrite result|
  4384 
  4410 
  4385     OperatingSystem isMSWINDOWSlike ifFalse:[
  4411     OperatingSystem isMSWINDOWSlike ifFalse:[
  4386         "this method supports non blocking socket for windows,
  4412         "this method supports non blocking socket for windows,
  4391 
  4417 
  4392     bytes := someBytes asByteArray.
  4418     bytes := someBytes asByteArray.
  4393     countRemainingBytesToWrite := bytes size.
  4419     countRemainingBytesToWrite := bytes size.
  4394 
  4420 
  4395     [
  4421     [
  4396         result := self primNonBlockingNextPutAll:bytes. "/ may just writes a subset or may 0 -> would block
  4422         result := self systemBlockingNextPutAll:bytes. "/ may just writes a subset or may 0 -> would block
  4397         result >= 0 "/ 0 or more bytes has been written
  4423         result >= 0 "/ 0 or more bytes has been written
  4398         and:[result ~= countRemainingBytesToWrite] "/ there are remaining bytes to write 
  4424         and:[result ~= countRemainingBytesToWrite] "/ there are remaining bytes to write 
  4399     ] whileTrue:[
  4425     ] whileTrue:[
  4400         result > 0 ifTrue:[
  4426         result > 0 ifTrue:[
  4401             bytes := bytes copyFrom:result + 1.
  4427             bytes := bytes copyFrom:result + 1.
  4404 
  4430 
  4405         "/ timeout does not matter, we wait indefinitely (due to loop)   
  4431         "/ timeout does not matter, we wait indefinitely (due to loop)   
  4406         self writeWaitWithTimeoutMs:1000.
  4432         self writeWaitWithTimeoutMs:1000.
  4407     ].
  4433     ].
  4408 
  4434 
  4409     "Created: / 30-01-2020 / 16:35:08 / Stefan Reise"
  4435     "Created: / 05-03-2020 / 10:35:54 / Stefan Reise"
  4410     "Modified (format): / 06-02-2020 / 13:18:59 / Stefan Reise"
  4436 !
  4411     "Modified (comment): / 03-03-2020 / 15:25:26 / Stefan Vogel"
  4437 
  4412 !
  4438 setNonBlocking
  4413 
  4439     "DO NO move this functionality into Win32OperatingSystem #setBlocking:fd:, 
  4414 primNonBlockingNextPutAll:someBytes
  4440      because it will not work correctly, caused by the following problems. 
  4415     "using #primNonBlockingNextPutAll: 
  4441      and even if the problems has been fixed, it would change the behavior for all sockets (not only websockets)"
  4416      because #nextPutAll: handles WSAEWOULDBLOCK / EWOULDBLOCK incorrect (at least for windows)
  4442 
  4417      only called by WebSocketStream #criticalSocketNextPutAll:"
  4443     "PROBLEM \ BUG:
       
  4444         currently STX will freeze when writing and reading concurrently on the same blocking socket from both ends.
       
  4445         even when all those writers and readers have their own thread (by calling via __NOINT_CALL).
       
  4446 
       
  4447      WORKAROUND (or Other Concept):
       
  4448         using a non-blocking socket
       
  4449 
       
  4450         NEW PROBLEM: 
       
  4451         with non-blocking sockets you can not use the default #nextPut: 
       
  4452         because #nextPut: ends up in __NOINT_CALL('send') and probably due to until now not supported non-blocking sockets,
       
  4453         it does not set __threadErrno correctly to WOULDBLOCK in case of would block.
       
  4454         so by using default #nextPut: with an non-blocking socket you will miss some bytes or 
       
  4455         falsely get an error instead of WOULBLOCK
       
  4456 
       
  4457         NEXT WORKAROUND:
       
  4458         use a your own #nextPut: method (#blockingNextPutAllForNonBlockingSocket:) and handle WOULDBLOCK correctly
       
  4459 
       
  4460      Notes:
       
  4461      - all sockets under windows are created as blocking sockets by default
       
  4462      - there is NO query in windows to ask if a socket is blocking or non-blocking"
       
  4463 
       
  4464     OperatingSystem isMSWINDOWSlike ifFalse:[
       
  4465         "/ this method is for windows os only
       
  4466         ^ self
       
  4467     ].
       
  4468 
       
  4469 %{
       
  4470 # ifdef __win32__
       
  4471     OBJ fp = __INST(handle);
       
  4472 
       
  4473     // ALWAYS check for proper arguments, please
       
  4474     if (fp != NULL) {
       
  4475         int result;
       
  4476         u_long nonBlocking = 1;
       
  4477         SOCKET socket = SOCKET_FROM_FILE_OBJECT(fp);
       
  4478 
       
  4479         result = ioctlsocket(socket, FIONBIO, &nonBlocking);
       
  4480         if (result == SOCKET_ERROR) {
       
  4481             console_fprintf(stderr, "Win32OS [info]: ioctlsocket failed with %d\n", WSAGetLastError());
       
  4482             RETURN(false);
       
  4483         }
       
  4484 
       
  4485         RETURN(true);
       
  4486     }
       
  4487 #endif // __win32__
       
  4488 %}.
       
  4489 
       
  4490     self primitiveFailed.
       
  4491 
       
  4492     "Modified: / 03-03-2020 / 15:29:26 / Stefan Vogel"
       
  4493     "Modified: / 05-03-2020 / 10:37:12 / Stefan Reise"
       
  4494 !
       
  4495 
       
  4496 systemBlockingNextPutAll:someBytes
       
  4497     "explanation of the method name:
       
  4498      systemBlocking -> this method blocks the entire STX 
       
  4499      until all bytes have been written or until the socket would block
       
  4500 
       
  4501      so please split big data into small packages"
       
  4502 
       
  4503     "PROBLEM \ BUG:
       
  4504         currently STX will freeze when writing and reading concurrently on the same blocking socket from both ends.
       
  4505         even when all those writers and readers have their own thread (by calling via __NOINT_CALL).
       
  4506 
       
  4507      WORKAROUND (or Other Concept):
       
  4508         using a non-blocking socket
       
  4509 
       
  4510         NEW PROBLEM: 
       
  4511         with non-blocking sockets you can not use the default #nextPut: 
       
  4512         because #nextPut: ends up in __NOINT_CALL('send') and probably due to until now not supported non-blocking sockets,
       
  4513         it does not set __threadErrno correctly to WOULDBLOCK in case of would block.
       
  4514         so by using default #nextPut: with an non-blocking socket you will miss some bytes or 
       
  4515         falsely get an error instead of WOULBLOCK
       
  4516 
       
  4517         NEXT WORKAROUND:
       
  4518         use a your own #nextPut: method (#blockingNextPutAllForNonBlockingSocket:) and handle WOULDBLOCK correctly
       
  4519 
       
  4520      Notes:
       
  4521      - all sockets under windows are created as blocking sockets by default
       
  4522      - there is NO query in windows to ask if a socket is blocking or non-blocking"
  4418 
  4523 
  4419     |bytes byteLength returnValue wsaError|
  4524     |bytes byteLength returnValue wsaError|
  4420 
  4525 
  4421     OperatingSystem isMSWINDOWSlike ifFalse:[
  4526     OperatingSystem isMSWINDOWSlike ifFalse:[
  4422         self error:'this method is for windows os only'.
  4527         self error:'this method is for windows os only'.
  4432     "
  4537     "
  4433     returnValue := -1. 
  4538     returnValue := -1. 
  4434 
  4539 
  4435 %{
  4540 %{
  4436 # ifdef __win32__
  4541 # ifdef __win32__
       
  4542     OBJ fp = __INST(handle);
       
  4543 
  4437     // ALWAYS check for proper arguments, please
  4544     // ALWAYS check for proper arguments, please
  4438     OBJ fp = __INST(handle);
  4545     if (
  4439 
  4546         __isExternalAddressLike(bytes) 
  4440     if (__isExternalAddressLike(bytes) 
       
  4441         && __isSmallInteger(byteLength) 
  4547         && __isSmallInteger(byteLength) 
  4442         && (fp != NULL)
  4548         && (fp != NULL)
  4443     ) {
  4549     ) {
  4444         int sendResult;
  4550         int sendResult;
  4445         int wsaErrorNo;
  4551         int wsaErrorNo;
  4466         WriteError raiseWith:wsaError.   
  4572         WriteError raiseWith:wsaError.   
  4467     ].
  4573     ].
  4468 
  4574 
  4469     ^ returnValue
  4575     ^ returnValue
  4470 
  4576 
  4471     "Modified: / 03-03-2020 / 15:29:09 / Stefan Vogel"
  4577     "Created: / 05-03-2020 / 10:48:56 / Stefan Reise"
  4472 !
       
  4473 
       
  4474 setNonBlocking
       
  4475     "using #setNonBlocking 
       
  4476      because implementing and using Win32OperatingSystem #setBlocking:fd: 
       
  4477      would reset to blocking (see senders of #blocking:)
       
  4478      and we need the socket to be non-blocking forever 
       
  4479      only called by WebSocketStream #criticalSocketNextPutAll:"
       
  4480 
       
  4481     OperatingSystem isMSWINDOWSlike ifFalse:[
       
  4482         "/ this method is for windows os only
       
  4483         ^ self
       
  4484     ].
       
  4485 
       
  4486 %{
       
  4487 # ifdef __win32__
       
  4488     // ALWAYS check for proper arguments, please
       
  4489     OBJ fp = __INST(handle);
       
  4490 
       
  4491     if (fp != NULL) {
       
  4492         int result;
       
  4493         u_long nonBlocking = 1;
       
  4494         SOCKET socket = SOCKET_FROM_FILE_OBJECT(fp);
       
  4495 
       
  4496         result = ioctlsocket(socket, FIONBIO, &nonBlocking);
       
  4497         if (result == SOCKET_ERROR) {
       
  4498             console_fprintf(stderr, "Win32OS [info]: ioctlsocket failed with %d\n", WSAGetLastError());
       
  4499             RETURN(false);
       
  4500         }
       
  4501 
       
  4502         RETURN(true);
       
  4503     }
       
  4504 #endif // __win32__
       
  4505 %}.
       
  4506 
       
  4507     self primitiveFailed.
       
  4508 
       
  4509     "Modified: / 03-03-2020 / 15:29:26 / Stefan Vogel"
       
  4510 ! !
  4578 ! !
  4511 
  4579 
  4512 !Socket methodsFor:'testing'!
  4580 !Socket methodsFor:'testing'!
  4513 
  4581 
  4514 isSSLSocket
  4582 isSSLSocket