Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

npipesocket.py 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import functools
  2. import time
  3. import io
  4. import win32file
  5. import win32pipe
  6. import pywintypes
  7. import win32event
  8. import win32api
  9. cERROR_PIPE_BUSY = 0xe7
  10. cSECURITY_SQOS_PRESENT = 0x100000
  11. cSECURITY_ANONYMOUS = 0
  12. MAXIMUM_RETRY_COUNT = 10
  13. def check_closed(f):
  14. @functools.wraps(f)
  15. def wrapped(self, *args, **kwargs):
  16. if self._closed:
  17. raise RuntimeError(
  18. 'Can not reuse socket after connection was closed.'
  19. )
  20. return f(self, *args, **kwargs)
  21. return wrapped
  22. class NpipeSocket:
  23. """ Partial implementation of the socket API over windows named pipes.
  24. This implementation is only designed to be used as a client socket,
  25. and server-specific methods (bind, listen, accept...) are not
  26. implemented.
  27. """
  28. def __init__(self, handle=None):
  29. self._timeout = win32pipe.NMPWAIT_USE_DEFAULT_WAIT
  30. self._handle = handle
  31. self._closed = False
  32. def accept(self):
  33. raise NotImplementedError()
  34. def bind(self, address):
  35. raise NotImplementedError()
  36. def close(self):
  37. self._handle.Close()
  38. self._closed = True
  39. @check_closed
  40. def connect(self, address, retry_count=0):
  41. try:
  42. handle = win32file.CreateFile(
  43. address,
  44. win32file.GENERIC_READ | win32file.GENERIC_WRITE,
  45. 0,
  46. None,
  47. win32file.OPEN_EXISTING,
  48. (cSECURITY_ANONYMOUS
  49. | cSECURITY_SQOS_PRESENT
  50. | win32file.FILE_FLAG_OVERLAPPED),
  51. 0
  52. )
  53. except win32pipe.error as e:
  54. # See Remarks:
  55. # https://msdn.microsoft.com/en-us/library/aa365800.aspx
  56. if e.winerror == cERROR_PIPE_BUSY:
  57. # Another program or thread has grabbed our pipe instance
  58. # before we got to it. Wait for availability and attempt to
  59. # connect again.
  60. retry_count = retry_count + 1
  61. if (retry_count < MAXIMUM_RETRY_COUNT):
  62. time.sleep(1)
  63. return self.connect(address, retry_count)
  64. raise e
  65. self.flags = win32pipe.GetNamedPipeInfo(handle)[0]
  66. self._handle = handle
  67. self._address = address
  68. @check_closed
  69. def connect_ex(self, address):
  70. return self.connect(address)
  71. @check_closed
  72. def detach(self):
  73. self._closed = True
  74. return self._handle
  75. @check_closed
  76. def dup(self):
  77. return NpipeSocket(self._handle)
  78. def getpeername(self):
  79. return self._address
  80. def getsockname(self):
  81. return self._address
  82. def getsockopt(self, level, optname, buflen=None):
  83. raise NotImplementedError()
  84. def ioctl(self, control, option):
  85. raise NotImplementedError()
  86. def listen(self, backlog):
  87. raise NotImplementedError()
  88. def makefile(self, mode=None, bufsize=None):
  89. if mode.strip('b') != 'r':
  90. raise NotImplementedError()
  91. rawio = NpipeFileIOBase(self)
  92. if bufsize is None or bufsize <= 0:
  93. bufsize = io.DEFAULT_BUFFER_SIZE
  94. return io.BufferedReader(rawio, buffer_size=bufsize)
  95. @check_closed
  96. def recv(self, bufsize, flags=0):
  97. err, data = win32file.ReadFile(self._handle, bufsize)
  98. return data
  99. @check_closed
  100. def recvfrom(self, bufsize, flags=0):
  101. data = self.recv(bufsize, flags)
  102. return (data, self._address)
  103. @check_closed
  104. def recvfrom_into(self, buf, nbytes=0, flags=0):
  105. return self.recv_into(buf, nbytes, flags), self._address
  106. @check_closed
  107. def recv_into(self, buf, nbytes=0):
  108. readbuf = buf
  109. if not isinstance(buf, memoryview):
  110. readbuf = memoryview(buf)
  111. event = win32event.CreateEvent(None, True, True, None)
  112. try:
  113. overlapped = pywintypes.OVERLAPPED()
  114. overlapped.hEvent = event
  115. err, data = win32file.ReadFile(
  116. self._handle,
  117. readbuf[:nbytes] if nbytes else readbuf,
  118. overlapped
  119. )
  120. wait_result = win32event.WaitForSingleObject(event, self._timeout)
  121. if wait_result == win32event.WAIT_TIMEOUT:
  122. win32file.CancelIo(self._handle)
  123. raise TimeoutError
  124. return win32file.GetOverlappedResult(self._handle, overlapped, 0)
  125. finally:
  126. win32api.CloseHandle(event)
  127. @check_closed
  128. def send(self, string, flags=0):
  129. event = win32event.CreateEvent(None, True, True, None)
  130. try:
  131. overlapped = pywintypes.OVERLAPPED()
  132. overlapped.hEvent = event
  133. win32file.WriteFile(self._handle, string, overlapped)
  134. wait_result = win32event.WaitForSingleObject(event, self._timeout)
  135. if wait_result == win32event.WAIT_TIMEOUT:
  136. win32file.CancelIo(self._handle)
  137. raise TimeoutError
  138. return win32file.GetOverlappedResult(self._handle, overlapped, 0)
  139. finally:
  140. win32api.CloseHandle(event)
  141. @check_closed
  142. def sendall(self, string, flags=0):
  143. return self.send(string, flags)
  144. @check_closed
  145. def sendto(self, string, address):
  146. self.connect(address)
  147. return self.send(string)
  148. def setblocking(self, flag):
  149. if flag:
  150. return self.settimeout(None)
  151. return self.settimeout(0)
  152. def settimeout(self, value):
  153. if value is None:
  154. # Blocking mode
  155. self._timeout = win32event.INFINITE
  156. elif not isinstance(value, (float, int)) or value < 0:
  157. raise ValueError('Timeout value out of range')
  158. else:
  159. # Timeout mode - Value converted to milliseconds
  160. self._timeout = int(value * 1000)
  161. def gettimeout(self):
  162. return self._timeout
  163. def setsockopt(self, level, optname, value):
  164. raise NotImplementedError()
  165. @check_closed
  166. def shutdown(self, how):
  167. return self.close()
  168. class NpipeFileIOBase(io.RawIOBase):
  169. def __init__(self, npipe_socket):
  170. self.sock = npipe_socket
  171. def close(self):
  172. super().close()
  173. self.sock = None
  174. def fileno(self):
  175. return self.sock.fileno()
  176. def isatty(self):
  177. return False
  178. def readable(self):
  179. return True
  180. def readinto(self, buf):
  181. return self.sock.recv_into(buf)
  182. def seekable(self):
  183. return False
  184. def writable(self):
  185. return False