What I'll be talking about
Server:
>>> sock = socket(AF_INET6, SOCK_DGRAM) >>> sock.bind(('', 1055)) >>> data, clientaddr = sock.recvfrom(4096) >>> data 'request' >>> clientaddr ('::ffff:127.0.0.1', 39241, 0, 0) >>> sock.sendto(b'response', clientaddr) 7 >>> sock.close() >>> del sock
Client:
>>> sock = socket(AF_INET, SOCK_DGRAM) >>> sock.sendto(b'request', ('127.0.0.1', 1055)) 7 >>> sock.recvfrom(4096) ('response', ('127.0.0.1', 1055)) >>> sock.close() >>> del sock
Binding to '' is the wildcard.
Use .sendto(), .recvfrom()
udp6 can receive IPv4 connections: ::ffff:n.n.n.n v4 mapped address.
Netstat shows the listening socket after binding:
$ netstat -l -n -A inet,inet6 Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State udp6 0 0 :::1055 :::*
Sockets not cleaned up when closed
Client:
>>> sock = socket(AF_INET, SOCK_DGRAM) >>> sock.connect(('127.0.0.1', 1055)) >>> sock.send(b'message') 7
Server:
>>> sock = socket(AF_INET6, SOCK_STREAM) >>> sock.bind(('', 1055)) >>> sock.listen(SOMAXCONN) >>> client, addr = sock.accept() >>> addr ('::ffff:127.0.0.1', 54584, 0, 0) >>> client.recv(4096) 'message' >>> client.close() >>> sock.close()
Client:
>>> sock = socket(AF_INET, SOCK_STREAM) >>> sock.connect(('127.0.0.1', 1055)) >>> sock.sendall(b'message') 7 >>> sock.close()
$ netstat netstat -n -A inet,inet6 Active Internet connections (w/o servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:54584 127.0.0.1:1055 ESTABLISHED tcp6 0 0 127.0.0.1:1055 127.0.0.1:54584 ESTABLISHED
sock = socket() sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sock.bind(...) ...
getaddrinfo(host, port, family=AF_UNSPEC, socktype=0, proto=0, flags=0) --> [(family, socktype, proto, canonname, sockaddr), ...]
AI_CANONNAME: return canonical name
AI_NUMERICHOST, AI_NUMERICSERV: no lookups
AI_PASSIVE: suitable for listening
AI_ADDRCONFIG: AF has configured interface
AI_V4MAPPED, AI_ALL: return v4 addrs when asking AF_INET6
Be as explicit as possible:
getaddrinfo('example.com', 80, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, AI_ADDRCONFIG)
In practice just see what works (client):
ai_list = socket.getaddrinfo('example.com', 80, 0, SOCK_STREAM) err = None for af, socktype, proto, cn, sockaddr in ai_list: sock = None try: sock = socket(af, socktype, proto) sock.connect(sockaddr) except Exception, e: err = e if sock: sock.close() else: break else: raise err if not ai_list: raise error('getaddrinfo returns empty list')
try: sock = create_connection(('example.com', 80)) except socket.error as e: logging.warn('Failed to create connection: %s', e.strerror)
strerror:
Connection refused Name or service not known ...
Call .setblocking(Flase)
Operations might raise EWOULDBLOCK:
sock = socket() sock.setblocking(False) sock.connect(...) try: sock.recv(4096) except IOError as e: if e.errno == errno.EWOULDBLOCK: pass
Managing this by had is messy
select(rlist, wlist, xlist[, timeout]) --> (ready_rlist, ready_wlist, ready_xlist)
Graceful interrupting I/O operations:
while keep_running: rpipe, wpipe = os.pipe() rlist = get_rlist() rlist.append(rpipe) wlist = get_wlist() readable, writable, _ = select.select(rlist, wlist, [], 60) if rpipe in readable: try: os.read(rpipe, 4096) except Exception: pass break if readable: # handle reads if writable: # handle writes
def recv(n): while True: try: sock.recv(n) except socket.error as e: if e.errno == errno.EINTR: continue else: raise
No package boundaries in streams
Example of protocol header (AgentX, RFC 2741):
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | h.version | h.type | h.flags | <reserved> | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | h.sessionID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | h.transactionID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | h.packetID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | h.payload_length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
"Protocol" layer which wants to send and receive "frames"
Minimal copying of data
Several approaches:
io.BytesIO
C implementation, hard to use thread safe
list of strings
More fragmented memory
bytearray (for fixed size data only)
...
Receiving data, using BytesIO:
class Protocol(object): def __init__(self): self._rbuf = io.BytesIO() def data_received(self, data): self._rbuf.seek(0, io.SEEK_END) self._rbuf.write(data) if self._rbuf.tell() >= 20: self._rbuf.seek(0, io.SEEK_SET) hdr = self._rbuf.read(20) payload_length, = struct.unpack('!L', hdr[16:20]) payload = self._rbuf.read(payload_length) if len(payload) == payload_length: tail = self._rbuf.read() self._rbuf.seek(0, io.SEEK_SET) self._rbuf.truncate() self._rbuf.write(tail) self.process_frame(hdr + payload)
Sending data:
class Transport(object): def __init__(self): self._wbuf = [] self._rpipe, self._wpipe = os.pipe() def write(self, data): self._wbuf.append(data) try: os.write(self._wpipe, 'a') except Exception: pass def _send(self, sock): buf = self._wbuf self._wbuf = [] data = ''.join(buf) try: n = sock.send(data) except socket.error, e: if e.errno not in [errno.EWOULDBLOCK, errno.EINTR]: raise self._wdata.insert(0, data) del data else: if len(data) > n: self._wdata.insert(0, data[n:]) del data
Client:
>>> sock = socket() >>> sock.connect(('127.0.0.1', 1055)) >>> sock.send(b'request') 7 >>> sock.shutdown(SHUT_WR) >>> sock.recv(4096) # blocks 'response'
Server:
>>> sock = socket(AF_INET6) >>> sock.bind(('', 1055)) >>> sock.listen(SOMAXCONN) >>> client, addr = sock.accept() # blocks >>> fp = client.makefile() >>> fp.read(4096) # blocks until EOF 'request' >>> fp.write(b'response') 8 >>> client.close() >>> del client
Can be used without shutting down the socket ends:
Can be used in non-blocking mode:
(Yes, these all have a particular meaning)
Network methods yield control to the hub:
from eventlet.green.socket import * sock = socket() sock.connect(('127.0.0.1', 1055)) # switch to hub sock.send(b'request') # switch to hub
Spawning tasks:
def server(): sock = socket(AF_INET6) sock.bind(('', 1055)) sock.listen(SOMAXCONN) while True: client, addr = sock.accept() # switch to hub eventlet.spawn(handle_client, client, addr) def handle_client(sock, addr): print('connection from {}'.format(addr)) req = sock.recv(4096) # switch to hub sock.send(b'response') # switch to hub
No need to do complicated write buffer
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |