Use non-blocking sockets
This commit is contained in:
parent
e7dcec142a
commit
9fd1006f2d
|
@ -1,4 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import errno
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
|
@ -41,9 +42,12 @@ class Connection(threading.Thread):
|
||||||
if self.server:
|
if self.server:
|
||||||
self.status = 'connected'
|
self.status = 'connected'
|
||||||
|
|
||||||
self.buffer = b''
|
self.buffer_receive = b''
|
||||||
|
self.buffer_send = b''
|
||||||
|
|
||||||
self.next_message_size = shared.header_length
|
self.next_message_size = shared.header_length
|
||||||
self.next_header = True
|
self.next_header = True
|
||||||
|
self.on_connection_fully_established_scheduled = False
|
||||||
|
|
||||||
self.last_message_received = time.time()
|
self.last_message_received = time.time()
|
||||||
self.last_message_sent = time.time()
|
self.last_message_sent = time.time()
|
||||||
|
@ -53,29 +57,44 @@ class Connection(threading.Thread):
|
||||||
self._connect()
|
self._connect()
|
||||||
if self.status != 'connected':
|
if self.status != 'connected':
|
||||||
return
|
return
|
||||||
self.s.settimeout(0.5)
|
self.s.settimeout(0)
|
||||||
if not self.server:
|
if not self.server:
|
||||||
self.send_queue.put(message.Version(self.host, self.port))
|
self.send_queue.put(message.Version(self.host, self.port))
|
||||||
while True:
|
while True:
|
||||||
|
time.sleep(0.3)
|
||||||
data = True
|
data = True
|
||||||
try:
|
try:
|
||||||
data = self.s.recv(1024)
|
data = self.s.recv(self.next_message_size - len(self.buffer_receive))
|
||||||
self.buffer += data
|
self.buffer_receive += data
|
||||||
except socket.timeout or ssl.SSLError:
|
except ssl.SSLWantReadError:
|
||||||
|
pass
|
||||||
|
except socket.error as e:
|
||||||
|
err = e.args[0]
|
||||||
|
if err == errno.EAGAIN or err == errno.EWOULDBLOCK:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
except ConnectionResetError:
|
||||||
|
logging.debug('Disconnecting from {}:{}. Reason: ConnectionResetError'.format(self.host, self.port))
|
||||||
|
data = None
|
||||||
|
self._process_buffer_receive()
|
||||||
|
self._request_objects()
|
||||||
|
self._process_queue()
|
||||||
|
self._send_data()
|
||||||
if time.time() - self.last_message_received > shared.timeout:
|
if time.time() - self.last_message_received > shared.timeout:
|
||||||
logging.debug('Disconnecting from {};{}. Reason: time.time() - self.last_message_received > shared.timeout'.format(self.host, self.port))
|
logging.debug(
|
||||||
|
'Disconnecting from {}:{}. Reason: time.time() - self.last_message_received > shared.timeout'.format(
|
||||||
|
self.host, self.port))
|
||||||
data = None
|
data = None
|
||||||
if time.time() - self.last_message_received > 30 and self.status != 'fully_established':
|
if time.time() - self.last_message_received > 30 and self.status != 'fully_established':
|
||||||
logging.debug('Disconnecting from {};{}. Reason: time.time() - self.last_message_received > 30 and self.status != \'fully_established\''.format(self.host, self.port))
|
logging.debug(
|
||||||
|
'Disconnecting from {}:{}. Reason: time.time() - self.last_message_received > 30 and self.status != \'fully_established\''.format(
|
||||||
|
self.host, self.port))
|
||||||
data = None
|
data = None
|
||||||
if time.time() - self.last_message_sent > 300 and self.status == 'fully_established':
|
if time.time() - self.last_message_sent > 300 and self.status == 'fully_established':
|
||||||
self.send_queue.put(message.Message(b'pong', b''))
|
self.send_queue.put(message.Message(b'pong', b''))
|
||||||
except ConnectionResetError:
|
if self.on_connection_fully_established_scheduled and not (self.buffer_send or self.buffer_receive):
|
||||||
logging.debug('Disconnecting from {};{}. Reason: ConnectionResetError'.format(self.host, self.port))
|
self._on_connection_fully_established()
|
||||||
data = None
|
|
||||||
self._process_buffer()
|
|
||||||
self._request_objects()
|
|
||||||
self._process_queue()
|
|
||||||
if self.status == 'disconnecting':
|
if self.status == 'disconnecting':
|
||||||
data = None
|
data = None
|
||||||
if not data:
|
if not data:
|
||||||
|
@ -97,28 +116,32 @@ class Connection(threading.Thread):
|
||||||
|
|
||||||
self.status = 'failed'
|
self.status = 'failed'
|
||||||
|
|
||||||
|
def _send_data(self):
|
||||||
|
try:
|
||||||
|
amount = self.s.send(self.buffer_send[:1000])
|
||||||
|
self.buffer_send = self.buffer_send[amount:]
|
||||||
|
except (BlockingIOError, ssl.SSLWantWriteError):
|
||||||
|
pass
|
||||||
|
|
||||||
def _do_tls_handshake(self):
|
def _do_tls_handshake(self):
|
||||||
self.s.settimeout(30)
|
|
||||||
logging.debug('Initializing TLS connection with {}:{}'.format(self.host, self.port))
|
logging.debug('Initializing TLS connection with {}:{}'.format(self.host, self.port))
|
||||||
self.s = ssl.wrap_socket(self.s, keyfile=os.path.join(shared.source_directory, 'tls', 'key.pem'),
|
self.s = ssl.wrap_socket(self.s, keyfile=os.path.join(shared.source_directory, 'tls', 'key.pem'),
|
||||||
certfile=os.path.join(shared.source_directory, 'tls', 'cert.pem'),
|
certfile=os.path.join(shared.source_directory, 'tls', 'cert.pem'),
|
||||||
server_side=self.server, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False,
|
server_side=self.server, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=False,
|
||||||
ciphers='AECDH-AES256-SHA')
|
ciphers='AECDH-AES256-SHA', suppress_ragged_eofs=True)
|
||||||
if hasattr(self.s, "context"):
|
if hasattr(self.s, "context"):
|
||||||
self.s.context.set_ecdh_curve("secp256k1")
|
self.s.context.set_ecdh_curve("secp256k1")
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
self.s.do_handshake()
|
self.s.do_handshake()
|
||||||
break
|
break
|
||||||
except ssl.SSLError as e:
|
except ssl.SSLWantReadError:
|
||||||
if e.errno == 2:
|
select.select([self.s], [], [])
|
||||||
select.select([self.s], [self.s], [])
|
except ssl.SSLWantWriteError:
|
||||||
else:
|
select.select([], [self.s], [])
|
||||||
break
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
break
|
break
|
||||||
self.s.settimeout(0.5)
|
|
||||||
self.tls = True
|
self.tls = True
|
||||||
logging.debug('Established TLS connection with {}:{}'.format(self.host, self.port))
|
logging.debug('Established TLS connection with {}:{}'.format(self.host, self.port))
|
||||||
|
|
||||||
|
@ -127,15 +150,13 @@ class Connection(threading.Thread):
|
||||||
logging.debug('{}:{} <- {}'.format(self.host, self.port, structure.Object.from_message(m)))
|
logging.debug('{}:{} <- {}'.format(self.host, self.port, structure.Object.from_message(m)))
|
||||||
else:
|
else:
|
||||||
logging.debug('{}:{} <- {}'.format(self.host, self.port, m))
|
logging.debug('{}:{} <- {}'.format(self.host, self.port, m))
|
||||||
self.s.settimeout(60)
|
self.buffer_send += m.to_bytes()
|
||||||
self.s.sendall(m.to_bytes())
|
|
||||||
self.s.settimeout(0.5)
|
|
||||||
|
|
||||||
def _on_connection_fully_established(self):
|
def _on_connection_fully_established(self):
|
||||||
logging.info('Established Bitmessage protocol connection to {}:{}'.format(self.host, self.port))
|
logging.info('Established Bitmessage protocol connection to {}:{}'.format(self.host, self.port))
|
||||||
|
self.on_connection_fully_established_scheduled = False
|
||||||
if self.remote_version.services & 2: # NODE_SSL
|
if self.remote_version.services & 2: # NODE_SSL
|
||||||
self._do_tls_handshake()
|
self._do_tls_handshake()
|
||||||
time.sleep(5)
|
|
||||||
with shared.objects_lock:
|
with shared.objects_lock:
|
||||||
if len(shared.objects) > 0:
|
if len(shared.objects) > 0:
|
||||||
self.send_queue.put(message.Inv({vector for vector in shared.objects.keys() if shared.objects[vector].expires_time > time.time()}))
|
self.send_queue.put(message.Inv({vector for vector in shared.objects.keys() if shared.objects[vector].expires_time > time.time()}))
|
||||||
|
@ -149,7 +170,7 @@ class Connection(threading.Thread):
|
||||||
m = self.send_queue.get()
|
m = self.send_queue.get()
|
||||||
if m:
|
if m:
|
||||||
if m == 'fully_established':
|
if m == 'fully_established':
|
||||||
self._on_connection_fully_established()
|
self.on_connection_fully_established_scheduled = True
|
||||||
else:
|
else:
|
||||||
self._send_message(m)
|
self._send_message(m)
|
||||||
self.last_message_sent = time.time()
|
self.last_message_sent = time.time()
|
||||||
|
@ -157,17 +178,17 @@ class Connection(threading.Thread):
|
||||||
self.status = 'disconnecting'
|
self.status = 'disconnecting'
|
||||||
break
|
break
|
||||||
|
|
||||||
def _process_buffer(self):
|
def _process_buffer_receive(self):
|
||||||
while len(self.buffer) >= self.next_message_size:
|
while len(self.buffer_receive) >= self.next_message_size:
|
||||||
if self.next_header:
|
if self.next_header:
|
||||||
self.next_header = False
|
self.next_header = False
|
||||||
h = message.Header.from_bytes(self.buffer[:shared.header_length])
|
h = message.Header.from_bytes(self.buffer_receive[:shared.header_length])
|
||||||
self.next_message_size += h.payload_length
|
self.next_message_size += h.payload_length
|
||||||
else:
|
else:
|
||||||
m = message.Message.from_bytes(self.buffer[:self.next_message_size])
|
m = message.Message.from_bytes(self.buffer_receive[:self.next_message_size])
|
||||||
|
|
||||||
self.next_header = True
|
self.next_header = True
|
||||||
self.buffer = self.buffer[self.next_message_size:]
|
self.buffer_receive = self.buffer_receive[self.next_message_size:]
|
||||||
self.next_message_size = shared.header_length
|
self.next_message_size = shared.header_length
|
||||||
self.last_message_received = time.time()
|
self.last_message_received = time.time()
|
||||||
self._process_message(m)
|
self._process_message(m)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user