__init__.py 19 KB

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