概念

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程

通俗理解(摘自网上一大神)

这些名词比较绕口,理解涵义就好。一个epoll场景:一个酒吧服务员(一个线程),前面趴了一群醉汉,突然一个吼一声“倒酒”(事件),你小跑过去给他倒一杯,然后随他去吧,突然又一个要倒酒,你又过去倒上,就这样一个服务员服务好多人,有时没人喝酒,服务员处于空闲状态,可以干点别的玩玩手机。至于epoll与select,poll的区别在于后两者的场景中醉汉不说话,你要挨个问要不要酒,没时间玩手机了。io多路复用大概就是指这几个醉汉共用一个服务员。

三个函数

1、select

进程指定内核监听哪些文件描述符(最多监听1024个fd)的哪些事件,当没有文件描述符事件发生时,进程被阻塞;当一个或者多个文件描述符事件发生时,进程被唤醒。

当我们调用select()时:

  1 上下文切换转换为内核态

  2 将fd从用户空间复制到内核空间

  3  内核遍历所有fd,查看其对应事件是否发生

  4  如果没发生,将进程阻塞,当设备驱动产生中断或者timeout时间后,将进程唤醒,再次进行遍历

  5 返回遍历后的fd

  6  将fd从内核空间复制到用户空间

  1. fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])
  2.  
  3. 参数: 可接受四个参数(前三个必须)
  4. rlist: wait until ready for reading
  5. wlist: wait until ready for writing
  6. xlist: wait for an exceptional condition
  7. timeout: 超时时间
  8.  
  9. 返回值:三个列表
  10.  
  11. select方法用来监视文件描述符(当文件描述符条件不满足时,select会阻塞),当某个文件描述符状态改变后,会返回三个列表
  12. 1、当参数1 序列中的fd满足“可读”条件时,则获取发生变化的fd并添加到fd_r_list
  13. 2、当参数2 序列中含有fd时,则将该序列中所有的fd添加到 fd_w_list
  14. 3、当参数3 序列中的fd发生错误时,则将该发生错误的fd添加到 fd_e_list
  15. 4、当超时时间为空,则select会一直阻塞,直到监听的句柄发生变化
  16. 当超时时间 n(正整数)时,那么如果监听的句柄均无任何变化,则select会阻塞n秒,之后返回三个空列表,如果监听的句柄有变化,则直接执行。

在服务端我们可以看到,我们需要不停的调用select, 这就意味着:

  1  当文件描述符过多时,文件描述符在用户空间与内核空间进行copy会很费时

  2  当文件描述符过多时,内核对文件描述符的遍历也很浪费时间

  3  select最大仅仅支持1024个文件描述符

参考:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html

2、poll

参考:http://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html

3、epoll

参考:http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

epoll是select和poll改进后的结果,相比下epoll具有以下优点:

1、支持一个进程打开的socket描述符(FD)不受限制(仅受限于操作系统的最大文件句柄数)

select最大的缺陷就是单个进程所打开的FD是有一定限制的,它由FD_SETSIZE设置,默认值是1024,epoll并没有这个限制,它所支持的FD上限是操作系统的最大文件句柄数,这个数字远远大于1024

2、I/O效率不会随着FD数目的增加而线性下降

 epoll的解决方案在epoll_ctl函数中。每次注册新的事件到epoll句柄中时,会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝。epoll保证了每个fd在整个过程中只会拷贝一次

传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,由于网络延时或者链路空闲,任一时刻只有少部分的socket是“活跃”的,但是select/poll每次调用都会线性扫描全部集合,导致效率呈现线性下降。epoll不存在这个问题,它只会对“活跃”的socket进行操作-这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的,那么,只有“活跃”的socket才会主动的去调用callback函数,其他idle状态socket则不会。在这点上,epoll实现了一个伪AIO

3、使用mmap加速内核与用户空间的消息传递

epoll会在epoll_ctl时把指定的fd遍历一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd

无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存复制就显得非常重要,epoll是通过内核和用户空间mmap使用同一块内存实现。

4、epoll的API更加简单

用来克服select/poll缺点的方法不只有epoll,epoll只是一种Linux的实现方案。在freeBSD下有kqueue,而dev/poll是最古老的Solaris的方案,使用难度依次递增。但epoll更加简单。

epoll详解(python中)

Python中的select模块专注于I/O多路复用,提供了select  poll  epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kqueue方法(freeBSD系统)

select.epoll(sizehint=-1, flags=0) 创建epoll对象

  1. epoll.close()
  2. Close the control file descriptor of the epoll object.关闭epoll对象的文件描述符
  3.  
  4. epoll.closed
  5. True if the epoll object is closed.检测epoll对象是否关闭
  6.  
  7. epoll.fileno()
  8. Return the file descriptor number of the control fd.返回epoll对象的文件描述符
  9.  
  10. epoll.fromfd(fd)
  11. Create an epoll object from a given file descriptor.根据指定的fd创建epoll对象
  12.  
  13. epoll.register(fd[, eventmask])
  14. Register a fd descriptor with the epoll object.向epoll对象中注册fd和对应的事件
  15.  
  16. epoll.modify(fd, eventmask)
  17. Modify a registered file descriptor.修改fd的事件
  18.  
  19. epoll.unregister(fd)
  20. Remove a registered file descriptor from the epoll object.取消注册
  21.  
  22. epoll.poll(timeout=-1, maxevents=-1)
  23. Wait for events. timeout in seconds (float)阻塞,直到注册的fd事件发生,会返回一个dict,格式为:{(fd1,event1),(fd2,event2),……(fdn,eventn)}

事件:

  1. EPOLLERR = 8 ----发生错误
  2. EPOLLET = 2147483648 ----默认为水平触发,设置该事件后则边缘触发
  3. EPOLLHUP = 16 ----挂起状态
  4. EPOLLIN = 1 ----可读
  5. EPOLLMSG = 1024 ----忽略
  6. EPOLLONESHOT = 1073741824 ----一次性行为。在退出一个事件后,FD内部禁用
  7. EPOLLOUT = 4 ----可写
  8. EPOLLPRI = 2 ----紧急可读
  9. EPOLLRDBAND = 128 ----读取优先
  10. EPOLLRDNORM = 64 ----相当于epollin
  11. EPOLLWRBAND = 512 ----写入优先
  12. EPOLLWRNORM = 256 ----相当于epollout

水平触发和边缘触发:

Level_triggered(水平触发,有时也称条件触发):当被监控的文件描述符上有可读写事件发生时,epoll.poll()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll.poll()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!! 优点很明显:稳定可靠

Edge_triggered(边缘触发,有时也称状态触发):当被监控的文件描述符上有可读写事件发生时,epoll.poll()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll.poll()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!缺点:某些条件下不可靠

  1. #!/usr/bin/python
  2. # coding:utf-8
  3.  
  4. import select,socket
  5. import time
  6.  
  7. EOL1 = b'\n\n'
  8. EOL2 = b'\n\r\n'
  9. response = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
  10. response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
  11. response += b'Hello, world!'
  12.  
  13. sk = socket.socket()
  14. sk.bind(('192.168.110.100',8080))
  15. sk.listen(5)
  16. sk.setblocking(0) #设置非阻塞模式
  17.  
  18. epoll = select.epoll() #建立一个epoll对象
  19. epoll.register(sk.fileno(),select.EPOLLIN) #监听sk文件描述符的读事件(连接过来,产生读事件)
  20. try:
  21. connections = {}; requests = {}; responses = {}
  22. while True:
  23. events = epoll.poll() #关注是否有关心的事发生
  24. for fileno,event in events: # 返回的events是一个(fileno, event code)tuple列表. fileno是文件描述符, 是一个整型数.
  25. if fileno == sk.fileno(): #如果是服务器socket事件(即有新连接),建立一个新 连接
  26. connection, address = sk.accept() #建立的新连接
  27. connection.setblocking(0) #设置socket为非阻塞模式.
  28. epoll.register(connection.fileno(), select.EPOLLIN) # 注册socket的read(EPOLLIN)事件
  29. connections[connection.fileno()] = connection # 保存文件描述符
  30. requests[connection.fileno()] = b'' #发送过来的内容
  31. responses[connection.fileno()] = response # 要发送的内容
  32.  
  33. elif event & select.EPOLLIN: #如果读事件发生
  34. requests[fileno] += connections[fileno].recv(1024)# 从客户端读取信息
  35. if EOL1 in requests[fileno] or EOL2 in requests[fileno]: #表示信息接收完毕,结束标志
  36. epoll.modify(fileno, select.EPOLLOUT)#一旦完整的http请求接收到,取消注册读取事件,注册写入事件(EPOLLOUT), 写入事件在能够发送数据回客户端的时候产生
  37. print('-'*40 + '\n' + requests[fileno].decode()[:-2])
  38.  
  39. elif event & select.EPOLLOUT: #如果写入事件发生在一个客户端socket上面, 我们就可以发送新数据到客户端了.
  40. byteswritten = connections[fileno].send(responses[fileno]) #发送数据到客户端,并返回发送的字节个数
  41. responses[fileno] = responses[fileno][byteswritten:] #对字符串进行切片操作,如果完全切,表面发送完毕
  42. if len(responses[fileno]) == 0: #表明数据发送完毕
  43. epoll.modify(fileno, 0) #一旦所有的返回数据都发送完, 取消监听读取和写入事件.
  44. connections[fileno].shutdown(socket.SHUT_RDWR)
  45.  
  46. elif event & select.EPOLLHUP: #表示客户端断开连接
  47. epoll.unregister(fileno) #取消注册
  48. connections[fileno].close() #断开连接.
  49. del connections[fileno] #销毁对象
  50.  
  51. finally:
  52. epoll.unregister(sk.fileno())
  53. epoll.close()
  54. serversocket.close()

服务端

  1. #!/usr/bin/python
  2. # coding:utf-8
  3.  
  4. import socket
  5. obj = socket.socket()
  6. obj.connect(('192.168.110.100',8080))
  7. obj.sendall('hellob\n\r\n')
  8. print obj.recv(1024)
  9. obj.close()

客户端

实战代码:

  1. # /usr/bin/python
  2. # coding:utf-8
  3.  
  4. import select
  5. import socket
  6. import sys
  7. import Queue
  8. import time
  9. import threading
  10. import logging
  11. import datetime
  12. import re, os
  13. import hashlib
  14.  
  15. sys.path.append('../')
  16.  
  17. import multiprocessing
  18.  
  19. from SQLdb import SQLdb
  20. from mylog import MyLog as Log
  21.  
  22. from communication_packet import Communication_Packet, Communication_Packet_Flags, Error_Info_Flags
  23. from encryption import PrpCrypt
  24. import pdb
  25.  
  26. '''
  27. Constant Meaning
  28. EPOLLIN Available for read
  29. EPOLLOUT Available for write
  30. EPOLLPRI Urgent data for read
  31. EPOLLERR Error condition happened on the assoc. fd
  32. EPOLLHUP Hang up happened on the assoc. fd
  33. EPOLLET Set Edge Trigger behavior, the default is Level Trigger behavior
  34. EPOLLONESHOT Set one-shot behavior. After one event is pulled out, the fd is internally disabled
  35. EPOLLRDNORM Equivalent to EPOLLIN
  36. EPOLLRDBAND Priority data band can be read.
  37. EPOLLWRNORM Equivalent to EPOLLOUT
  38. EPOLLWRBAND Priority data may be written.
  39. EPOLLMSG Ignored.
  40.  
  41. '''
  42.  
  43. class Server(object):
  44. def __init__(self, server_IP=None, server_port=None):
  45. # def __init__(self,server_address = ('112.33.9.154',11366)):
  46. '''
  47. 初始化服务器一些全局数据
  48. '''
  49. # pdb.set_trace()
  50.  
  51. # 使用默认模式:debug模式
  52. self.log = Log()
  53. self.log.openConsole() # 打开控制端输出
  54. self.logger = self.log.getLog()
  55. self.dbname = 'sync_test'
  56.  
  57. # Defaut, we use local host IP and port:11366
  58. if server_IP is None or server_port is None:
  59. if server_IP is None:
  60. try:
  61. self.server_IP = self.getlocalIP()
  62. self.logger.info('Current server_IP: %s' % self.server_IP)
  63. except:
  64. self.logger.critical('Get server IP Error!')
  65. raise
  66.  
  67. if server_port is None:
  68. self.server_port = 11366
  69. else:
  70. self.server_IP = server_IP
  71. self.server_port = server_port
  72.  
  73. self.server_address = (self.server_IP, self.server_port) # 设置server地址
  74. self.ListenNum = 100 # 设置最大监控soket连接数
  75. self.connections = {} # 记录当前连接
  76. self.requests = {} # 记录当前连接的请求数据
  77. self.addresses = {} # 记录客户端地址
  78. self.errorInfo = {} # 记录错误信息,如果出错则把错误信息返回给客户端
  79. self.responseInfo = {}
  80. self.readthreadRecord = {}
  81.  
  82. self.lock = threading.Lock() # 构造线程锁用于数据同步
  83. self.db = SQLdb() # 初始化数据库用于数据库同步
  84. self.setDB('localhost', 'root', '', 'sync_test')
  85.  
  86. self.readthreadlock = threading.Lock()
  87.  
  88. self.EOF = '\n\r\n'
  89. self.servernum = 'serverxxxxx'
  90. self.key = '91keytest'
  91. # set communication user id
  92. Communication_Packet.set_userid(self.servernum)
  93. self.encryption = PrpCrypt()
  94. pass
  95.  
  96. def setServerAddree(self, server_ip, server_port):
  97. '''
  98. Set server address
  99. '''
  100. self.server_address = (server_ip, server_port) # 设置server地址
  101.  
  102. def setDB(self, host=None, username=None, password=None, dbname=None):
  103. self.db = SQLdb(host, username, password, dbname)
  104.  
  105. def getlocalIP(self):
  106. '''
  107. 获取第一块网卡i做为绑定IP
  108. '''
  109. try:
  110. s = os.popen('ifconfig').read()
  111. except:
  112. raise
  113. else:
  114. ip = re.findall('inet addr:(?<![\.\d])(?:\d{1,3}\.){3}\d{1,3}(?![\.\d])', s)[0].split(':')[1]
  115. return ip
  116.  
  117. def __init_server(self):
  118. '''
  119. 初始化server以及epoll监控对象
  120. '''
  121. try:
  122. # pdb.set_trace()
  123. # Create a TCP/IP socket
  124. self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  125. self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  126. self.server.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  127.  
  128. # 设置socket为非阻塞状态
  129. self.server.setblocking(0)
  130.  
  131. # Bind the socket to the port
  132. self.logger.info('starting up on %s port %s' % self.server_address)
  133. try:
  134. self.server.bind(self.server_address)
  135. except:
  136. echo = os.popen('''lsof -i :11366 |grep "(LISTEN)" | awk '{printf($1)}' ''').read()
  137. print '端口%s被%s进程占用!' % (self.server_port, echo)
  138. self.logger.error('Bind on %s port %s Error!' % self.server_address)
  139. raise
  140.  
  141. # Listen for incoming connections
  142. # self.server.listen(self.ListenNum)
  143. self.server.listen(1)
  144.  
  145. # Set up the epoll
  146. self.epoll = select.epoll()
  147. self.epoll.register(self.server.fileno(), select.EPOLLIN | select.EPOLLET) # 为server注册读事件,并设置其为边缘出发模式
  148. except:
  149. raise Exception('__init_server Error')
  150.  
  151. def __jionthreads(self):
  152. '''
  153. join the threading
  154. '''
  155. # self.logger.debug('Current threadtast is %d ' % len(threading.enumerate()))
  156. main_thread = threading.currentThread()
  157. for t in threading.enumerate():
  158. if t is main_thread:
  159. continue
  160. else:
  161. t.join(0) # 非阻塞join
  162.  
  163. # self.logger.debug('After joined the threads... %d '% len(threading.enumerate()))
  164.  
  165. def __format_str(self, businessFlag, data, endFlag=True, errorFlag=False, hasnewconf=False, versioninfo=''):
  166. '''
  167. 格式化发送数据
  168. '''
  169. formatstr = {'BUSINESS_TYPE': businessFlag, 'DATA': data, 'ENDFLAG': endFlag, 'ERRORFLAG': errorFlag,
  170. 'HASNWECONF': hasnewconf, 'VERSIONINFO': versioninfo}
  171. return str(formatstr) + self.EOF
  172.  
  173. def get_table_filed(self, table_name, db, lock, db_name):
  174. # pdb.set_trace()
  175. # from the db get the table field!
  176. query_detection_version_field_sql = "select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s' and TABLE_SCHEMA = '%s';" % (
  177. table_name, db_name)
  178. with self.lock:
  179. detection_version_fields = self.db.fechdb(query_detection_version_field_sql)
  180. DB_ERROR = self.db.Error
  181. if DB_ERROR:
  182. # Record Error and end task
  183. DB_ERROR = False
  184. self.logger.error('----Get %s fileds Error! End this task-----' % table_name)
  185. return
  186. else:
  187. # query result is Unicode,so we need to encode to utf-8
  188. table_field = [field[0].encode('utf-8') for field in detection_version_fields]
  189. return table_field
  190.  
  191. def calc_md5(self, data):
  192. return hashlib.md5(data).hexdigest()
  193.  
  194. def validating_message_token(self, receving_data):
  195. # print receving_data
  196. # pdb.set_trace()
  197. # print receving_data
  198. print len(receving_data)
  199. pre_md5 = receving_data[:16]
  200. suffix_md5 = receving_data[-16:]
  201. message_md5 = pre_md5 + suffix_md5
  202. message = receving_data[16:-16]
  203. cur_md5 = self.calc_md5(message)
  204. print cur_md5, message_md5
  205. if message_md5 == cur_md5:
  206. return True, message
  207. else:
  208. return False, message
  209. pass
  210.  
  211. def validating_content_token(self, content):
  212. receive_content = content['Business']['Content']
  213. if not isinstance(receive_content, str):
  214. receive_content = str(receive_content)
  215. receive_md5 = content['Info_Status']['Security_Token']
  216. receive_time = content['Info_Status']['Time']
  217. cur_md5 = self.calc_md5(receive_content + receive_time + self.key)
  218. if cur_md5 == receive_md5:
  219. return True
  220. else:
  221. return False
  222. pass
  223.  
  224. def packaging_message(self, Message=None, Error=None, Info_Status=None):
  225. # ----Pack send message
  226. # Init Communication_Packet
  227. cm_packet = Communication_Packet()
  228.  
  229. # def set_content_Business(self,b_type,b_content,table_name = None,table_field = None,b_is_syncdb = True):
  230. cm_packet.set_content_Business(targv=Message)
  231.  
  232. # def set_content_Error(self,error_flag = False,error_type = None,error_info = None):
  233. cm_packet.set_content_Error(targv=Error)
  234.  
  235. now_time = str(datetime.datetime.now()) # get current time
  236. # Business+time+key calculate token,calculate token
  237. security_token = self.calc_md5(str(cm_packet.CMC_Business) + now_time + self.key)
  238. # def set_content_Info_Status(self,info_type,security_token,time,is_end):
  239. # Need to replace the security_token
  240. Info_Status = list(Info_Status) # 转化元组到列表
  241. Info_Status[1] = security_token
  242. Info_Status[2] = now_time
  243. Info_Status = tuple(Info_Status) # 重新转化列表到元组作为参数
  244.  
  245. cm_packet.set_content_Info_Status(targv=Info_Status)
  246. try:
  247. send_data = cm_packet.content
  248. except Exception, e:
  249. raise e
  250. else:
  251. # we Encryption data
  252. self.logger.debug(type(send_data))
  253. encryption_send_data = self.encryption.encrypt(str(send_data))
  254. # caculate md5
  255. encrypt_send_md5 = self.calc_md5(encryption_send_data)
  256. complete_send_data = encrypt_send_md5[:16] + encryption_send_data + encrypt_send_md5[-16:] + self.EOF
  257. return complete_send_data
  258.  
  259. def unpackaging_message(self, unpacking_str):
  260. # pdb.set_trace()
  261. if not isinstance(unpacking_str, str):
  262. raise exceptions.ValueError
  263. else:
  264. unpacking_str = unpacking_str.strip(self.EOF)
  265. flag, message = self.validating_message_token(unpacking_str)
  266. if flag:
  267. decrypt_str = self.encryption.decrypt(message)
  268. try:
  269. message_dict = eval(decrypt_str)
  270. except Exception, e:
  271. self.logger.error('Eval decrypt_str Error!')
  272. raise e
  273. else:
  274. if self.validating_content_token(message_dict):
  275. return message_dict
  276. else:
  277. self.logger.error('Message is tampered!')
  278. return None
  279. pass
  280. else:
  281. self.logger.error('Message is tampered!')
  282. return None
  283.  
  284. def init_detection_nums(self):
  285. # get detect_point_nums from server db
  286. # pdb.set_trace()
  287. query_sql = "select distinct(sync_point_no) from sync_control"
  288. BD_ERROR = False
  289. with self.lock:
  290. point_nums = self.db.fechdb(query_sql)
  291. BD_ERROR = self.db.Error
  292. if BD_ERROR:
  293. self.logger.error('-----get detect_point_nums error-----')
  294.  
  295. if point_nums:
  296. nums_list = [p[0] for p in point_nums]
  297. return nums_list
  298. else:
  299. return None
  300.  
  301. def verification_sync_point_no(self, detection_num):
  302. detect_point_nums = self.init_detection_nums()
  303. if not detect_point_nums:
  304. self.logger.error('-----db config error!-----')
  305. raise
  306. if detection_num in detect_point_nums:
  307. return True
  308. else:
  309. return False
  310.  
  311. def read_sync_contol_configure(self, table_name, sync_type, sync_point_no, sync_user='server'):
  312. # Read the control configuration from loacal db,if have not,we sync it from server,then read it again
  313. # pdb.set_trace()
  314. qeury_sql = "select * from sync_control where sync_table = '%s' and sync_type = '%s' and sync_user = '%s' and sync_point_no = '%s';" % (
  315. table_name, sync_type, sync_user, sync_point_no)
  316. DB_ERROR = False
  317.  
  318. # set table name
  319. sync_table_name = 'sync_control'
  320.  
  321. # get `sync_control` table fields
  322. table_field = self.get_table_filed(sync_table_name, self.lock, self.db, self.dbname)
  323. if not table_field:
  324. self.logger.error('----------' % table_name)
  325. return None
  326.  
  327. with self.lock:
  328. control_configure = self.db.fechdb(qeury_sql)
  329. DB_ERROR = self.db.Error
  330. if DB_ERROR:
  331. self.logger.error('-----Get control configure Error!-----')
  332. return None
  333. if control_configure:
  334. # Get the configure from db! and On the basis of classification of table name and sync type(uploat or download)
  335. # format the configure to a list
  336. control_configure_list = []
  337. for iter_conf in control_configure:
  338. control_configure_item = dict.fromkeys(table_field)
  339. lenth = len(table_field)
  340. # set value for everyone key
  341. for i in range(lenth):
  342. val = iter_conf[i]
  343. if isinstance(val, unicode):
  344. val = val.encode('utf-8')
  345. control_configure_item[table_field[i]] = val
  346. control_configure_list.append(control_configure_item)
  347. return control_configure_list
  348.  
  349. def parsing_config(self, config):
  350. # parsing the config to get the sql
  351. # may modify the logic of the code
  352. # pdb.set_trace()
  353. p_conf = dict()
  354. table_name = config['sync_table'] # Get the download table name
  355. p_conf['table_name'] = table_name
  356. table_field = config['sync_field'] # Get sync table field!
  357. p_conf['sync_type'] = config['sync_type']
  358. p_conf['sync_range'] = config['sync_range']
  359. p_conf['sync_range_value'] = config['sync_range_value']
  360. p_conf['sync_is_update_time'] = config['sync_is_update_time']
  361.  
  362. # if table_field is null,we need sync all the field!
  363. if not table_field:
  364. table_field = self.get_table_filed(table_name, self.lock, self.db, self.dbname)
  365. if not table_field:
  366. self.logger.error(
  367. '-----Terminate this task,becase of getting the %s table fileds fialed!-----' % table_name)
  368. return
  369. p_conf['table_field'] = table_field
  370. # Get this operation's type
  371. try:
  372. sql_operations = eval(config['sync_operation_type'])
  373. except Exception, e:
  374. self.logger.error('-----get sync_operation_type error!-----')
  375. return
  376.  
  377. upside_operate = sql_operations['upside'] # if have, this download operation need carry db info to the server!
  378. p_conf['upside_operate'] = upside_operate
  379. downside_operate = sql_operations['downside'] # how to handle the downloaded db info!
  380. p_conf['downside_operate'] = downside_operate
  381. update_state_operate = sql_operations['update_state']
  382. p_conf['update_state_operate'] = update_state_operate
  383.  
  384. # Get the sync sql of the corresponding operation
  385. try:
  386. sqls = eval(config['sync_sql'])
  387. except Exception, e:
  388. self.logger.error('-----get sync_sql error!-----')
  389. raise
  390.  
  391. upside_sqls = sqls['upside'] # a tuple or None
  392. p_conf['upside_sqls'] = upside_sqls
  393.  
  394. downside_sqls = sqls['downside'] # a tuple or None
  395. p_conf['downside_sqls'] = downside_sqls
  396.  
  397. update_state_sqls = sqls['update_state'] # a tuple or None
  398. p_conf['update_state_sqls'] = update_state_sqls
  399.  
  400. # Get the sync patch field of the corresponding operation
  401. try:
  402. if config['sync_patch_field']:
  403. patch_fields = eval(config['sync_patch_field'])
  404. else:
  405. pass
  406. except Exception, e:
  407. self.logger.error('-----get sync_field error!-----')
  408. raise
  409.  
  410. upside_fields = patch_fields['upside'] # a tuple or None
  411. p_conf['upside_fields'] = upside_fields
  412.  
  413. downside_fields = patch_fields['downside'] # a tuple or None
  414. p_conf['downside_fields'] = downside_fields
  415.  
  416. update_state_fields = patch_fields['update_state'] # a tuple or None
  417. p_conf['update_state_fields'] = update_state_fields
  418.  
  419. # Get the sync_field_value of the corresponding operation
  420. try:
  421. if config['sync_patch_field_value']:
  422. patch_field_values = eval(config['sync_patch_field_value'])
  423. else:
  424. pass
  425. except Exception, e:
  426. self.logger.error('-----get sync_field_value error!-----')
  427. return
  428.  
  429. upside_patch_field_values = patch_field_values['upside'] # a tuple or None
  430. p_conf['upside_patch_field_values'] = upside_patch_field_values
  431.  
  432. downside_patch_field_values = patch_field_values['downside'] # a tuple or None
  433. p_conf['downside_patch_field_values'] = downside_patch_field_values
  434.  
  435. update_state_patch_field_values = patch_field_values['update_state'] # a tuple or None
  436. p_conf['update_state_patch_field_values'] = update_state_patch_field_values
  437.  
  438. is_carry_state = config['sync_is_carry_state']
  439. p_conf['is_carry_state'] = is_carry_state
  440.  
  441. is_update_state = config['sync_is_update_state']
  442. p_conf['is_update_state'] = is_update_state
  443.  
  444. is_use_state_carry_data = config['sync_is_use_state_carry_data']
  445. p_conf['is_use_state_carry_data'] = is_use_state_carry_data
  446.  
  447. return p_conf
  448.  
  449. def __proxy(self, FileNo):
  450. # 启动线程处理写事件
  451. newthread = threading.Thread(target=self.__handler_write_event, args=(FileNo,))
  452. # newthread.daemon = True
  453. newthread.start()
  454.  
  455. def __delevnet(self, FileNo):
  456. '''
  457. 注销不再关注的事件以及删除相关的资源
  458. '''
  459. self.logger.info('Start to unregister and close %s socket... ' % FileNo)
  460. self.epoll.unregister(FileNo)
  461. self.connections[FileNo].close()
  462. del self.connections[FileNo]
  463. del self.requests[FileNo]
  464. del self.addresses[FileNo]
  465. del self.errorInfo[FileNo]
  466. self.logger.info('unregistered and closed the %s socket! ' % FileNo)
  467.  
  468. # def __read_from_socket(self,FileNo):
  469. # '''
  470. # Read data from socket
  471. # '''
  472. # if self.requests[FileNo]:
  473. # return False
  474. # else:
  475. # try:
  476. # while True:
  477. # tmpdata = self.connections[FileNo].recv(4096)
  478. # if not tmpdata:
  479. # return True
  480. # self.requests[FileNo] += tmpdata
  481. # self.logger.debug(len(tmpdata))
  482. # #print tmpdata
  483. # except socket.error:
  484. # return True
  485. # except Exception,e:
  486. # raise e
  487. def __read_from_socket(self, FileNo):
  488. '''
  489. Read data from socket
  490. '''
  491. try:
  492. while True:
  493. tmpdata = self.connections[FileNo].recv(4096)
  494. # print 'tmpdata: %s' % tmpdata
  495. # 因为python没有EPOLLRDHUP,而客户端主动关闭或者没有发送数据前ctr+c
  496. # 服务器触发的是EPOLLIN事件,而从socket里面读取到的数据为空...没有找到其他解决方案!
  497. if not tmpdata:
  498. break
  499. self.requests[FileNo] += tmpdata
  500. # self.logger.debug(len(tmpdata))
  501. except socket.error:
  502. pass
  503. except Exception, e:
  504. raise e
  505.  
  506. # error_flag = False,error_type = None,error_info = None
  507. def __deal_business(self, FileNo):
  508. # 根据接受到的数据处理客户端业务
  509. # pdb.set_trace()
  510. try:
  511. message = self.unpackaging_message(self.requests[FileNo])
  512. # we need reset the requests info
  513. self.requests[FileNo] = ''
  514. # self.logger.debug(message)
  515. except:
  516. # 需要设置错误标志并却设置错误信息
  517. self.logger.error('unpackaging_message Error!')
  518. self.errorInfo[FileNo] = (Error_Info_Flags.Receive_Data_Error, 'Server recieved data Error!')
  519. return
  520. else:
  521. if message:
  522. business = message['Business']
  523. client_id = message['userid']
  524. error_info = message['Error']
  525. info_states = message['Info_Status']
  526. verification_result = self.verification_sync_point_no(client_id)
  527. if verification_result:
  528. # Here we handle the business
  529. # 1、get config from the db
  530. # read_sync_contol_configure(self,table_name,sync_type,sync_point_no,sync_user):
  531. try:
  532. table_name = business['Table_Name']
  533. sync_type = business['Type']
  534. is_sync_flag = business['Is_Syncdb']
  535. except:
  536. self.errorInfo[FileNo] = (
  537. Error_Info_Flags.Receive_Data_Error, 'Business information is incomplete!')
  538. return
  539. if sync_type == Communication_Packet_Flags.DOLOAD_DB:
  540. s_type = 'download'
  541. else:
  542. s_type = 'upload'
  543. b_config = self.read_sync_contol_configure(table_name, s_type, client_id)
  544. # pdb.set_trace()
  545. if b_config:
  546. p_config = [self.parsing_config(conf) for conf in b_config]
  547. self.logger.debug(p_config)
  548. else:
  549. pass
  550. if b_config:
  551. self.real_business_processing_functions(FileNo, p_config, business, info_states, error_info)
  552. else:
  553. # set error info!
  554. self.errorInfo[FileNo] = (
  555. Error_Info_Flags.Server_config_Error, 'Server config is None, give up this task!')
  556.  
  557. else:
  558. # 用户信息认证失败
  559. self.logger.error('-----User authentication failed! userid: %s-----' % client_id)
  560. self.errorInfo[FileNo] = (Error_Info_Flags.User_Certification_Error, 'User authentication failed!')
  561. else:
  562. # if no message,it means information authenticationfailed!
  563. self.logger.error('-----Clinet\'s Information authentication failed!-----')
  564. self.errorInfo[FileNo] = (
  565. Error_Info_Flags.Info_Certification_Error, 'Information authentication failed!')
  566.  
  567. def calculate_time(self, time_type, time_value):
  568. # maybe is str,we need to convert it to int type
  569. time_value = int(time_value)
  570.  
  571. # get current time as the end time
  572. cur_time = datetime.datetime.now()
  573. hours = 0
  574. if time_type == 'hour':
  575. hours = time_value * 24
  576. elif time_type == 'day':
  577. hours = time_value * 24
  578. elif time_type == 'week':
  579. hours = time_value * 24 * 7
  580. elif time_type == 'month':
  581. hours = time_value * 24 * 30
  582. else:
  583. self.logger.error('-----time_type Error!-----')
  584. return None
  585. # caculate the start time
  586. start_time = cur_time - datetime.timedelta(hours=hours)
  587. return (start_time, cur_time)
  588.  
  589. # handle the bussiness from the client
  590. def real_business_processing_functions(self, FileNo, business_config, business, info_states, error_info):
  591. # pdb.set_trace()
  592. # according to the config we handle the business
  593. business_config = business_config[0]
  594. if info_states['Info_Type'] == Communication_Packet_Flags.REQEST:
  595. # get bussiness type
  596. request_bussiness_type = business['Type']
  597. if request_bussiness_type == Communication_Packet_Flags.UPLOAD_DB:
  598. request_bussiness_type = 'upload'
  599. elif request_bussiness_type == Communication_Packet_Flags.DOLOAD_DB:
  600. request_bussiness_type = 'download'
  601. else:
  602. self.errorInfo[FileNo] = (
  603. Error_Info_Flags.Client_Data_Pack_Error, 'Request business type error %s' % request_bussiness_type)
  604. return
  605.  
  606. loc_config_sync_type = business_config['sync_type']
  607. if request_bussiness_type == loc_config_sync_type:
  608.  
  609. is_carry_state = business_config['is_carry_state']
  610. is_use_state_carry_data = business_config['is_use_state_carry_data']
  611. is_update_state = business_config['is_update_state']
  612. # handle the download request
  613. if request_bussiness_type == 'download':
  614. # parsing the loacal config
  615. up_sql_list = []
  616. upside_operates = business_config['upside_operate'].split('|')
  617. upside_sqls = business_config['upside_sqls']
  618. upside_fields = business_config['upside_fields']
  619. upside_patch_field_values = business_config['upside_patch_field_values']
  620. sync_range = business_config['sync_range']
  621. sync_range_value = business_config['sync_range_value']
  622.  
  623. lenth = len(upside_sqls)
  624. for i in range(lenth):
  625. sql_part = upside_sqls[i]
  626.  
  627. # if sync_range is not None,we will ignore the other
  628. if sync_range:
  629. if sync_range == 'period':
  630. t_type, t_value = sync_range_value.split(':')
  631. s_time, e_time = self.calculate_time(t_type, t_value)
  632. qeury_sql = sql_part % (str(s_time), str(e_time))
  633. else:
  634. qeury_sql = sql_part
  635. # add it into the list
  636. up_sql_list.append(qeury_sql)
  637. else:
  638. # we need parsing other configurations
  639. if is_use_state_carry_data:
  640. try:
  641. # [((u'update_time', u'1970-01-01 00:00:00'),)] limk this
  642. qeury_sql = sql_part % business['Content'][0][0][1]
  643. except:
  644. self.errorInfo[FileNo] = (Error_Info_Flags.Client_Data_Pack_Error, 'Content Error!')
  645. else:
  646. qeury_sql = sql_part
  647. up_sql_list.append(qeury_sql)
  648. query_data = []
  649. for u_sql in up_sql_list:
  650. BD_ERROR = False
  651. with self.lock:
  652. res = self.db.fechdb(u_sql)
  653. BD_ERROR = self.db.Error
  654. if BD_ERROR:
  655. self.errorInfo[FileNo] = (
  656. Error_Info_Flags.Server_DB_Error, 'Server db Error,SQL: %s' % u_sql)
  657. break
  658. else:
  659. query_data.append(res)
  660. self.responseInfo[FileNo] = query_data
  661. # handle the upload request
  662. elif request_bussiness_type == 'upload':
  663. # pdb.set_trace()
  664. # parsing the loacal config
  665. content = business['Content']
  666. try:
  667. self.refresh_the_database(business_config, content)
  668. except Exception, e:
  669. print e
  670. self.errorInfo[FileNo] = (Error_Info_Flags.Server_config_Error, 'Server Config Error!')
  671.  
  672. else:
  673. self.errorInfo[FileNo] = (Error_Info_Flags.Client_Data_Pack_Error, 'bussiness type Error!')
  674. else:
  675. self.errorInfo[FileNo] = (Error_Info_Flags.Client_Data_Pack_Error,
  676. 'server config type is different from client request business type! Error!')
  677. else:
  678. self.errorInfo[FileNo] = (Error_Info_Flags.Client_Data_Pack_Error, 'Communication_Packet_Flags Error!')
  679.  
  680. # update the db
  681. def refresh_the_database(self, handle_config, db_content):
  682. '''
  683. refresh the database,maybe insert、update、delete...
  684. '''
  685. # parsing the handle config
  686. table_name = handle_config['table_name']
  687. table_field = handle_config['table_field']
  688. downside_operate = handle_config['downside_operate']
  689. update_state_operate = handle_config['update_state_operate']
  690. downside_sqls = handle_config['downside_sqls']
  691. update_state_sqls = handle_config['update_state_sqls']
  692. downside_fields = handle_config['downside_fields']
  693. update_state_fields = handle_config['update_state_fields']
  694. downside_patch_field_values = handle_config['downside_patch_field_values']
  695. update_state_patch_field_values = handle_config['update_state_patch_field_values']
  696. is_update_time = handle_config['sync_is_update_time']
  697. # pdb.set_trace()
  698.  
  699. try:
  700. table_field = eval(table_field)
  701. if not table_field:
  702. table_field = self.get_table_filed(table_name, self.lock, self.db, self.dbname)
  703. first_field = table_field[0]
  704. except Exception, e:
  705. self.logger.error('-----eval table_field error,config is error!-----')
  706. raise e
  707. if first_field == 'id':
  708. is_id = True
  709. else:
  710. is_id = False
  711.  
  712. download_oprations = downside_operate.split('|')
  713. if 'file' in download_oprations:
  714. filename = self.createNewBlackListPath()
  715. handle_flag = self.handle_file_func(db_content, filename)
  716. return handle_flag
  717. # table_field = eval(table_field)
  718. try:
  719. is_update_time = int(is_update_time)
  720. except:
  721. self.logger.error('-----is_update_time config value error!-----')
  722. raise
  723. for db_item in db_content:
  724. if is_update_time:
  725. time_index = table_field.index('update_time')
  726. update_time = (str(datetime.datetime.today()).split('.')[0],)
  727. db_item = db_item[:time_index] + update_time + db_item[time_index + 1:]
  728. if is_id:
  729. rowdata = db_item[1:]
  730. else:
  731. rowdata = db_item
  732. # self.logger.debug(rowdata)
  733. # print dict(zip(self.phishing_log_fields,rowdata))
  734. lenth = len(download_oprations)
  735. for oper in download_oprations:
  736. # here we get all the patched field value
  737. # '((fixed,true),(carry,None),(tansfer,None))',
  738. myindex = download_oprations.index(oper)
  739. fields_value = []
  740. # pdb.set_trace()
  741. for i in range(len(downside_patch_field_values[myindex])):
  742. val = downside_patch_field_values[myindex][i]
  743. if val[0] == 'fixed':
  744. pass
  745. elif val[0] == 'carry':
  746. pass
  747. elif val[0] == 'transfer':
  748. field_name = downside_fields[myindex][i]
  749. v_index = table_field.index(field_name)
  750. tf_value = db_item[v_index]
  751. fields_value.append(tf_value)
  752. pass
  753. else:
  754. self.logger.error('-----server downside_patch_field_values Error! valuse: %s------' % str(
  755. downside_patch_field_values))
  756. # pdb.set_trace()
  757. if fields_value:
  758. d_sql, f_val = self.pre_handle_None_value(downside_sqls[myindex],
  759. self.format_field_value(fields_value))
  760. db_sql = self.format_sql(d_sql, f_val)
  761. else:
  762. db_sql = downside_sqls[myindex]
  763.  
  764. # pdb.set_trace()
  765. BD_ERROR = False
  766. with self.lock:
  767. if oper == 'insert':
  768. self.db.insertdb(db_sql)
  769. BD_ERROR = self.db.Error
  770. if oper == 'update':
  771. self.db.updatedb(db_sql)
  772. BD_ERROR = self.db.Error
  773. if oper == 'delete':
  774. self.db.deldb(db_sql)
  775. BD_ERROR = self.db.Error
  776. if not BD_ERROR:
  777. break
  778. else:
  779. continue
  780. else:
  781. return True
  782.  
  783. def format_tuple(self, tup):
  784. '''
  785. It is None if field in DB is NULL when we get the data from db use mysqldb!
  786. Format the None to NuLL for inserting data to DB
  787. '''
  788. vluelist = ['NULL' if t is None else t for t in tup]
  789. padlist = ['%s' if t is None else '\'%s\'' for t in tup]
  790. padstr = ''
  791. for pl in padlist:
  792. padstr += pl
  793. padstr += ','
  794. else:
  795. padstr = padstr[:-1]
  796. return padstr % tuple(vluelist)
  797.  
  798. def format_sql(self, patch_sql, patch_field_value):
  799. if isinstance(patch_sql, str) and isinstance(patch_field_value, tuple):
  800. try:
  801. res_sql = patch_sql % patch_field_value
  802. except:
  803. res_sql = None
  804. return res_sql
  805. else:
  806. self.logger.error('-----formate_sql args type error-----')
  807. raise exceptions.TypeError
  808.  
  809. def format_field_value(self, field_value):
  810. # we neeed hanle the ' or " in the mysql statement
  811. res_list = list()
  812. for val in field_value:
  813. if isinstance(val, unicode):
  814. val = val.encode('utf-8')
  815. if isinstance(val, str):
  816. f_val = val.replace('\'', '\\\'').replace('\"', '\\\"')
  817. else:
  818. f_val = val
  819. res_list.append(f_val)
  820. return tuple(res_list)
  821.  
  822. def get_all_sub_str_index(self, index_str, sub_str, none_indexs):
  823. # print index_str
  824. index_list = []
  825. start_index = 0
  826. cnt = 0
  827. while True:
  828. try:
  829. tmp_index = index_str.index(sub_str, start_index)
  830. except:
  831. break
  832. else:
  833. if cnt in none_indexs:
  834. index_list.append(tmp_index)
  835. start_index = tmp_index + len(sub_str)
  836. cnt += 1
  837. return tuple(index_list)
  838.  
  839. def pre_handle_None_value(self, patch_sql, field_values):
  840.  
  841. # get all the None value index
  842. None_indexs = []
  843. for i in range(len(field_values)):
  844. if field_values[i] is None:
  845. None_indexs.append(i)
  846.  
  847. if None_indexs:
  848. # get '%s' indexs
  849. s_indexs = self.get_all_sub_str_index(patch_sql, "'%s'", None_indexs)
  850.  
  851. str_list = list(patch_sql)
  852. # pdb.set_trace()
  853. subtraction_index = 0
  854. for ix in s_indexs:
  855. print subtraction_index
  856. str_list.pop(ix - subtraction_index)
  857. # print str_list[ix-subtraction_index]
  858. str_list.pop(ix - subtraction_index + 2)
  859. subtraction_index += 2
  860. replace_str = ''.join(str_list)
  861. # pdb.set_trace()
  862. # print replace_str
  863. # pdb.set_trace()
  864. res_field_values = ['NULL' if f_val is None else f_val for f_val in field_values]
  865.  
  866. return replace_str, tuple(res_field_values)
  867. else:
  868. return patch_sql, field_values
  869.  
  870. def __handler_read_event(self, FileNo):
  871. self.logger.info('Start handle the recieved data...')
  872. # 对接受到数据做业务处理
  873. try:
  874. self.__deal_business(FileNo)
  875. except Exception, e:
  876. self.logger.error('__deal_business Exception: %s' % e)
  877. # self.logger.debug(datetime.datetime.now())
  878. try:
  879. self.epoll.modify(FileNo, select.EPOLLOUT | select.EPOLLET | select.EPOLLONESHOT)
  880. except:
  881. pass
  882. self.logger.error('Deal_business ERRor')
  883. else:
  884. self.modify_revent_to_wevent(FileNo)
  885. self.logger.info('Handle the recieved data End!')
  886.  
  887. # content = dict(sync_point=self.detect_piont_name,sync_point_no = self.detect_piont_serial_number)
  888. # message_info = (Communication_Packet_Flags.DOLOAD_DB,str(content),True,table_name,table_field)
  889. # error_info = (False,None,None)
  890. # message_status = (Communication_Packet_Flags.RESPONSE,None,str(datetime.datetime.now()),True)
  891.  
  892. def __handler_write_event(self, FileNo):
  893.  
  894. # if errorInfo is not null,we send Error to Client else handing write business
  895. Error_Info = None
  896. try:
  897. Error_Info = self.errorInfo[FileNo]
  898. # reset error info to None
  899. self.errorInfo[FileNo] = ''
  900. except:
  901. # 说明socket已从列表注销和直接退出程序
  902. self.logger.info('This socket is removed from error info list!')
  903. return
  904. error_info = (False, None, None)
  905. if Error_Info:
  906. print Error_Info # using debug
  907. error_info = (True, Error_Info[0], Error_Info[1])
  908.  
  909. response = self.responseInfo[FileNo]
  910. # need reset the response info
  911. self.responseInfo[FileNo] = ''
  912.  
  913. res_info = (None, response, None, None, False, None)
  914.  
  915. info_states = (Communication_Packet_Flags.RESPONSE, None, None, True)
  916.  
  917. message = self.packaging_message(res_info, error_info, info_states)
  918. self.logger.debug(message)
  919. self.send_message(FileNo, message, True) # send the message to client
  920.  
  921. self.modify_wevent_to_revent(FileNo) # modify the event
  922.  
  923. def modify_wevent_to_revent(self, FileNo):
  924. '''
  925. If we trigger the read envet,we use this function
  926. '''
  927. try:
  928. # We need modify event to read event!
  929. self.epoll.modify(FileNo, select.EPOLLET | select.EPOLLIN | select.EPOLLONESHOT)
  930. except:
  931. pass
  932.  
  933. def modify_revent_to_wevent(self, FileNo):
  934. '''
  935. If we trigger the write envet,we use this function
  936. '''
  937. try:
  938. self.epoll.modify(FileNo, select.EPOLLET | select.EPOLLOUT | select.EPOLLONESHOT)
  939. except:
  940. pass
  941.  
  942. def send_message(self, FileNo, message, blocking=True):
  943. # if message is big,use noblocking it will occur error!
  944. # so, we maybe set it to blocking
  945. if blocking:
  946. self.connections[FileNo].setblocking(True)
  947. if FileNo not in self.connections:
  948. self.logger.debug('This socket not in the connections list!')
  949. return
  950. try:
  951. self.connections[FileNo].sendall(message)
  952. except Exception, e:
  953. pass
  954. # last we need to set it to False! we use the noblocking module
  955. if blocking:
  956. self.connections[FileNo].setblocking(False)
  957.  
  958. def start_server(self):
  959. try:
  960.  
  961. self.__init_server() # 初始化服务器
  962. except:
  963. # 初始化服务器错误
  964. self.logger.critical('Init server Error...')
  965. raise
  966.  
  967. while True:
  968. # join the thread
  969. self.__jionthreads()
  970.  
  971. # Wait for at least one of the sockets to be ready for processing
  972. self.logger.info('waiting for the next event')
  973. events = self.epoll.poll()
  974.  
  975. for fileno, event in events:
  976. # Handle inputs
  977. if fileno == self.server.fileno():
  978. try:
  979. while True:
  980. connection, address = self.server.accept()
  981. connection.setblocking(0) # 设置连接为非阻塞模式
  982. # Here we can not use select.EPOLLONESHOT flag.This flag
  983. self.epoll.register(connection.fileno(),
  984. select.EPOLLIN | select.EPOLLET) # 把新来的连接同样设置为边缘出发模式
  985. self.connections[connection.fileno()] = connection # 记录连接
  986. self.requests[connection.fileno()] = '' # 记录业务请求
  987. self.addresses[connection.fileno()] = address # 记录连接地址
  988. self.errorInfo[connection.fileno()] = ''
  989. self.responseInfo[connection.fileno()] = ''
  990. # 设置错误信息如果为空串则是无错误信息
  991. self.logger.info('========================================')
  992. self.logger.info('Client %s:%s connected server' % (address))
  993. except socket.error:
  994. pass
  995.  
  996. # elif event & (select.EPOLLIN | select.EPOLLONESHOT):
  997. elif event & select.EPOLLIN:
  998.  
  999. # Read data from socket untill data is recieved over!
  1000. self.logger.debug('EVENT EPOLLIN: %s' % hex(event))
  1001. # pdb.set_trace()
  1002. try:
  1003. r_flag = self.__read_from_socket(fileno)
  1004. except socket.error:
  1005. pass
  1006. except Exception, e:
  1007. # if we catch other Exception, it is to say that we recieved data from client Error!
  1008. # We need send error data to client!
  1009. self.logger.warning('Catch other exception when recieve data!')
  1010.  
  1011. self.errorInfo[fileno] = (
  1012. Error_Info_Flags.Receive_Data_Error, '-----Server recieved data Error!-----')
  1013. self.modify_revent_to_wevent(fileno)
  1014. else:
  1015. # if it has no exception when eval the data, we think that client data is recieved over.
  1016. # #then start a new thread to deal with the client data
  1017. # if not r_flag:
  1018. # print '#################################'
  1019. # pass
  1020. # else:
  1021. if self.requests[fileno]:
  1022. # Start a new thread to disposal the client requests
  1023. # self.logger.debug(self.requests[fileno])
  1024. if self.requests[fileno].endswith(self.EOF):
  1025. newthread = threading.Thread(target=self.__handler_read_event, args=(fileno,))
  1026. newthread.daemon = True
  1027. newthread.start()
  1028. print 'start %s' % newthread.name
  1029. # print 'print start new thread'
  1030. else:
  1031. # 没有从客户端读取到数据,说明客户端已经关闭,主动挂断
  1032. # self.logger.info("closing %s %s (HUP)" % self.addresses[fileno])
  1033. self.__delevnet(fileno)
  1034.  
  1035. # elif event & (select.EPOLLOUT | select.EPOLLONESHOT):
  1036. elif event & select.EPOLLOUT:
  1037. self.logger.debug('EVENT EPOLLOUT: %s' % bin(event))
  1038. # Write event happened,we use proxy to deal
  1039. # print 'Current file descripter: %d' % fileno
  1040. self.__proxy(
  1041. fileno) # We neet a proxy using a function,but not a threadiing! If threading it has bugs(multi trigger event--I think)
  1042.  
  1043. elif event & select.EPOLLHUP:
  1044. self.logger.debug('EVENT EPOLLHUP: %s' % bin(event))
  1045. # Client hung up, del event!
  1046. self.logger.info("closing %s %s (HUP)" % self.addresses[fileno])
  1047. self.__delevnet(fileno)
  1048.  
  1049. elif event & select.EPOLLERR:
  1050. # self.logger.debug('EVENT: %s' % event)
  1051. self.logger.info(" exception on %s" % connections[fileno].getpeername())
  1052. self.__delevnet(fileno)
  1053.  
  1054. else:
  1055. # self.logger.debug('EVENT: %s' % bin(event))
  1056. # Other event,do not handle
  1057. pass
  1058.  
  1059. if __name__ == '__main__':
  1060. # pdb.set_trace()
  1061. myserver = Server()
  1062. myserver.start_server()

server

# /usr/bin/env python
# coding:utf-8 import socket
import sys, os
import threading
import time
import logging
import multiprocessing
import random import datetime import hashlib
# hashlib.md5(open(fileName,'rb').read()).hexdigest() import pdb sys.path.append('../')
import exceptions # 导入任务调度模块
from apscheduler.schedulers.blocking import BlockingScheduler from sqldb import SQLdb
from mylog import MyLog as Log from communication_packet import Communication_Packet class Client(object):
# Handle message type
HANDLE_GENERAL = 1 # 处理普通的应答包
HANDLE_INSERT = 2 # 所有数据安装传递过来的进行数据库插入操作
HANDLE_UPDATE = 3 # 对传递过来的数据进行更新操作,需要传递更新条件
HANDLE_INERT_UPDATE = 4
HANDLE_FILE = 5 def __init__(self, IP='112.33.9.154', Port=11366, blackdir=None, db=None):
self.tasksched = BlockingScheduler() # 任务调度器
self.serverIP = IP # 设置服务器IP
self.serverPort = Port # 设置服务器端口
if db is not None:
self.db = db
else:
self.db = SQLdb() # 初始化数据库用于数据库同步 self.lock = threading.Lock() # 使用默认模式:debug模式
self.log = Log()
self.log.openConsole() # 打开控制端输出
self.logger = self.log.getLog() self.EOF = '\n\r\n' # Set EOF flag if blackdir is None:
self.basepath = './blacklist/'
else:
self.basepath = blackdir self.sysdbFirstFlag = False
self.key = None # using to calculate token
self.encryption = None
self.detect_piont_name = 'xxxx'
self.detect_piont_serial_number = 'xxxxxx' def set_DB(self, host=None, username=None, password=None, dbname=None):
self.db = SQLdb(host, username, password, dbname) def set_first_synclog_flag(self, flag):
self.synclog_flag = flag def setBlacklistDir(self, filedir=None):
# Set blacklist dir
if filedir is None:
self.basepath = './blacklist/'
else:
self.basepath = filedir def createNewBlackListPath(self): # blacklistdir if not exists,create it
if os.path.exists(self.basepath):
pass
else:
try:
os.mkdir(self.basepath)
except:
raise
nowtime = datetime.datetime.now().strftime('%Y_%b_%d_%H_%M_%S')
filename = 'blacklist_' + nowtime + '.txt' # 根据文件扩展名
filepath = self.basepath + filename
return filepath def handle_file_func(self, content, filename):
try:
content_data = eval(db_content)
except Exception, e:
self.logger.error('-----handle_file_func: eval business_content error!-----')
return False
else:
# Open file for write data
try:
w_file = file(filename, 'w')
for data_item in content_data:
w_file.write(str(data))
else:
w_file.close()
except Exception, e:
self.logger.error('-----handle_file_func: write data to file Error!------')
return False
else:
return True def reap(self):
# 回收可回收的进程,使用多进程的时候调用...可能不用
while True:
try:
result = os.waitpid(-1, os.WNOHANG)
if not result[0]: break
except:
break
self.logger.info("reaped child process %d" % result[0]) def __connect_server(self, IP, Port):
'''
连接远程服务器反正通讯套接字
'''
# Creat a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connet the socket to the port where the server is listening
server_address = (IP, Port)
self.logger.info('connecting to %s port %s' % server_address)
try:
sock.connect(server_address)
except:
self.logger.error('connecting to %s port %s error!' % server_address)
raise
return sock def __get_tasks(self): # Get task from db,return an list def scheduler_tasks(self, task_list=None):
# start scheduler to sched the tasks
pass def calculateTime(self, starttime=None, intervalMinutes=None):
if not starttime:
nowtime = datetime.datetime.now()
else:
nowtime = starttime
if intervalMinutes:
interval = datetime.timedelta(minutes=intervalMinutes)
return nowtime - interval
else:
return nowtime def calc_md5(self, data):
return hashlib.md5(data)).hexdigest() def validating_message_token(self, receving_data):
pre_md5 = receving_data[:16]
suffix_md5 = receving_data[-16:]
message_md5 = pre_md5 + suffix_md5
message = receving_data.lstrip().rstrip(suffix_md5)
cur_md5 = self.calc_md5(message)
if message_md5 == cur_md5:
return True, message
else:
return False, message
pass def read_sync_state(self):
qeury_sql = "select * from sync_state;"
DB_ERROR = False
with self.lock:
sync_status = self.db.fechdb(query_sql)
DB_ERROR = self.db.Error
if DB_ERROR:
raise
if sync_status:
sync_status_dict = dict()
for sync_s in sync_status:
sync_status_dict[sync_s[0]] = sync_s
return sync_status_dict
else:
return None def read_sync_contol_configure(self, read_flag=False):
# Read the control configuration from loacal db,if have not,we sync it from server,then read it again qeury_sql = "select * from sync_control;"
DB_ERROR = False # set table name
table_name = 'sync_control' # get `sync_control` table fields
table_field = self.get_table_filed(table_name, self.lock, self.db)
if not table_field:
self.logger.error('----------' % table_name)
return None with self.lock:
control_configure = self.db.fechdb(qeury_sql)
DB_ERROR = self.db.Error
if DB_ERROR:
self.logger.error('-----Get control configure Error!-----')
return None
if control_configure:
# Get the configure from db! and On the basis of classification of table name and sync type(uploat or download)
# format the configure to a list
control_configure = []
for iter_conf in control_configure:
control_configure_item = dict.fromkeys(table_field)
lenth = len(table_field)
# set value for everyone key
for i in range(lenth):
control_configure_item[table_field[i]] = iter_conf[i]
control_configure.append(control_configure_item)
return control_configure
else:
# we need get the configuration from the server! reload the configuration!
if read_flag: # if we read it again and no configure,return
return None # sysnc the sync_control table from the server!
self.logger.info('=====Start to init the sync control table from Server...=====')
try:
socket = self.__connect_server(self.serverIP, self.serverPort) # 连接服务器
except Exception, e:
# raise Exception
raise e # we need carry the detect point number to server!
content = dict(sync_point=self.detect_piont_name, sync_point_no=self.detect_piont_serial_number) message_info = (Communication_Packet.DOLOAD_DB, str(content), True, table_name, table_field)
error_info = (False, None, None)
message_status = (Communication_Packet.REQEST, None, str(datetime.datetime.now()), True) send_message = self.packaging_message(message_info, error_info, message_status)
try:
socket.sendall(send_message)
except, Exception, e:
# 发送出现错误则直接返回终止此次业务请求
self.logger.error('-----When send the gain_newconfig_from_server message request to server Error!-----')
return
self.download_db(socket, self.HANDLE_INSERT)
self.logger.info('=====End init the sync control table from Server!=====') # After get control table we need read it from the local table again!
self.read_sync_contol_configure(True) def start_tasks(self):
control_config = self.read_sync_contol_configure()
thread_list = []
if control_config:
for config in control_config:
newthread = threading.Thread(target=self.thread_task, args=(config))
newthread.setDaemon = True
newthread.start()
thread_list.append(newthread)
for t in thread_list:
t.join()
else:
self.logger.error('-----init the sync control configuration error!-----')
raise def thread_task(self, task_config):
# init an instance of sheduler
my_scheduler = BlockingScheduler()
# self.tasksched.add_job(self.synchronous_DB, 'interval', minutes = 15,max_instances = 1)
'''
--cron--
Parameters:
year (int|str) – 4-digit year
month (int|str) – month (1-12)
day (int|str) – day of the (1-31)
week (int|str) – ISO week (1-53)
day_of_week (int|str) – number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun)
hour (int|str) – hour (0-23)
minute (int|str) – minute (0-59)
second (int|str) – second (0-59)
start_date (datetime|str) – earliest possible date/time to trigger on (inclusive)
end_date (datetime|str) – latest possible date/time to trigger on (inclusive)
timezone (datetime.tzinfo|str) – time zone to use for the date/time calculations (defaults to scheduler timezone)
'''
if task_config['sync_type'] == 'upload':
if task_config['sync_time_mode'] == 'period':
period_minutes = task_config['sync_time_value']
my_scheduler.add_job(self.syn_upload_db, 'interval', minutes=15, max_instances=1, args=task_config)
my_scheduler.start()
elif task_config['sync_time_mode'] == 'fixed_time':
try:
hours, minutes, seconds = task_config['sync_time_value'].split(':')
except:
self.logger.error('-----get sync_time_value Error!-----')
raise e
else:
my_scheduler.add_job(self.syn_upload_db, 'cron', year='*', month='*', day='*', hour=hours,
minute=minutes, second=seconds, max_instances=1, args=task_config)
my_scheduler.start()
else:
self.logger.error('----sysnc control config error-----')
raise
elif task_config['sync_type'] == 'download':
if task_config['sync_time_mode'] == 'period':
period_minutes = task_config['sync_time_value']
my_scheduler.add_job(self.sync_download_db, 'interval', minutes=15, max_instances=1, args=task_config)
my_scheduler.start()
elif task_config['sync_time_mode'] == 'fixed_time':
try:
hours, minutes, seconds = task_config['sync_time_value'].split(':')
except:
self.logger.error('-----get sync_time_value Error!-----')
raise e
else:
my_scheduler.add_job(self.sync_download_db, 'cron', year='*', month='*', day='*', hour=hours,
minute=minutes, second=seconds, max_instances=1, args=task_config)
my_scheduler.start()
else:
self.logger.error('----sysnc control config error-----')
raise
else:
self.logger.error('----sync_type error-----')
raise def sync_download_db(self, config):
# parsing the config to get the sql
pass def syn_upload_db(self, config):
pass def validating_content_token(self, content):
receive_content = content['Business']['Content']
receive_md5 = content['Info_Status']['Security_Token']
receive_time = content['Info_Status']['Time']
cur_md5 = self.calc_md5(receive_content + receive_time + self.key)
if cur_md5 == receive_md5:
return True
else:
return False
pass # encrypt decrypt
def packaging_message(self, Message=None, Error=None, Info_Status=None):
# ----Pack send message
# Init Communication_Packet
cm_packet = Communication_Packet() # def set_content_Business(self,b_type,b_content,table_name = None,table_field = None,b_is_syncdb = True):
cm_packet.set_content_Business(targv=Message) # def set_content_Error(self,error_flag = False,error_type = None,error_info = None):
cm_packet.set_content_Error(Error) now_time = str(datetime.datetime.now()) # get current time
# Business+time+key calculate token,calculate token
security_token = self.calc_md5(str(cm_packet.CMC_Business) + now_time + self.key)
# def set_content_Info_Status(self,info_type,security_token,time,is_end):
# Need to replace the security_token
Info_Status = list(Info_Status) # 转化元组到列表
Info_Status[1] = security_token
Info_Status = tuple(Info_Status) # 重新转化列表到元组作为参数 cm_packet.set_content_Info_Status(targv=Info_Status)
try:
send_data = cm_packet.content
except Exception, e:
raise e
else:
# we Encryption data
encryption_send_data = self.encryption.encrypt(send_data)
# caculate md5
encrypt_send_md5 = self.calc_md5(encryption_send_data)
complete_send_data = encrypt_send_md5[:16] + encryption_send_data + encrypt_send_md5[-16:] + self.EOF
return complete_send_data def unpackaging_message(self, unpacking_str):
if not instances(unpacking_str, str):
raise exceptions.ValueError
else:
unpacking_str = unpacking_str.strip(self.EOF)
flag, message = self.validating_message_token(unpacking_str)
if flag:
decrypt_str = self.encryption.decrypt(message)
try:
message_dict = eval(decrypt_str)
except, Exception, e:
self.logger.error('Eval decrypt_str Error!')
raise e
else:
if validating_content_token(message_dict):
return message_dict
else:
self.logger.error('Message is tampered!')
return None
pass
else:
self.logger.error('Message is tampered!')
return None
pass def recieve_packet(self, communication_socket):
# 接收数据从服务器
recieveData = None
partdata = None
try:
while partdata is not None:
try:
partdata = communication_socket.recv(4096)
except Exception, e:
self.logger.error(str(e))
break
else:
recieveData += partdata
if partdata.endswith(self.EOF):
break
recieveData = recieveData.rstrip(self.EOF)
return recieveData
except Exception, e:
raise e
pass def insertdb_updatedb(self, handle_type, db_content, table_name, table_field, filter_condition=None):
'''
add
'''
try:
db_data = eval(db_content)
except Exception, e:
self.logger.error('-----eval business_content error!-----')
raise e
else:
try:
first_field = eval(table_field)[0]
except Exception, e:
self.logger.error('-----eval table_field error!-----')
raise e
if first_field == 'id':
is_id = True
else:
is_id = False
for db_item in db_data:
if is_id:
rowdata = db_item[1:]
else:
rowdata = db_item
self.logger.debug(rowdata)
# print dict(zip(self.phishing_log_fields,rowdata))
if handle_type == self.HANDLE_INSERT or handle_type == self.HANDLE_INERT_UPDATE:
insert_sql = 'insert into %s %s values(%s);' % (
table_name, table_field.replace('\'', ''), self.format_tuple(rowdata)) # print insert_sql
DB_ERROR = False
with self.lock:
self.db.insertdb(insert_sql)
DB_ERROR = self.db.Error
if DB_ERROR:
if handle_type == self.HANDLE_INERT_UPDATE: pass
elif handle_type == self.HANDLE_INSERT:
pass
else:
pass
elif handle_type == self.HANDLE_UPDATE:
# update the data
pass
else:
pass else:
return True
pass def set_filter(self, filter, table_field, filter_key, filter_relationship):
'''
Func: set the filter for update the db when when handling the message if the handle_flag is set HANDLE_INERT_UPDATE!
Note: all the filter condithion are needed to be ordered!
table_field: filter condithion field
filter_key: = >= <= != if has like,like|>=... And so on
filter_relationship: and、or、None
'''
if not filter:
filter = dict(filter=[])
else:
if 'filter' not in filter.keys():
filter['filter'] = []
filter['filter'].append(table_field, filter_key, filter_relationship) return filter def set_update_field(self, filter, update_field):
'''
Set the update fileds when update db!
update_field:update filed list!
'''
filter['update_field'] = update_field def parsing_filter(self, filter, table_name, table_fields, table_values, filter_field_values=None):
'''
Func: return the update sql
'''
update_values = []
update_fields = filter['update_field']
for field in update_fields:
v_index = table_fields.index[field]
up_value = table_values[v_index]
update_values.append(up_value) update_sql_part_one = 'update %s set ' % table_name
lenth = len(update_values)
for i in range(lenth)
up_value = update_values[i]
up_field = update_fields[i]
update_sql_part_one += '%s=\'%s\',' % (up_field, up_value) # strip the last comma result like: update tablea set a = '1',b = '2'
update_sql_part_one = update_sql_partone.rstrip(',') update_sql_part_two = 'where '
for tmp_item in filter['filter']:
field = tmp_item[0]
filter_key = tmp_item[1].splite['|']
relationship = tmp_item[2] # get filter condithion value. For example: where left(ip_loc,2) = 'China'
if field in filter_field_values:
filter_condition_value = filter_field_values[field]
else:
v_index = table_fields.index[field]
filter_condition_value = table_values[v_index]
if not relationship:
relationship = ''
if '(' in filter_key:
update_sql_part_two += "%s%s %s " % (filter_key, filter_condition_value, relationship)
else:
update_sql_part_two += "%s%s%s %s" % (field, filter_key, filter_condition_value, relationship) # merge the sql statement
update_sql = update_sql_part_one + update_sql_part_two.strip()
return update_sql def handle_message__packet(self, socket, handle_type=None, filter=None, handle_callback=None, is_recieved=False,
message_dict=None):
'''
socket: communication socket
handle_type: handle message type(HANDLE_GENERAL,HANDLE_INSERT,HANDLE_UPDATE)
filter: if handle_type is HANDLE_UPDATE,it is the update filter condithion
handle_callback: default is None!if not None,it will use the handle_callback to handle the message
is_recieved: default is False,if message is received.
message_dict: if is_recieved is True,it is the received message_dict
Note: if use some args,you need to use 'key = vale' to passing parameters
'''
if not is_recieved:
# if not recieved message,we receive the message and unpack the message
try:
message = self.recieve_packet(socket)
except Exception, e:
self.logger.error(str(e))
else:
message_dict = self.unpackaging_message(message) if message_dict:
if handle_callback:
# if callback is not None,use the handle_callback to hanle the message
handle_callback(message_dict)
else:
# Pasing packet and get information
# get bussiness type
business = response_dict['Business']
business_type = business['Type']
business_content = business['Content']
business_is_syncdb = business['Is_Syncdb']
business_table_name = business['Table_Name']
business_table_field = business['Table_Field'] # get error info
Error_info = response_dict['Error']
error_flag = Error_info['Is_Error']
error_type = Error_info['Error_Type']
error_info = Error_info['Error_Info'] # get packet info status
info_status = response_dict['Info_Status']
info_type = info_status['Info_Type']
is_end = info_status['Is_End']
token = info_status['Security_Token']
info_time = info_status['Time'] if handle_type == self.HANDLE_GENERAL:
# This packet is GENERAL communication! hanling upload_db response message to make sure that server handle the business
if communication_type == communication_packet.GENERAL_INFO and business_content == communication_packet.HANDLE_OK and info_type == communication_packet.RESPONSE
return True, is_end
elif error_flag:
self.logger.error(
'Message\'type is \'%s\' and message\'error_info is \'%s\'' % (error_type, error_info))
return False, is_end
else:
self.logger.error('This message packet is not the general message!')
return False, is_end
pass
elif handle_type == self.HANDLE_INSERT and info_type == communication_packet.RESPONSE:
handle_flag = self.insertdb_updatedb(self.HANDLE_INSERT, business_content, business_table_name,
business_table_field)
return handle_flag, is_end
pass
elif handle_type == self.HANDLE_UPDATE and info_type == communication_packet.RESPONSE:
return False, is_end
pass
elif handle_type == self.HANDLE_INERT_UPDATE and info_type == communication_packet.RESPONSE:
handle_flag = self.insertdb_updatedb(self.HANDLE_INERT_UPDATE, business_content, business_table_name,
business_table_field, filter)
return handle_flag, is_end
elif handle_type == self.HANDLE_UPDATE and info_type == communication_packet.RESPONSE:
handle_flag = self.insertdb_updatedb(self.HANDLE_UPDATE, business_content, business_table_name,
business_table_field)
return handle_flag, is_end
elif handle_type == self.HANDLE_FILE and info_type == communication_packet.RESPONSE:
handle_flag = self.handle_file_func(business_content)
return handle_flag, is_end
pass
else:
# 没有处理请求包,因为服务器不会主动发起请求
return False, is_end
pass else:
raise Exception('handle_response_packet\'message_dict is None!') pass def upload_db(self, communication_socket, table_name, query_sql=None, ALL=True, table_field=None, Error_info=None):
# Get the dbinfo which is sended
# if db_data is Relatively large,it will raise exceptions,so you need contol the data
DB_ERROR = False
DB_CNT_ERROR = False
if query_sql:
with self.lock:
db_data = self.db.fechdb(query_sql)
DB_ERROR = self.db.Error # If query db error return None
if DB_ERROR:
self.logger.error('Query DB Error! Crrent query_sql: %s' % query_sql)
return None if not db_data:
# 如果没数据则不需要向服务器传输数据,直接返回None
self.logger.info('No Data need to be upload! table_name: %s' % table_name)
return None # Set packet args
# def set_content_Business(self,b_type = Communication_Packet.GENERAL_INFO,b_content = Communication_Packet.HANDLE_OK,table_name = None,table_field = None,b_is_syncdb = False,targv = None):
message = (Communication_Packet.UPLOAD_DB, str(db_data), table_name, table_field, True)
# def set_content_Error(self,error_flag = False,error_type = None,error_info = None,targv = None):
error_info = (False, None, None)
# def set_content_Info_Status(self,info_type,security_token,time,is_end,targv = None):
info_status = (Communication_Packet.REQEST, None, ALL)
complete_send_data = self.packaging_message(message, error_info, info_status)
try:
communication_socket.sendall(complete_send_data)
except, Exception, e:
self.logger.error('Send data error when upload_db!')
raise e
else:
if ALL:
return True
else:
# 对应答包进行处理进行处理
try:
handle_flag, business_is_end = self.handle_general_response_packet(communication_socket)
except Exception, e:
raise e
else:
if handle_flag and business_is_end:
return True
pass
else:
return False
pass
else:
raise Exception('query_sql statement is None') def download_db(self, communication_socket, handle_type=None, handle_message_callback=None, filter=None):
# We receive data from server
try:
recieveData = self.recieve_packet(communication_socket)
except Exception, e:
self.logger.error('Download_db Error when receiving data!')
raise e
# unpacking message
Message_dict = self.unpackaging_message(recieveData) if not message:
raise Exception('download_db Message_dict Error!')
if handle_message_callback:
handle_flag = handle_message_callback(Message_dict)
else:
is_handle_complete, is_handle_again = self.handle_message__packet(communication_socket, is_recieved=True,
message_dict=Message_dict, filter=filter) # parsing received data to dict
# 这里需要用递归处理数据
if is_handle_complete and not is_handle_again:
self.download_db(communication_socket, handle_message_callback, filter)
else:
# 关闭socket连接,退出函数
communication_socket.close()
pass def format_tuple(self, tup):
'''
It is None if field in DB is NULL when we get the data from db use mysqldb!
Format the None to NuLL for inserting data to DB
'''
valuelist = ['NULL' if t is None else t for t in tup]
padlist = ['%s' if t is None else '\'%s\'' for t in tup]
padstr = ''
for pl in padlist:
padstr += pl
padstr += ','
else:
padstr = padstr[:-1]
return padstr % tuple(valuelist) def sync_log_db(self, Start_Time=None, End_Time=None, Sync_All=False, ID=None):
# pdb.set_trace()
'''
sync log db to Server!
If error when sync log,it will return but not raise an exception!
If raise an exception,it will impact tha main program
'''
self.logger.info('=====Start to sysnc log=====')
try:
# connect to server,get and communication socket
sock = self.__connect_server(self.serverIP, self.serverPort)
except:
raise
last_update_id = None # updating update_id
# query log db to get some info
if Start_Time and End_Time:
if datetime.datetime.strptime(Start_Time, '%Y-%m-%d %H:%M:%S') > datetime.datetime.strptime(End_Time,
'%Y-%m-%d %H:%M:%S'):
self.logger.error('-----sync_log_db argv\'Start_Time and End_Time is error!,End this task-----')
# raise Exception('Start_Time and End_Time Error!')
return
query_cnt_sql = "select count(id),min(id),max(id) from phishing_log where time between '%s' and '%s';" % (
Start_Time, End_Time)
# query_log_sql = "select * from phishing_log where time between '%s' and '%s';" %(Start_Time,End_Time)
elif Sync_All:
query_cnt_sql = "select count(1),min(id),max(id) from phishing_log;"
# query_log_sql = 'select * from phishing_log;'
elif ID:
query_cnt_sql = "select count(1),min(id),max(id) from phishing_log where id > %s;" % ID
# query_log_sql = "select * from phishing_log where id > %s;" % ID
else:
query_update_id_sql = "select last_update_id,last_update_time from ph_log_sync_id;"
DB_ERROR = False
with self.lock:
ID_Tuple = self.db.fechdb(query_update_id_sql)
DB_ERROR = self.db.Error
if DB_ERROR:
DB_ERROR = False
self.logger.error('-----Get id from ph_log_sync_id Error. End this task----')
last_update_id = ID_Tuple[0][0]
query_cnt_sql = "select count(1),min(id),max(id) from phishing_log where id > %s;" % last_update_id
# query_log_sql = "select * from phishing_log where id > %s;" % last_update_id # get table fields!
log_fields = self.get_table_filed(table_name, self.lock, self.db)
if not table_field:
self.logger.error(
'-----Terminate this task(sync_log_db),becase of getting the %s table fileds fialed!-----' % table_name)
return with self.lock:
data_cnt = self.db.fechdb(query_cnt_sql)
DB_ERROR = self.db.Error
if DB_ERROR:
# Record Error into log and end task
DB_ERROR = False
self.logger.error('-----Get log data count Error when sys log db! End this task-----')
return
else:
# 若果数据超过10000条则分批进行同步处理
cnt, min_id, max_id = data_cnt[0]
upload_cnt = 1
if cnt >= 10000:
upload_cnt = (max_id - min_id + 1) / 1000 + 1
# def upload_db(self,communication_socket,table_name,query_sql = None,ALL = True,table_field = None,Error_info = None):
ALL = False
log_table_name = 'phishing_log'
for i in range(upload_cnt):
start_id = min_id
end_id = min_id + 10000
if end_id >= max_id:
end_id = max_id
ALL = True
query_log_sql = "select * from phishing_log where id >%s and id <=end_id" % (start_id, end_id)
try:
handle_ok = self.upload_db(sock, log_table_name, query_log_sql, ALL, log_fields, None)
except Exception, e:
self.logger.error('-----' + str(e) + 'when sysnc db!-----')
return
if handle_ok:
continue
else:
self.logger.error("-----upload_db Error! And query_log_sql is '%s'.End this task-----" % query_log_sql)
break
else:
# update update_id
if last_update_id:
cur_time = datetime.datetime.now()
update_id_sql = "update ph_log_sync_id set last_update_id = '%s',last_update_time = '%s' where last_update_id = %s;" % (
max_id, cur_time, last_update_id)
BD_ERROR = False
with self.lock:
self.db.updatedb(update_id_sql)
BD_ERROR = self.db.Error
if BD_ERROR:
inset_id_sql = "insert into ph_log_sync_id valuse(%s,%s)" % (max_id, cur_time)
with self.lock:
self.db.insertdb(inset_id_sql)
BD_ERROR = self.db.Error
if BD_ERROR:
self.logger.error(
'-----This sys log db error when update last_update_id! Old id is %s,new id is %s! End this task' % (
last_update_id, max_id) - ----)
else:
pass
else:
pass self.logger.info('=====End to sysnc log=====') def gain_newconfig_from_server(self):
'''
Get newest configure which is the fishing website regular expressions from server!
''' self.logger.info('=====Start to gain newcofig from Server...=====')
try:
socket = self.__connect_server(self.serverIP, self.serverPort) # 连接服务器
except Exception, e:
# raise Exception
raise e # 查询数据库从数据库获取当前配置的版本信息
query_version_sql = "select max(version),update_time from detection_version;"
BD_ERROR = False
with self.lock:
version_tuple = self.db.fechdb(query_version_sql)
DB_ERROR = self.db.Error
if BD_ERROR:
self.logger.error('-----Get local configure version Error! End this task-----')
return
local_version = version_tuple[0][0]
local_update_time = version_tuple[0][1]
table_name = 'detection_version' # Get detection_version fields
# If get fields fialed,it will terminate this task!
table_field = self.get_table_filed(table_name, self.lock, self.db)
if not table_field:
self.logger.error(
'-----Terminate this task(gain_newconfig_from_server),becase of getting the %s table fileds fialed!-----' % table_name)
return message_info = (Communication_Packet.DOLOAD_DB, str(local_version), True, table_name, table_field)
error_info = (False, None, None)
message_status = (Communication_Packet.REQEST, None, str(datetime.datetime.now()), True) send_message = self.packaging_message(message_info, error_info, message_status)
try:
socket.sendall(send_message)
except, Exception, e:
# 发送出现错误则直接返回终止此次业务请求
self.logger.error('-----When send the gain_newconfig_from_server message request to server Error!-----')
return
self.download_db(socket, self.HANDLE_INSERT)
self.logger.info('=====End to gain newcofig from Server!=====') def get_detection_point_num(self):
# Get detection_point_num from file or db and return it!
# Reading the config file to get he detection_point_num!
# wait to handle...
detection_point_num = None
is_detection_point = None return tdetection_point_num, is_detection_point def get_table_filed(self, table_name, db, lock):
# from the db get the table field!
query_detection_version_field_sql = "select COLUMN_NAME from information_schema.COLUMNS where table_name = '%s';" % table_name
with self.lock:
detection_version_fields = self.db.fechdb(query_detection_version_field_sql)
DB_ERROR = self.db.Error
if DB_ERROR:
# Record Error and end task
DB_ERROR = False
self.logger.error('----Get %s fileds Error! End this task-----' % table_name)
return
else:
# query result is Unicode,so we need to encode to utf-8
table_field = [field.encode('utf-8') for field in detection_version_fields[0]]
return table_field def sync_info_db(self):
'''
1、Get blacklist from server and write it to file for linkage equipment
2、Sync info table to client point
Use a way that depends on the config file
'''
self.logger.info('=====Start to get blacklist from Server=====')
# Get communication socket
try:
sock = self.__connect_server(self.serverIP, self.serverPort) # connect to server
except Exception, e:
self.logger.error(str(e))
raise
is_detection_point, point_num = self.get_detection_point_num() # Need to handle again-----
table_name = 'phishing_info'
if is_detection_point:
table_field = ('phishing_site')
else:
# get the table field from the db
table_field = self.get_table_filed(table_name, self.lock, self.db)
if not table_field:
self.logger.error(
'-----Terminate this task(gain_newconfig_from_server),becase of getting the %s table fileds fialed!-----' % table_name)
return # packing the request packet
message_info = (Communication_Packet.DOLOAD_DB, str(point_num), True, table_name, table_field)
error_info = (False, None, None)
message_status = (Communication_Packet.REQEST, None, str(datetime.datetime.now()), True) send_message = self.packaging_message(message_info, error_info, message_status)
try:
socket.sendall(send_message)
except, Exception, e:
# if send data error,end this task!
self.logger.error('-----When send the gain_newconfig_from_server message request to server Error!-----')
return if is_detection_point:
# If detection piont we update the phishing_info
self.download_db(socket, self.HANDLE_INSERT)
else:
# write the data to file!
self.download_db(socket, self.HANDLE_FILE)
self.logger.info('=====End to get phishing_info from Server!=====') def sync_white_list(self, sync_flag=0):
'''
sync_flag:
0、first upload the white list then download the white list from server
1、only upload the white list
2、only download the white list
'''
# Get communication socket
try:
sock = self.__connect_server(self.serverIP, self.serverPort) # connect to server
except Exception, e:
self.logger.error(str(e))
raise
if sync_flag == 0 or sync_flag == 1:
self.sync_client_white_list_to_server(sock)
if sync_flag == 0 or sync_flag == 2:
self.sync_server_white_list_to_client(sock) # close the network socket,end this task!
sock.close() def sync_server_white_list_to_client(self, communication_socket):
# pdb.set_trace()
'''
Sync server white list to this detection point
communication_socket: Network socket
'''
self.logger.info('=====Start sync_server_white_list_to_client!=====') # get the last update white list time of this client!
qurey_sql = "select max(update_time) from white_list where valid = true;"
BD_ERROR = False
with self.lock:
loacal_last_update_time = self.db.fechdb(qurey_sql)
BD_ERROR = self.db.Error
if BD_ERROR:
self.logger.error('-----Get white list last update time Error! End this task!-----')
return
if loacal_last_update_time:
last_update_time = loacal_last_update_time[0][0]
else:
last_update_time = None table_name = 'white_list'
table_field = self.get_table_filed(table_name, self.lock, self.db)
if not table_field:
self.logger.error(
'-----Terminate this task(sync_server_white_list_to_client),becase of getting the %s table fileds fialed!-----' % table_name)
return # packing the request packet
message_info = (Communication_Packet.DOLOAD_DB, str(last_update_time), True, table_name, table_field)
error_info = (False, None, None)
message_status = (Communication_Packet.REQEST, None, str(datetime.datetime.now()), True)
send_message = self.packaging_message(message_info, error_info, message_status)
try:
socket.sendall(send_message)
except, Exception, e:
# if send data error,end this task!
self.logger.error('-----When send the sync_server_white_list_to_client message request to server Error!-----')
return # After send the request,start to download white list db data
# update_sql = "update white_list set domain = '%s',valid = '%s',recorded = '%s',update_time = '%s',website_name = '%s',website_link = '%s',icp_code = '%s',organization = '%s'where domain = '%s';" % fielddata
myfilter = dict()
update_field = (
'domain', 'valid', 'recorded', 'update_time', 'website_name', 'website_link', 'icp_code', 'organization')
self.set_update_field(myfilter, update_field) # def set_filter(self,filter,table_field,filter_key,filter_relationship):
self.set_filter('domain', '=', None)
self.logger.debug(str(myfilter))
self.download_db(communication_socket, self.HANDLE_INSERT, filter=myfilter) def sync_client_white_list_to_server(self, communication_socket):
# pdb.set_trace()
'''
upload the local white list to server!
communication_socket: network socket
'''
self.logger.debug('=====Start sync_client_white_list_to_server!=====')
# qeury the the lacal white list data which is needed to upload
query_sql = "select * from white_list where update_time is NULL and valid = 1;"
query_modified_sql = "select * from white_list where update_time is not NULL and valid = 0;" # get white_list table fields
table_name = 'white_list'
table_field = self.get_table_filed(table_name, self.lock, self.db)
if not table_field:
self.logger.error(
'-----Terminate this task(sync_server_white_list_to_client),becase of getting the %s table fileds fialed!-----' % table_name)
return # upload the new local added white list
handle_ok = self.upload_db(sock, log_table_name, query_sql, False, log_fields, None)
if handle_ok:
# upload the loacl new modified white list
handle_ok = self.upload_db(sock, log_table_name, query_modified_sql, True, log_fields, None)
if handle_ok:
self.logger.info('upload the loacl new modified white list OK!')
self.logger.info('sync_client_white_list_to_server OK!')
else:
self.logger.error('-----UPLOAD the loacl new modified white list Error!-----')
else:
self.logger.error('' - ----UPLOAD
the
new
local
added
white
list
Error!-----'') def task_scheduled(self):
# muti process scheduling the task
pass
) def start_client(self):
'''
--interval--
Parameters:
weeks (int) – number of weeks to wait
days (int) – number of days to wait
hours (int) – number of hours to wait
minutes (int) – number of minutes to wait
seconds (int) – number of seconds to wait
start_date (datetime|str) – starting point for the interval calculation
end_date (datetime|str) – latest possible date/time to trigger on
timezone (datetime.tzinfo|str) – time zone to use for the date/time calculations
--cron--
Parameters:
year (int|str) – 4-digit year
month (int|str) – month (1-12)
day (int|str) – day of the (1-31)
week (int|str) – ISO week (1-53)
day_of_week (int|str) – number or name of weekday (0-6 or mon,tue,wed,thu,fri,sat,sun)
hour (int|str) – hour (0-23)
minute (int|str) – minute (0-59)
second (int|str) – second (0-59)
start_date (datetime|str) – earliest possible date/time to trigger on (inclusive)
end_date (datetime|str) – latest possible date/time to trigger on (inclusive)
timezone (datetime.tzinfo|str) – time zone to use for the date/time calculations (defaults to scheduler timezone) add_job:
Parameters:
func – callable (or a textual reference to one) to run at the given time
trigger (str|apscheduler.triggers.base.BaseTrigger) – trigger that determines when func is called
args (list|tuple) – list of positional arguments to call func with
kwargs (dict) – dict of keyword arguments to call func with
id (str|unicode) – explicit identifier for the job (for modifying it later)
name (str|unicode) – textual description of the job
misfire_grace_time (int) – seconds after the designated run time that the job is still allowed to be run
coalesce (bool) – run once instead of many times if the scheduler determines that the job should be run more than once in succession
max_instances (int) – maximum number of concurrently running instances allowed for this job
next_run_time (datetime) – when to first run the job, regardless of the trigger (pass None to add the job as paused)
jobstore (str|unicode) – alias of the job store to store the job in
executor (str|unicode) – alias of the executor to run the job with
replace_existing (bool) – True to replace an existing job with the same id (but retain the number of runs from the existing one)
''' # 根据情况调度任务
# new_task_sched = BlockingScheduler()
# new_task_sched.add_job(self.synchronous_DB, 'interval', minutes = 5,max_instances = 1)
# new_task_sched.start()
# print '===================='
'''
self.tasksched.add_job(self.synchronous_DB, 'interval', minutes = 5,max_instances = 1)
self.tasksched.add_job(self.get_newconfig, 'interval', days = 1,max_instances = 1)
self.tasksched.add_job(self.get__new_blacklist, 'interval', days = 1,max_instances = 1)
self.tasksched.add_job(self.upload_white_list, 'interval', days = 1,max_instances = 10)
self.tasksched.add_job(self.updatewhitelist, 'interval', days = 1,max_instances = 10)
'''
self.tasksched.add_job(self.synchronous_DB, 'interval', minutes=15, max_instances=1)
self.tasksched.add_job(self.get_newconfig, 'interval', minutes=30, max_instances=1)
self.tasksched.add_job(self.get__new_blacklist, 'interval', minutes=30, max_instances=1)
self.tasksched.add_job(self.upload_white_list, 'interval', minutes=30, max_instances=1)
self.tasksched.add_job(self.updatewhitelist, 'interval', minutes=30, max_instances=1)
self.tasksched.start() def connecttest(self):
self.logger.info('Start connect server...')
sock = self.__connect_server(self.serverIP, self.serverPort) # 连接服务器
self.logger.info('connect server ok!')
time.sleep(10) self.logger.info('close sock')
sock.shutdown(socket.SHUT_RDWR)
sock.close() def test(self):
'''
debug
'''
# pdb.set_trace()
# self.setsysdbflag(True)
# self.updatewhitelist()
# self.sysdbFirstFlag = True
# self.synchronous_DB()
# self.get__new_blacklist()
# self.get_newconfig()
# self.connecttest()
self.upload_white_list()
# self.updatewhitelist() def muilti_test(self):
jobs = []
get_black_list_thread = threading.Thread(target=self.get__new_blacklist)
get_black_list_thread.daemon = True get_config_thread = threading.Thread(target=self.get_newconfig)
get_config_thread.daemon = True upload_white_list_thread = threading.Thread(target=self.upload_white_list)
upload_white_list_thread.daemon = True update_white_list_thread = threading.Thread(target=self.updatewhitelist)
update_white_list_thread.daemon = True get_black_list_thread.start()
jobs.append(get_black_list_thread) get_config_thread.start()
jobs.append(get_config_thread) upload_white_list_thread.start()
jobs.append(upload_white_list_thread) update_white_list_thread.start()
jobs.append(update_white_list_thread) for t in jobs:
t.join() if __name__ == '__main__':
'''
起进程出问题,所以只能起线程,还没有找到具体原因
'''
# mydb = SQLdb(dbname = 'tmp')
# myclient = Client(db = mydb)
myclient = Client()
if len(sys.argv) >= 2:
try:
flag = sys.argv[1]
except:
pass
else:
try:
flag = flag.title()
flag = eval(flag)
except:
raise 'ARGV ERROR!'
else:
if isinstance(flag, bool):
myclient.setsysdbflag(flag)
else:
raise 'ARGV ERROR!'
# myclient.test()
# myclient.muilti_test()
myclient.start_client() '''
jobs=[]
for i in range(50):
tmpclient = Client()
newprocess = threading.Thread(target = Client.test,args=(tmpclient,))
#newprocess = multiprocessing.Process(target = Client.test,args=(tmpclient,)) #使用进程就会出错,查了一些资料好像是说是bug
newprocess.start()
jobs.append(newprocess) for t in jobs:
t.join()
'''

client

I/O多路复用之epoll实战的更多相关文章

  1. 用C写一个web服务器(二) I/O多路复用之epoll

    .container { margin-right: auto; margin-left: auto; padding-left: 15px; padding-right: 15px } .conta ...

  2. python 网络编程 IO多路复用之epoll

    python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...

  3. IO多路复用之epoll总结

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  4. 知识联结梳理 : I/O多路复用、EPOLL(SELECT/POLL)、NIO、Event-driven、Reactor模式

    为了形成一个完整清晰的认识,将概念和关系梳理出来,把坑填平. I/O多路复用 I/O多路复用主要解决传统I/O单线程阻塞的问题.它通过单线程管理多个FD,当监听的FD有状态变化的时候的,调用回调函数, ...

  5. I/O多路复用之epoll

    1.select.poll的些许缺点 先回忆下select和poll的接口 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set ...

  6. unix网络编程——I/O多路复用之epoll

    1. 基本概念 当程序进行IO时,如果数据尚未准备好,那么IO将处于阻塞状态.当某个进程有多个打开的文件,比如socket,那么其后的所有准备好读写的文件将受到阻塞的影响而不能操作.不借助线程,单一进 ...

  7. IO多路复用之epoll

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  8. 关于非阻塞I/O、多路复用、epoll的杂谈

    本文主要是想解答一下这样几个问题: - 什么是非阻塞I/O - 非阻塞I/O和异步I/O的区别 - epoll的工作原理 文件描述符 文件描述符在本文有多次出现,难免有的朋友不太熟悉,有必要简单说明一 ...

  9. IO多路复用与epoll机制浅析

    epoll是Linux中用于IO多路复用的机制,在nginx和redis等软件中都有应用,redis的性能好的原因之一也就是使用了epoll进行IO多路复用,同时epoll也是各大公司面试的热点问题. ...

随机推荐

  1. word vbs脚本 设置所有题注样式为蓝色,下划线

    Attribute VB_Name = "题注样式" Sub SetCorssRef() 'Word版,设置所有交叉引用的文本的格式 Dim aField As Word.Fiel ...

  2. 如何优雅地运用 Chrome (Google)

    已经在很多工具类文章前言中,提及使用工具的重要性:以至于在写这篇时候,大为窘迫:穷尽了脑海中那些名句箴言,目测都已然在先前文章中被引用.鉴于杳让人心底意识到工具的重要性,并且能践行之,远比介绍如何使用 ...

  3. 基于本地文件系统的LocalDB

    零.前言 之前写一些小工具的时候,需要用到数据存储方面的技术,但是用数据库又觉得太大了,本地文件存储txt文件存储又不是很规范,于是乎想到了去编写一个简单的基于本地文件系统的数据存储库,暂且叫它loc ...

  4. 【JAVAWEB学习笔记】07_BootStrap、Viewport介绍

    今天主要学习了BootStrap,viewport的介绍和最后对网站进行了重构 今天晨读单词: Compatible:兼容性 viewport:视口 device:设备 initial:初始化(缩写i ...

  5. Python下划线的使用

    References: [1]. http://python.jobbole.com/81129/ 本文将讨论Python中下划线(_)字符的使用方法.我们将会看到,正如Python中的很多事情,下划 ...

  6. Maven学习(四)

    eclipse创建Maven web项目 1.创建新项目 选择File -> New ->Project 选择New Project窗口中选择 Maven -> Maven Proj ...

  7. 微信小程序 生命周期函数详解

    微信小程序 生命周期函数 小程序中 判断当前首页是从其他页面返回,还是由入口打开 由于小程序的数据在我们退出小程序时并没有得到释放,因此再次点击开来数据依然没有变成初始化 解决方法:在小程序 data ...

  8. 基于HTML5 Canvas 实现弹出框

    用户鼠标移入时,有弹出框出现,这样的需求很常见.这在处理HTML元素实现时简单,但是如果是对HTML5 Canvas 构成的图形进行处理,这种方法不再适用,因为Canvas使用的是另外一套机制,无论在 ...

  9. nodeJS之流stream

    前面的话 当内存中无法一次装下需要处理的数据时,或者一边读取一边处理更加高效时,我们就需要用到数据流.NodeJS中通过各种Stream来提供对数据流的操作.本文将详细说明NodeJS中的流strea ...

  10. mac Path had bad ownership/permissions

    解决方案: http://stackoverflow.com/questions/28063598/error-while-executing-plist-file-path-had-bad-owne ...