__init__.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. #!/usr/bin/env python3.5
  2. # Copyright 2017 Digital
  3. #
  4. # This file is part of DigiLib.
  5. #
  6. # DigiLib is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # DigiLib is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with DigiLib. If not, see <http://www.gnu.org/licenses/>.
  18. import atexit
  19. import logging
  20. import logging.handlers
  21. import os
  22. import queue
  23. import select
  24. import socket
  25. import sys
  26. import threading
  27. import time
  28. import traceback
  29. import blinker
  30. import curio
  31. import digilib.misc
  32. lclient = logging.getLogger(__name__+".client")
  33. lserver = logging.getLogger(__name__+".server")
  34. lschat = logging.getLogger(__name__+".server.chat")
  35. lcchat = logging.getLogger(__name__+".client.chat")
  36. _tasks = []
  37. class ConnHandlerBase(object):
  38. """
  39. ConnectionHandlerBase is the base class for all connection handlers.
  40. It provides basic methodes. Consider inheriting form ConnectionHandler
  41. instead, as it provides better functionality.
  42. """
  43. addr = None
  44. block_size = 1024
  45. server = None
  46. def __init__(self, socket, addr, server):
  47. super(ConnHandlerBase, self).__init__()
  48. self.socket = socket
  49. self.addr = addr
  50. self.server = server
  51. async def disconnect(self):
  52. """
  53. disconenct explicitely disconnects the client, shuts the socket down
  54. and closes it.
  55. """
  56. try:
  57. await self.send(bytes())
  58. except:
  59. lserver.debug("error during disconenct")
  60. try:
  61. await self.socket.shutdown(0)
  62. except:
  63. lserver.debug("error during socket shutdown")
  64. try:
  65. await self.socket.close()
  66. except:
  67. lserver.debug("error closing socket")
  68. async def handle(self, data):
  69. """
  70. This method is called for every message the server receives from the
  71. client. It should handle the data. Performing asynchronous blocking
  72. actions is ok, as the client loop does not wait for this method to
  73. finish. therefore, this method can be called multiple times at once,
  74. use curio locks if appropriate.
  75. """
  76. raise NotImplemented()
  77. async def recv(self):
  78. """
  79. This method waits for the client to send something and returns bytes!
  80. """
  81. data_received = await self.socket.recv(self.block_size)
  82. return data_received
  83. async def send(self, data, log_msg=None):
  84. """
  85. This method sends bytes to the client. Returns False if an exception
  86. was raised during sending, otherwise True.
  87. """
  88. if log_msg:
  89. lschat.info("server:"+str(log_msg))
  90. else:
  91. lschat.info("Server:"+str(data))
  92. try:
  93. await self.socket.send(data)
  94. return True
  95. except Exception as e:
  96. lserver.error(e, exc_info=True)
  97. return False
  98. class ConnHandler(object):
  99. """
  100. More advanced connection handler than ConnectionHandlerBase. For
  101. instance, sends() takes a string and encodes it, recv() decodes
  102. the client's and returns a string and welcome_client is called after
  103. the ConnHandler is initialized (Not after the inheriting class is
  104. initialized though!)
  105. """
  106. def __init__(self, socket, addr, server):
  107. super(ConnHandler, self).__init__(socket,addr,server)
  108. async def disconnect(self):
  109. """
  110. disconenct() explicitely disconnects the client, performes a proper
  111. the shutdow on the socket and closes it.
  112. """
  113. try:
  114. await self.send("")
  115. except:
  116. lserver.debug("error during disconenct")
  117. try:
  118. await self.socket.shutdown(0)
  119. except:
  120. lserver.debug("error during socket shutdown")
  121. try:
  122. await self.socket.close()
  123. except:
  124. lserver.debug("error closing socket")
  125. async def handle(self, data):
  126. return
  127. async def recv(self):
  128. """
  129. This method waits for the client to send something, decodes it and
  130. returns a string.
  131. """
  132. data_received = await self.socket.recv(self.block_size)
  133. data_decoded = data_received.decode("utf-8")
  134. return data_decoded
  135. async def send(self, data, log_msg=False):
  136. """
  137. This method takes a string, encodes it and sends it to the client.
  138. Returns False if an exception was raised during sending, otherwise True.
  139. """
  140. if log_msg:
  141. lschat.info("server:"+log_msg)
  142. else:
  143. lschat.info("Server:"+data)
  144. data_encoded = bytes(data, "utf-8")
  145. try:
  146. await self.socket.send(data_encoded)
  147. return True
  148. except Exception as e:
  149. lserver.error(e, exc_info=True)
  150. return False
  151. async def welcome_client(self):
  152. """
  153. This method can be used to send a welcome message to the client.
  154. """
  155. self.send("Welcome client, this is the server sending!")
  156. pass
  157. class ConnHandlerEcho(ConnHandler):
  158. """
  159. A Conn handler which sends everything it receives to every other client
  160. connected to the server
  161. """
  162. def __init__(self, socket, addr, server):
  163. super(ConnHandlerEcho, self).__init__(socket, addr, server)
  164. def handle(self, data):
  165. for h in self.server.connection_handler:
  166. if not h is self:
  167. h.send(data)
  168. class Server(object):
  169. """
  170. Server opens either an unix or an inet connection. for every client which connects a new ClientHandler class is created.
  171. """
  172. def __init__(self,
  173. host,
  174. port=None,
  175. af_family="AF_INET",
  176. log_ip=False,
  177. max_allowed_clients=5,
  178. handler_class=None,
  179. handler_kwargs={},
  180. ):
  181. super(Server, self).__init__()
  182. # set to true when the server shuts down, for instance after a
  183. # fatal exception
  184. self.exit_event = False
  185. # on which hostname or ip address the connection will be opened
  186. self.host = host
  187. # on which port the connection will be opened
  188. self.port = port
  189. # what AF_INET family to use, either AF_INET or AF_UNIX (file socket)
  190. self.af_family = af_family
  191. # whether ip addresses will be logged
  192. self.log_ip = log_ip
  193. # number of maximum client connection at a time
  194. self.max_allowed_clients = max_allowed_clients
  195. # this handler class will be used when creating a new handler
  196. self.handler_class = handler_class
  197. # the kwargs passed to the handler's __init__ function
  198. self.handler_kwargs = handler_kwargs
  199. # don't make the socket yet, we don't need right now. will be created
  200. # when the start method is called
  201. # self.socket = self.make_socket()
  202. self.socket = None
  203. # create a task group for handlers, so we can easily cancel/terminate
  204. # them all at once
  205. self.handle_tasks = curio.TaskGroup(name="tg_handle_clients")
  206. # list of all active connection handlers
  207. self.connection_handler = []
  208. # these aren't actually used, I will remove them soon
  209. # self.conn_to_addr = {}
  210. # self.addr_to_conn = {}
  211. # self.conn_to_handler ={}
  212. # self.handler_to_conn = {}
  213. # this was used for the select.select approach. we use the async approach now, let's remove it
  214. # self.read_sockets_expected = [self.socket]
  215. # register our cleanup method to be executed when the program exits.
  216. # the cleanup function unregisters itself, so it won't get executed twice when the user called it befor the program exites
  217. atexit.register(self.shutdown)
  218. def make_socket(self):
  219. """
  220. factory method for sockets.
  221. this method makes a normal socket and wraps it in a curi.io.Socket wrapper
  222. """
  223. lserver.debug("making a {} socket".format(self.af_family))
  224. if self.af_family == "AF_INET":
  225. s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  226. elif self.af_family == "AF_UNIX":
  227. s = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  228. else:
  229. raise ValueError(
  230. "AF_FAMILY '{}' not supported!".format(
  231. self.af_family
  232. )
  233. )
  234. s = curio.io.Socket(s)
  235. return s
  236. def make_handler(self, conn, addr):
  237. """
  238. factory method for handlers.
  239. this method creates a handler object from self.handler_class and self.handler_kwargs
  240. """
  241. return self.handler_class(conn, addr, self, **self.handler_kwargs)
  242. # DEPRECATED
  243. # def register_conn(self, conn, addr):
  244. # # if self.log_ip:
  245. # # lserver.info("New connection from {} on port {}".format(*addr))
  246. # self.read_sockets_expected.append(conn)
  247. # if addr:
  248. # self.conn_to_addr[conn] = addr
  249. # self.addr_to_conn[addr] = conn
  250. #
  251. # def unregister_conn(self, conn):
  252. # self.read_sockets_expected.remove(conn)
  253. # addr = self.conn_to_addr.get(conn, False)
  254. # if addr:
  255. # del self.addr_to_conn[addr]
  256. # del self.conn_to_addr[conn]
  257. #
  258. # def register_handler(self, handler, conn):
  259. # self.connection_handler.append(handler)
  260. # self.conn_to_handler[conn] = handler
  261. # self.handler_to_conn[handler] = conn
  262. #
  263. # def unregister_handler(self, handler, conn):
  264. # self.connection_handler.remove(handler)
  265. # del self.conn_to_handler[conn]
  266. # del self.handler_to_conn[handler]
  267. def setup(self):
  268. """
  269. creates a scoket, opens the connection and starts listening for
  270. clients. this method does not block, it returns after starting to
  271. listen.
  272. """
  273. lserver.info("setting up server")
  274. self.socket = self.make_socket()
  275. if self.af_family == "AF_INET":
  276. self.socket.bind((self.host, self.port))
  277. elif self.af_family == "AF_UNIX":
  278. if os.path.exists(self.host):
  279. lserver.debug("file already exists")
  280. lserver.debug("attempting to remove it")
  281. os.remove(self.host)
  282. self.socket.bind(self.host)
  283. self.socket.listen(self.max_allowed_clients)
  284. def shutdown(self):
  285. """
  286. This method properly shuts down the sockets and closes them.
  287. it unregisters itself from atexit, so it doesn't get executed twice
  288. when it was manually called before to program exits.
  289. """
  290. def error_handler(func,*args,log_text="error",async_=False,**kwargs):
  291. try:
  292. if async_:
  293. coro = func(*args,**kwargs)
  294. lserver.debug("{} {}".format(func,coro))
  295. else:
  296. func(*args,**kwargs)
  297. except Exception as exc:
  298. lserver.debug("error occured during "+log_text,exc_info=exc)
  299. atexit.unregister(self.shutdown)
  300. lserver.info("shutting down server")
  301. error_handler(
  302. self.handle_tasks.cancel_remaining,log_text="handler cancel",async_=True)
  303. # check if there is actually a socket. if the shutdown method is
  304. # executed before the start method, there is no socket.
  305. if self.socket:
  306. error_handler(self.socket.shutdown,log_text="socket shutdown")
  307. error_handler(self.socket.close,log_text="socket close")
  308. def start(self):
  309. """
  310. this method starts the server. it is blocking.
  311. """
  312. self.setup()
  313. curio.run(self.run)
  314. async def run(self):
  315. """
  316. this method is the main loop of the Server. it waits for new client
  317. connections and creates a handle task for each of them. it does not
  318. receive or send anything.
  319. """
  320. lserver.debug("entering main loop")
  321. while ( not self.exit_event ):
  322. lserver.debug("waiting for client to connect")
  323. # wait for a client to connect
  324. conn,addr = await self.socket.accept()
  325. if self.log_ip:
  326. lserver.info(
  327. "new client connection, {}:{}!".format(*addr))
  328. else:
  329. lserver.info("a new client connected, let's handle it")
  330. handler = self.make_handler(conn, addr)
  331. # self.register_conn(conn, addr)
  332. # self.register_handler(handler, conn)
  333. await self.handle_tasks.spawn(self.handle_client(conn,handler))
  334. async def handle_client(self,socket,handler):
  335. """
  336. This method waits for the client to send something and calls the
  337. ClientHandler's handle method. there is a handle_client method running for each client connected.
  338. """
  339. if hasattr(handler,"on_welcome"):
  340. await handler.on_welcome()
  341. while True:
  342. try:
  343. if self.log_ip:
  344. lserver.debug("waiting for {} to send something"
  345. .format(socket.getsockname()))
  346. else:
  347. lserver.debug("waiting for the client to send something")
  348. # wait for the client to send something
  349. data = await handler.recv()
  350. # if there is no data the client disconnected. this is a
  351. # tcp protocoll specification.
  352. if not data:
  353. if self.log_ip:
  354. lserver.info("the connection to {} was closed"
  355. .format(socket.getsockname()))
  356. else:
  357. lserver.info("the connection to the client was closed")
  358. # self.unregister_handler(handler, socket)
  359. # self.unregister_conn(socket)
  360. await handler.close()
  361. # break out of the loop. don't return because we need to
  362. # do cleanup
  363. break
  364. else:
  365. # don't strip the data of its whitespaces, since they may
  366. # be important.
  367. lschat.info("Client:"+data.rstrip())
  368. coro = handler.handle(data)
  369. task = await curio.spawn(coro)
  370. except Exception as e:
  371. lserver.error(e, exc_info=True)
  372. # let's sleep a bit, in case something is broken and the
  373. # loop throws an exception every time
  374. await curio.sleep(0.01)
  375. # if a task exits and hasn't been joined, curio prints a warning.
  376. # we don't want the warning, so let's join the current task for 0
  377. # seconds. instead of task.join() we use task.wait(). the only
  378. # difference is that wait doesn't throw a exception if the task was
  379. # stopped or crashed
  380. cur_task = await curio.current_task()
  381. await curio.ignore_after(0,cur_task.wait)
  382. class Client(threading.Thread):
  383. """docstring for Client"""
  384. is_connecting = False
  385. is_connected = False
  386. status = "uninitialized"
  387. def __init__(self,
  388. host,
  389. port=None,
  390. af_family="AF_INET",
  391. handle_data_func=None,
  392. error_handler=None,
  393. block_size=1024,
  394. ):
  395. self.super_class = super(Client, self)
  396. self.super_class.__init__()
  397. self.name = "Client"
  398. self.exit_event = False
  399. self.host = host
  400. self.port = port
  401. self.af_family = af_family
  402. self.block_size = block_size
  403. self.handle_data_func = handle_data_func
  404. self.is_connected = False
  405. self.error_handler = error_handler
  406. # self.socket = self.make_socket()
  407. self.socket = None
  408. self.status = "disconnected"
  409. def connect(self):
  410. self.status = "connecting"
  411. self.socket = self.make_socket()
  412. lclient.info(
  413. "connecting to socket '{}' of type {}".format(
  414. self.host,
  415. self.af_family
  416. )
  417. )
  418. try:
  419. if self.af_family == "AF_INET":
  420. self.socket.connect((self.host, self.port))
  421. elif self.af_family == "AF_UNIX":
  422. if os.path.exists(self.host):
  423. self.socket.connect(self.host)
  424. else:
  425. lclient.warn("File not found. Aborting.")
  426. return
  427. self.is_connected = True
  428. self.status = "connected"
  429. lclient.info("connected")
  430. return True
  431. except Exception as e:
  432. lclient.debug(e, exc_info=True)
  433. if type(e) is ConnectionRefusedError:
  434. lclient.info("failed to connect to socket '{}'".format(self.host))
  435. self.disconnect()
  436. return False
  437. def disconnect(self):
  438. lclient.info("disconnecting from socket '{}'".format(self.host))
  439. self.is_connected = False
  440. self.status = "disconnected"
  441. if self.socket:
  442. try:
  443. self.socket.shutdown(socket.SHUT_RDWR)
  444. except Exception as e:
  445. lclient.error(e)
  446. try:
  447. self.socket.close()
  448. except Exception as e:
  449. lclient.error("error occured while closing the socket, " +
  450. "maybe it is already closed",exc_info=e)
  451. del self.socket
  452. self.socket = None
  453. def handle_data(self, data_received):
  454. data_decoded = data_received.decode("utf-8")
  455. lcchat.info("Server: "+data_decoded)
  456. if self.handle_data_func:
  457. try:
  458. self.handle_data_func(data_decoded)
  459. except Exception as e:
  460. lclient.error(e, exc_info=True)
  461. def is_running(self):
  462. return (self in threading.enumerate())
  463. def make_socket(self):
  464. lclient.info("creating a {} socket".format(self.af_family))
  465. if self.af_family == "AF_INET":
  466. s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  467. elif self.af_family == "AF_UNIX":
  468. s = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  469. else:
  470. raise ValueError(
  471. "AF_FAMILY '{}' not supported!".format(
  472. self.af_family
  473. )
  474. )
  475. return s
  476. def main_loop(self):
  477. lclient.debug("starting main loop")
  478. while ( not self.exit_event ):
  479. if not self.status in ["connected"]:
  480. time.sleep(0.1)
  481. continue
  482. # print(0)
  483. read_confirmed, write_confirmed, exc_confirmed \
  484. = select.select(
  485. [self.socket],
  486. [],
  487. [self.socket],
  488. 1
  489. )
  490. if self.socket in exc_confirmed:
  491. self.is_connected = False
  492. lclient.warning("socket is expected to corrupt, exiting")
  493. self.disconnect()
  494. # self.stop()
  495. break
  496. elif self.socket in read_confirmed:
  497. try:
  498. data_received = self.read_from_socket()
  499. if data_received == b'':
  500. lclient.info("connection is broken, closing socket exiting")
  501. self.disconnect()
  502. # self.stop()
  503. # break
  504. else:
  505. try:
  506. self.handle_data(data_received)
  507. except Exception as e:
  508. lserver.error(
  509. "Error while handling data",
  510. exc_info=e
  511. )
  512. except Exception as e:
  513. lclient.error(e, exc_info=True)
  514. if type(e) is OSError:
  515. self.is_connected = False
  516. lclient.warn("connection broken, exiting")
  517. self.disconnect()
  518. # self.stop()
  519. # break
  520. else:
  521. raise
  522. else:
  523. time.sleep(0.1)
  524. def read_from_socket(self):
  525. data_received = self.socket.recv(self.block_size)
  526. return data_received
  527. def run(self):
  528. # self.connect()
  529. if self.error_handler:
  530. self.error_handler(self.main_loop)
  531. else:
  532. self.main_loop()
  533. def send(self, msg):
  534. msg = msg.rstrip()
  535. msg_encoded = bytes(msg+"\r\n", "utf-8")
  536. try:
  537. lcchat.info("Client: "+msg)
  538. self.socket.send(msg_encoded)
  539. except Exception as e:
  540. self.is_connected = False
  541. lclient.error(e, exc_info=True)
  542. self.status = "shutdown"
  543. def setup(self):
  544. pass
  545. def stop(self,reason=None):
  546. self.disconnect()
  547. self.exit_event = True
  548. if reason:
  549. print(reason)
  550. class AsyncClient(object):
  551. """docstring for Client"""
  552. is_connecting = False
  553. is_connected = False
  554. status = "uninitialized"
  555. def __init__(self,
  556. host,
  557. port=None,
  558. af_family="AF_INET",
  559. handle_data_func=None,
  560. error_handler=None,
  561. block_size=1024,
  562. ):
  563. self.super_class = super(AsyncClient, self)
  564. self.super_class.__init__()
  565. self.name = "Client"
  566. self.exit_event = False
  567. self.host = host
  568. self.port = port
  569. self.af_family = af_family
  570. self.block_size = block_size
  571. self.handle_data_func = handle_data_func
  572. self.is_connected = False
  573. self.error_handler = error_handler
  574. self.socket = None
  575. self.status = "disconnected"
  576. def connect(self):
  577. self.status = "connecting"
  578. self.socket = self.make_socket()
  579. lclient.info("connecting to socket '{}' of type {}".format(
  580. self.host,self.af_family))
  581. try:
  582. if self.af_family == "AF_INET":
  583. self.socket.connect((self.host, self.port))
  584. elif self.af_family == "AF_UNIX":
  585. self.socket.connect(self.host)
  586. self.is_connected = True
  587. self.status = "connected"
  588. lclient.info("connected")
  589. return True
  590. except Exception as e:
  591. lclient.debug(e, exc_info=True)
  592. if type(e) is ConnectionRefusedError:
  593. lclient.info("failed to connect to socket '{}'".format(self.host))
  594. self.disconnect()
  595. return False
  596. def disconnect(self):
  597. lclient.info("disconnecting from socket '{}'".format(self.host))
  598. self.is_connected = False
  599. self.status = "disconnected"
  600. if self.socket:
  601. try:
  602. self.socket.shutdown(socket.SHUT_RDWR)
  603. except Exception as e:
  604. lclient.error(e)
  605. try:
  606. self.socket.close()
  607. except Exception as e:
  608. lclient.error("error occured while closing the socket, " +
  609. "maybe it is already closed",exc_info=e)
  610. del self.socket
  611. self.socket = None
  612. def handle_data(self, data_received):
  613. data_decoded = data_received.decode("utf-8")
  614. lcchat.info("Server: "+data_decoded)
  615. if self.handle_data_func:
  616. try:
  617. self.handle_data_func(data_decoded)
  618. except Exception as e:
  619. lclient.error(e, exc_info=True)
  620. def is_running(self):
  621. return (self in threading.enumerate())
  622. def make_socket(self):
  623. lclient.info("creating a {} socket".format(self.af_family))
  624. if self.af_family == "AF_INET":
  625. s = trio.socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  626. elif self.af_family == "AF_UNIX":
  627. s = trio.socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  628. else:
  629. raise ValueError(
  630. "AF_FAMILY '{}' not supported!".format(
  631. self.af_family
  632. )
  633. )
  634. return s
  635. async def read_from_socket(self):
  636. data_received = await self.socket.recv(self.block_size)
  637. return data_received
  638. async def recv(self):
  639. data_received = await self.socket.recv(self.block_size)
  640. data_decoded = data_received.decode("utf-8")
  641. return data_decoded
  642. async def run(self):
  643. lclient.debug("starting main loop")
  644. while ( not self.exit_event ):
  645. if not self.status in ["connected"]:
  646. time.sleep(0.1)
  647. continue
  648. # if self.socket in exc_confirmed:
  649. # self.is_connected = False
  650. # lclient.warning("socket is expected to corrupt, exiting")
  651. # self.disconnect()
  652. # # self.stop()
  653. # break
  654. # elif self.socket in read_confirmed:
  655. try:
  656. data = await self.read_from_socket()
  657. if not data:
  658. lclient.info("connection closed")
  659. self.disconnect()
  660. else:
  661. self.handle_data(data)
  662. except Exception as e:
  663. lclient.error(e, exc_info=True)
  664. if type(e) is OSError:
  665. self.is_connected = False
  666. lclient.warn("connection broken, exiting")
  667. self.disconnect()
  668. else:
  669. raise
  670. async def start(self,connect=False):
  671. if connect:
  672. self.connect()
  673. if self.error_handler:
  674. self.error_handler(self.run)
  675. else:
  676. self.run()
  677. def send(self, msg):
  678. msg = msg.rstrip()
  679. msg_encoded = bytes(msg+"\r\n", "utf-8")
  680. try:
  681. lcchat.info("Client: "+msg)
  682. self.socket.send(msg_encoded)
  683. except Exception as e:
  684. self.is_connected = False
  685. lclient.error(e, exc_info=True)
  686. self.disconnect()
  687. # self.exit_event = True
  688. # self.status = "shutdown"
  689. def setup(self):
  690. pass
  691. def stop(self,reason=None):
  692. self.disconnect()
  693. self.exit_event = True
  694. if reason:
  695. print(reason)
  696. #