__init__.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  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 logging
  19. import logging.handlers
  20. import os
  21. import queue
  22. import select
  23. import socket
  24. import sys
  25. import threading
  26. import time
  27. import traceback
  28. lclient = logging.getLogger(__name__+".client")
  29. lserver = logging.getLogger(__name__+".server")
  30. lschat = logging.getLogger(__name__+".server.chat")
  31. lcchat = logging.getLogger(__name__+".client.chat")
  32. class ConnHandlerBase(object):
  33. def __init__(self, socket, addr, server):
  34. self.status = "init"
  35. super(ConnHandlerBase, self).__init__()
  36. self.socket = socket
  37. self.addr = addr
  38. self.server = server
  39. self.block_size = 1024
  40. self.welcome_client()
  41. self.status="connected"
  42. def welcome_client(self):
  43. pass
  44. def handle(self, data_decoded):
  45. return
  46. def recv(self):
  47. data_received = self.socket.recv(self.block_size)
  48. data_decoded = data_received.decode("utf-8")
  49. return data_decoded
  50. # def recv(self):
  51. # try:
  52. # data_received = self.socket.recv(self.block_size)
  53. # data_decoded = data_received.decode("utf-8")
  54. # if data_decoded:
  55. # lschat.info("Client:"+data_decoded.strip())
  56. # self.handle(data_decoded)
  57. # return True
  58. # else:
  59. # lserver.debug("connection corrupted")
  60. # return False
  61. # except Exception as e:
  62. # lserver.error(e, exc_info=True)
  63. # return False
  64. def send(self, msg):
  65. msg_encoded = bytes(msg, "utf-8")
  66. lschat.info("Server:"+msg)
  67. try:
  68. self.socket.send(msg_encoded)
  69. return True
  70. except Exception as e:
  71. lserver.error(e, exc_info=True)
  72. return False
  73. def close(self):
  74. self.status = "closed"
  75. try:
  76. self.socket.shutdown(0)
  77. except:
  78. lserver.error("error during socket shutdown, ignoring")
  79. try:
  80. self.socket.close()
  81. except:
  82. lserver.error("error closing socket, maybe already closed")
  83. class ConnHandlerEcho(ConnHandlerBase):
  84. def __init__(self, socket, addr, server):
  85. self.status = "init"
  86. self.super_class = super(ConnHandlerEcho, self)
  87. self.super_class.__init__(socket, addr, server)
  88. self.server = server
  89. def welcome_client(self):
  90. self.send("welcome to the client")
  91. def handle(self, data_decoded):
  92. lschat.info("Client:"+data_decoded)
  93. for h in list(set(self.server.connection_handler)-{self}):
  94. h.send(data_decoded)
  95. class Server(object):
  96. """docstring for SocketHandler"""
  97. def __init__(self,
  98. host,
  99. port=None,
  100. af_family="AF_INET",
  101. log_ip=False,
  102. max_allowed_clients=5,
  103. handler=None,
  104. handler_kwargs={},
  105. ):
  106. super(Server, self).__init__()
  107. self.exit_event = False
  108. self.host=host
  109. self.port=port
  110. self.af_family = af_family
  111. self.log_ip = log_ip
  112. self.handler_kwargs = handler_kwargs
  113. self.handler = handler
  114. self.max_allowed_clients = max_allowed_clients
  115. self.socket = self.make_socket()
  116. self.connection_handler = []
  117. self.conn_to_addr = {}
  118. self.addr_to_conn = {}
  119. self.conn_to_handler ={}
  120. self.handler_to_conn = {}
  121. self.read_sockets_expected = [self.socket]
  122. self.write_sockets_expected = []
  123. self.exc_sockets_expected = []
  124. def make_socket(self):
  125. lserver.debug("making a {} socket".format(self.af_family))
  126. if self.af_family == "AF_INET":
  127. return socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  128. elif self.af_family == "AF_UNIX":
  129. return socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  130. else:
  131. raise ValueError(
  132. "AF_FAMILY '{}' not supported!".format(
  133. self.af_family
  134. )
  135. )
  136. def make_handler(self, conn, addr):
  137. return self.handler(conn, addr, self, **self.handler_kwargs)
  138. def register_conn(self, conn, addr):
  139. # if self.log_ip:
  140. # lserver.info("New connection from {} on port {}".format(*addr))
  141. self.read_sockets_expected.append(conn)
  142. if addr:
  143. self.conn_to_addr[conn] = addr
  144. self.addr_to_conn[addr] = conn
  145. def unregister_conn(self, conn):
  146. self.read_sockets_expected.remove(conn)
  147. addr = self.conn_to_addr.get(conn, False)
  148. if addr:
  149. del self.addr_to_conn[addr]
  150. del self.conn_to_addr[conn]
  151. def register_handler(self, handler, conn):
  152. self.connection_handler.append(handler)
  153. self.conn_to_handler[conn] = handler
  154. self.handler_to_conn[handler] = conn
  155. def unregister_handler(self, handler, conn):
  156. self.connection_handler.remove(handler)
  157. del self.conn_to_handler[conn]
  158. del self.handler_to_conn[handler]
  159. def setup(self):
  160. lserver.info("setting up socket")
  161. if self.af_family == "AF_INET":
  162. self.socket.bind((self.host, self.port))
  163. elif self.af_family == "AF_UNIX":
  164. if os.path.exists(self.host):
  165. lserver.debug("file already exists")
  166. lserver.debug("attempting to remove it")
  167. os.remove(self.host)
  168. self.socket.bind(self.host)
  169. self.socket.listen(self.max_allowed_clients)
  170. # self.socket.settimeout(1)
  171. def run(self):
  172. self.setup()
  173. lserver.debug("entering main loop")
  174. while ( not self.exit_event ):
  175. # lserver.debug(self.read_sockets_expected)
  176. # lserver.debug(self.write_sockets_expected)
  177. # lserver.debug(self.exc_sockets_expected)
  178. read_sockets_confirmed, \
  179. write_sockets_confirmed, \
  180. exc_sockets_confirmed \
  181. = select.select(self.read_sockets_expected,
  182. self.write_sockets_expected,
  183. self.exc_sockets_expected,
  184. )
  185. for s in read_sockets_confirmed:
  186. socket_handler = self.conn_to_handler.get(s, None)
  187. if ( s == self.socket ):
  188. lserver.debug("handling new client")
  189. conn, addr = self.socket.accept()
  190. lserver.info(
  191. "New connection from {} on port {}".format(*addr))
  192. lserver.debug("peername: '{}', sockname: '{}'".format(
  193. conn.getpeername(),
  194. conn.getsockname(),
  195. ))
  196. handler = self.make_handler(conn, addr)
  197. self.register_conn(conn, addr)
  198. self.register_handler(handler, conn)
  199. elif ( socket_handler
  200. and (socket_handler in self.connection_handler) ):
  201. lserver.debug("handling client connection")
  202. try:
  203. data = socket_handler.recv()
  204. if not data:
  205. lserver.info("connection {} closed".format(self.socket))
  206. self.unregister_handler(socket_handler, s)
  207. self.unregister_conn(conn)
  208. socket_handler.close()
  209. else:
  210. lschat.info("Client:"+data.strip())
  211. socket_handler.handle(data)
  212. except Exception as e:
  213. lserver.error(e, exc_info=True)
  214. else:
  215. lserver.debug("else!")
  216. lserver.debug(socket_handler)
  217. time.sleep(1)
  218. def cleanup(self):
  219. pass
  220. class Client(threading.Thread):
  221. """docstring for Client"""
  222. is_connecting = False
  223. is_connected = False
  224. status = "uninitialized"
  225. def __init__(self,
  226. host,
  227. port=None,
  228. af_family="AF_INET",
  229. handle_data_func=None,
  230. error_handler=None,
  231. block_size=1024,
  232. ):
  233. self.super_class = super(Client, self)
  234. self.super_class.__init__()
  235. self.name = "Client"
  236. self.exit_event = False
  237. self.host = host
  238. self.port = port
  239. self.af_family = af_family
  240. self.block_size = block_size
  241. self.handle_data_func = handle_data_func
  242. self.is_connected = False
  243. self.error_handler = error_handler
  244. # self.socket = self.make_socket()
  245. self.socket = None
  246. self.status = "disconnected"
  247. def connect(self):
  248. self.status = "connecting"
  249. self.socket = self.make_socket()
  250. lclient.info(
  251. "connecting to socket '{}' of type {}".format(
  252. self.host,
  253. self.af_family
  254. )
  255. )
  256. try:
  257. if self.af_family == "AF_INET":
  258. self.socket.connect((self.host, self.port))
  259. elif self.af_family == "AF_UNIX":
  260. if os.path.exists(self.host):
  261. self.socket.connect(self.host)
  262. else:
  263. lclient.warn("File not found. Aborting.")
  264. return
  265. self.is_connected = True
  266. self.status = "connected"
  267. lclient.info("connected")
  268. return True
  269. except Exception as e:
  270. lclient.debug(e, exc_info=True)
  271. if type(e) is ConnectionRefusedError:
  272. lclient.info("failed to connect to socket '{}'".format(self.host))
  273. self.disconnect()
  274. return False
  275. def disconnect(self):
  276. lclient.info("disconnecting from socket '{}'".format(self.host))
  277. self.is_connected = False
  278. self.status = "disconnected"
  279. if self.socket:
  280. try:
  281. self.socket.shutdown(socket.SHUT_RDWR)
  282. except Exception as e:
  283. lclient.error(e)
  284. try:
  285. self.socket.close()
  286. except Exception as e:
  287. lclient.error("error occured while closing the socket, " +
  288. "maybe it is already closed",exc_info=e)
  289. del self.socket
  290. self.socket = None
  291. def handle_data(self, data_received):
  292. data_decoded = data_received.decode("utf-8")
  293. lcchat.info("Server: "+data_decoded)
  294. if self.handle_data_func:
  295. try:
  296. self.handle_data_func(data_decoded)
  297. except Exception as e:
  298. lclient.error(e, exc_info=True)
  299. def is_running(self):
  300. return (self in threading.enumerate())
  301. def make_socket(self):
  302. lclient.info("creating a {} socket".format(self.af_family))
  303. if self.af_family == "AF_INET":
  304. s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  305. elif self.af_family == "AF_UNIX":
  306. s = socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  307. else:
  308. raise ValueError(
  309. "AF_FAMILY '{}' not supported!".format(
  310. self.af_family
  311. )
  312. )
  313. return s
  314. def main_loop(self):
  315. lclient.debug("starting main loop")
  316. while ( not self.exit_event ):
  317. if not self.status in ["connected"]:
  318. time.sleep(0.1)
  319. continue
  320. # print(0)
  321. read_confirmed, write_confirmed, exc_confirmed \
  322. = select.select(
  323. [self.socket],
  324. [],
  325. [self.socket],
  326. 1
  327. )
  328. if self.socket in exc_confirmed:
  329. self.is_connected = False
  330. lclient.warning("socket is expected to corrupt, exiting")
  331. self.disconnect()
  332. # self.stop()
  333. break
  334. elif self.socket in read_confirmed:
  335. try:
  336. data_received = self.read_from_socket()
  337. if data_received == b'':
  338. lclient.info("connection is broken, closing socket exiting")
  339. self.disconnect()
  340. # self.stop()
  341. # break
  342. else:
  343. try:
  344. self.handle_data(data_received)
  345. except Exception as e:
  346. lserver.error(
  347. "Error while handling data",
  348. exc_info=e
  349. )
  350. except Exception as e:
  351. lclient.error(e, exc_info=True)
  352. if type(e) is OSError:
  353. self.is_connected = False
  354. lclient.warn("connection broken, exiting")
  355. self.disconnect()
  356. # self.stop()
  357. # break
  358. else:
  359. raise
  360. else:
  361. time.sleep(0.1)
  362. def read_from_socket(self):
  363. data_received = self.socket.recv(self.block_size)
  364. return data_received
  365. def run(self):
  366. # self.connect()
  367. if self.error_handler:
  368. self.error_handler(self.main_loop)
  369. else:
  370. self.main_loop()
  371. def send(self, msg):
  372. msg = msg.rstrip()
  373. msg_encoded = bytes(msg+"\r\n", "utf-8")
  374. try:
  375. lcchat.info("Client: "+msg)
  376. self.socket.send(msg_encoded)
  377. except Exception as e:
  378. self.is_connected = False
  379. lclient.error(e, exc_info=True)
  380. self.status = "shutdown"
  381. def setup(self):
  382. pass
  383. def stop(self,reason=None):
  384. self.disconnect()
  385. self.exit_event = True
  386. if reason:
  387. print(reason)
  388. class AsyncClient(object):
  389. """docstring for Client"""
  390. is_connecting = False
  391. is_connected = False
  392. status = "uninitialized"
  393. def __init__(self,
  394. host,
  395. port=None,
  396. af_family="AF_INET",
  397. handle_data_func=None,
  398. error_handler=None,
  399. block_size=1024,
  400. ):
  401. self.super_class = super(AsyncClient, self)
  402. self.super_class.__init__()
  403. self.name = "Client"
  404. self.exit_event = False
  405. self.host = host
  406. self.port = port
  407. self.af_family = af_family
  408. self.block_size = block_size
  409. self.handle_data_func = handle_data_func
  410. self.is_connected = False
  411. self.error_handler = error_handler
  412. self.socket = None
  413. self.status = "disconnected"
  414. def connect(self):
  415. self.status = "connecting"
  416. self.socket = self.make_socket()
  417. lclient.info("connecting to socket '{}' of type {}".format(
  418. self.host,self.af_family))
  419. try:
  420. if self.af_family == "AF_INET":
  421. self.socket.connect((self.host, self.port))
  422. elif self.af_family == "AF_UNIX":
  423. self.socket.connect(self.host)
  424. # if os.path.exists(self.host):
  425. # pass
  426. # else:
  427. # lclient.warn("File not found. Aborting.")
  428. # return
  429. self.is_connected = True
  430. self.status = "connected"
  431. lclient.info("connected")
  432. return True
  433. except Exception as e:
  434. lclient.debug(e, exc_info=True)
  435. if type(e) is ConnectionRefusedError:
  436. lclient.info("failed to connect to socket '{}'".format(self.host))
  437. self.disconnect()
  438. return False
  439. def disconnect(self):
  440. lclient.info("disconnecting from socket '{}'".format(self.host))
  441. self.is_connected = False
  442. self.status = "disconnected"
  443. if self.socket:
  444. try:
  445. self.socket.shutdown(socket.SHUT_RDWR)
  446. except Exception as e:
  447. lclient.error(e)
  448. try:
  449. self.socket.close()
  450. except Exception as e:
  451. lclient.error("error occured while closing the socket, " +
  452. "maybe it is already closed",exc_info=e)
  453. del self.socket
  454. self.socket = None
  455. def handle_data(self, data_received):
  456. data_decoded = data_received.decode("utf-8")
  457. lcchat.info("Server: "+data_decoded)
  458. if self.handle_data_func:
  459. try:
  460. self.handle_data_func(data_decoded)
  461. except Exception as e:
  462. lclient.error(e, exc_info=True)
  463. def is_running(self):
  464. return (self in threading.enumerate())
  465. def make_socket(self):
  466. lclient.info("creating a {} socket".format(self.af_family))
  467. if self.af_family == "AF_INET":
  468. s = trio.socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  469. elif self.af_family == "AF_UNIX":
  470. s = trio.socket.socket(socket.AF_UNIX,socket.SOCK_STREAM)
  471. else:
  472. raise ValueError(
  473. "AF_FAMILY '{}' not supported!".format(
  474. self.af_family
  475. )
  476. )
  477. return s
  478. def main_loop(self):
  479. lclient.debug("starting main loop")
  480. while ( not self.exit_event ):
  481. if not self.status in ["connected"]:
  482. time.sleep(0.1)
  483. continue
  484. # print(0)
  485. read_confirmed, write_confirmed, exc_confirmed \
  486. = select.select(
  487. [self.socket],
  488. [],
  489. [self.socket],
  490. 1
  491. )
  492. if self.socket in exc_confirmed:
  493. self.is_connected = False
  494. lclient.warning("socket is expected to corrupt, exiting")
  495. self.disconnect()
  496. # self.stop()
  497. break
  498. elif self.socket in read_confirmed:
  499. try:
  500. data_received = self.read_from_socket()
  501. if data_received == b'':
  502. lclient.info("connection is broken, closing socket exiting")
  503. self.disconnect()
  504. # self.stop()
  505. # break
  506. else:
  507. self.handle_data(data_received)
  508. except Exception as e:
  509. lclient.error(e, exc_info=True)
  510. if type(e) is OSError:
  511. self.is_connected = False
  512. lclient.warn("connection broken, exiting")
  513. self.disconnect()
  514. # self.stop()
  515. # break
  516. else:
  517. raise
  518. else:
  519. time.sleep(0.1)
  520. def read_from_socket(self):
  521. data_received = self.socket.recv(self.block_size)
  522. return data_received
  523. def run(self,connect=False):
  524. if connect:
  525. self.connect()
  526. if self.error_handler:
  527. self.error_handler(self.main_loop)
  528. else:
  529. self.main_loop()
  530. def send(self, msg):
  531. msg = msg.rstrip()
  532. msg_encoded = bytes(msg+"\r\n", "utf-8")
  533. try:
  534. lcchat.info("Client: "+msg)
  535. self.socket.send(msg_encoded)
  536. except Exception as e:
  537. self.is_connected = False
  538. lclient.error(e, exc_info=True)
  539. self.status = "shutdown"
  540. def setup(self):
  541. pass
  542. def stop(self,reason=None):
  543. self.disconnect()
  544. self.exit_event = True
  545. if reason:
  546. print(reason)
  547. #