#!/usr/bin/env python import sys import socket import select import time import empty import thr import dbg def port_conn( port, host='localhost' ): cs = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) try: cs.connect( ( host, port ) ) except: return None return cs def send_str( sc, s ): bt = s.encode( 'utf-8' ) try: sc.sendall( bt ) except: return False return True def recv_str( sc ): bufmax = 16 * 1024 bt = sc.recv( bufmax ) s = bt.decode( 'utf-8' ) return s def pipe_loop( sc_from, sc_to ): while True: s = recv_str( sc_from ) if not s: break if not send_str( sc_to, s ): break # srv=name # port=xxx,srv=name,id=xxx # kill_base_srv # name def get_dic( s ): if '=' not in s: return {} return dict( map( lambda t: t.split( '=' ), s.split( ',' ) ) ) def is_srv_dic( d ): return len( d ) == 1 and 'srv' in d def is_accept_dic( d ): ks = [ 'port', 'srv', 'id' ] return len( d ) == len( ks ) and all( map( lambda k: k in d, ks ) ) srvs = {} reqs = {} def base_th_srv( sc, d ): name = d.get( 'srv' ) if name in srvs: return q = thr.que_new() srvs[ name ] = q while True: s = q.get() if not send_str( sc, s ): break def base_th_accept( sc, s ): if s not in reqs: return ( sc2, ev ) = reqs.get( s ) ev2 = thr.event_new() reqs[ s ] = [ sc, ev2 ] ev.set() ev2.wait() if not send_str( sc, 'ok' ): return pipe_loop( sc, sc2 ) def base_th_connect( port, sc, s ): name = s q = srvs.get( name ) id = sc.fileno() s = 'port={},srv={},id={}'.format( port, name, id ) ev = thr.event_new() reqs[ s ] = [ sc, ev ] q.put( s ) ev.wait() [ sc2, ev2 ] = reqs.pop( s ) ev2.set() if not send_str( sc, 'ok' ): return pipe_loop( sc, sc2 ) def base_th( port, sc, ev_quit ): s = recv_str( sc ) if not s: sc.close() return d = get_dic( s ) if is_srv_dic( d ): base_th_srv( sc, d ) elif is_accept_dic( d ): base_th_accept( sc, s ) elif s == 'kill_base_srv': ev_quit.set() send_str( sc, 'next' ) elif s in srvs: base_th_connect( port, sc, s ) sc.close() def base_srv( port ): ss = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) ss.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) ss.bind( ( '', port ) ) ss.listen( 5 ) ev_quit = thr.event_new() while not ev_quit.wait( 0 ): ( cs, adr ) = ss.accept() th = thr.th_new( base_th, ( port, cs, ev_quit ) ) thr.ths.start( th ) dbg.out( 'base_srv quit' ) def server( port, name ): ss = port_conn( port ) if not ss: return None s = 'srv=' + name if not send_str( ss, s ): ss.close() return None return ss def accept( ss ): s = recv_str( ss ) if not s: return None d = get_dic( s ) if not is_accept_dic( d ): return None port = int( d.get( 'port' ) ) cs = port_conn( port ) if not send_str( cs, s ): cs.close() return None s = recv_str( cs ) if s != 'ok': cs.close() return None return cs def srv_loop( port, name, th_func ): ss = server( port, name ) if not ss: return while True: cs = accept( ss ) if not cs: break th = thr.th_new( th_func, ( cs, ) ) thr.ths.start( th ) def srv_func_new( cb ): def func( sc ): while True: s = recv_str( sc ) if not s: break r = cb( s ) if not send_str( sc, r ): break sc.close() return empty.new( locals() ) def connect( port, name ): cs = port_conn( port ) if not cs: return None if not send_str( cs, name ): cs.close() return None s = recv_str( cs ) if s != 'ok': cs.close() return None return cs def kill_base_srv( port ): cs = port_conn( port ) if not cs: return dbg.out( 'kill_base_srv' ) send_str( cs, 'kill_base_srv' ) r = recv_str( cs ) cs.close() cs = port_conn( port ) # for accept loop if cs: cs.close() def help(): lst = [ '\\', 'port base', 'port srv upper', 'port srv eval', 'port cli upper word word word ...', 'port cli eval word word word ...', 'port kill_base', ] dbg.help_exit( '\n'.join( lst ) ) def run(): if len( sys.argv ) < 2: help() argv = sys.argv[ 1 : ] def pop(): if len( argv ) <= 0: help() return argv.pop( 0 ) port = int( pop() ) names = { 'upper': lambda s: s.upper(), 'eval': lambda s: str( eval( s ) ), } cmd = pop() if cmd == 'base': base_srv( port ) elif cmd == 'srv': name = pop() if name in names: cb = names.get( name ) srv_func = srv_func_new( cb ) srv_loop( port, name, srv_func.func ) elif cmd == 'cli': name = pop() if name in names: sc = connect( port, name ) if not sc: return while len( argv ) > 0: w = pop() if not send_str( sc, w ): break r = recv_str( sc ) if not r: break dbg.out( '{} --> {}'.format( w, r ) ) sc.close() elif cmd == 'kill_base': kill_base_srv( port ) if __name__ == "__main__": run() # EOF