def calculate_and_compare_checksum(self, data): calculated_checksum=self.get_checksum(data) received_checksum=data[-4:-2] print_to_log( 'Calculated checsum={}'.format(calculated_checksum), 'Received checsum={}'.format(received_checksum) ) return self.compare_checksum(received_checksum,calculated_checksum)
def list_wait(self): print_to_log( 'Listening to {} , {} , {} '.format( list(map(socket.socket.fileno, self.read_set)), list(map(socket.socket.fileno, self.write_set)), list(map(socket.socket.fileno, self.error_set))), 'Heard from for {} , {} , {} '.format( list(map(socket.socket.fileno, self.readable)), list(map(socket.socket.fileno, self.writable)), list(map(socket.socket.fileno, self.exceptional))))
def manage_write(self): #Send message in response to write_set->select->writable initiated by manage_read() and initiate_write() print_to_log('Following will be sent', self.write_msg) try: self.conn[0].send(self.write_msg) self.write_msg = '' #not in astm. because status except Exception as my_ex: print_to_log("Disconnection from client?", my_ex) self.write_set.remove( self.conn[0]) #now no message pending, so remove it from write set self.error_set = self.read_set.union(self.write_set) #update error set
def manage_read(self, data): #EOF is handled in base class #for receiving data if (data == b'\x05'): signal.alarm(0) self.main_status = 1 self.write_msg = b'\x06' self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set #new file need not be class global (unlike existing file -> which needs to be deleted) new_file = self.get_inbox_filename() self.fd = open(new_file, 'wb') fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB) #lock file #Now use this fd for stx-etb/etx frame writing self.fd.write(data) print_to_log('File Content written:', data) signal.alarm(self.alarm_time) elif (data[-1:] == b'\x0a'): signal.alarm(0) if (self.calculate_and_compare_checksum(data) == True): print_to_log('checksum matched:', 'Proceeding to write data') self.fd.write(data) print_to_log('File Content written:', data) self.write_msg = b'\x06' self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set else: print_to_log('checksum mismatched:', 'Proceeding to send NAK') self.write_msg = b'\x15' self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set signal.alarm(self.alarm_time) elif (data == b'\x04'): signal.alarm(0) self.fd.write(data) print_to_log('File Content written:', data) self.fd.close() self.main_status = 0 self.send_status = 0 elif (data == b'\x06'): #ACK when sending print_to_log("main_status", self.main_status) print_to_log("send_status", self.send_status) print_to_log("total_lines", self.total_lines) print_to_log("current_line", self.current_line) print_to_log("byte_data_list", self.byte_data_list) if ((self.send_status == 1 or self.send_status == 2) and (self.current_line < self.total_lines or self.current_line == 0)): signal.alarm(0) self.send_status = 2 print_to_log('send_status=={}'.format(self.send_status), 'post-ENQ ACK') #writing self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set print_to_log('######current line: ', self.current_line) if (self.current_line == 0): #open, set data self.get_first_outbox_file() #set current_outbox file fd = open(self.outbox_data + self.current_outbox_file, 'rb') #data must not be >1024 #it will be ETX data , not ETB data #Frame number will always be one and only one #line by line data setup self.byte_data_list = fd.read(2024).split(b'\x0a') print_to_log('byte_data_list', self.byte_data_list) self.total_lines = len( self.byte_data_list ) - 1 #python split alway return minimum 1 len list self.current_line = 0 print_to_log('total_lines', self.total_lines) print_to_log('current line: ', self.current_line) byte_data = self.byte_data_list[self.current_line] + b'\x0a' print_to_log('File Content', byte_data) chksum = self.get_checksum(byte_data) print_to_log('CHKSUM', chksum) self.write_msg = byte_data #set message self.current_line = self.current_line + 1 #self.send_status=3 #data sent -> change status only when really data of stx-lf or anyother-inappropriate frame really sent #print_to_log('send_status=={}'.format(self.send_status),'changed send_status to 3 (data sent to write buffer)') #writing end signal.alarm(self.alarm_time) #wait for receipt of second ack elif (self.send_status == 3): signal.alarm(0) self.send_status = 4 print_to_log('send_status=={}'.format(self.send_status), 'post-LF ACK') #write self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set self.write_msg = b'\x04' #set message EOT print_to_log("main_status", self.main_status) print_to_log("send_status", self.send_status) print_to_log("total_lines", self.total_lines) print_to_log("current_line", self.current_line) print_to_log("byte_data_list", self.byte_data_list) self.current_line = 0 self.total_lines = 0 print_to_log("main_status", self.main_status) print_to_log("send_status", self.send_status) print_to_log("total_lines", self.total_lines) print_to_log("current_line", self.current_line) print_to_log("byte_data_list", self.byte_data_list) self.archive_outbox_file() #self.send_status=0 #change only where actually sent #self.main_status=0 #change only where actually sent #print_to_log('send_status=={}'.format(self.send_status),'sent EOT') #print_to_log('main_status=={}'.format(self.main_status),'connection is now, neutral') #write end #alarm not required, no expectation signal.alarm(self.alarm_time) signal.alarm( self.alarm_time ) #when EOT is really sent, change status, or if nothing is sent change status using alarm elif (data == b'\x15'): #NAK signal.alarm(0) self.send_status = 4 print_to_log('send_status=={}'.format(self.send_status), 'post-ENQ/LF NAK. Some error') #write - same as above self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set self.write_msg = b'\x04' #set message EOT self.archive_outbox_file() #self.send_status=0 #only when actually sent #self.main_status=0 print_to_log('send_status=={}'.format(self.send_status), 'initiate_write() sent EOT') print_to_log('main_status=={}'.format(self.main_status), 'initiate_write() now, neutral') #write end #alarm not required, no expectation signal.alarm(self.alarm_time) signal.alarm( self.alarm_time ) #when EOT is really sent then change status , or if nothing is sent change status
def signal_handler(self, signal, frame): print_to_log('Alarm Stopped', 'Signal:{} Frame:{}'.format(signal, frame)) print_to_log('change statuses ', ' and close file') print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), ' -->previous ') self.send_status = 0 #data sent self.main_status = 0 print_to_log( 'current main_status={} send_status={}'.format( self.main_status, self.send_status), ' -->current ') try: if self.fd != None: self.fd.close() print_to_log('self.signal_handler()', 'file closed') except Exception as my_ex: print_to_log('self.signal_handler()', 'error in closing file:{}'.format(my_ex)) print_to_log('Alarm..response NOT received in stipulated time', 'data receving/sending may be incomplate')
def manage_write(self): #common code #Send message in response to write_set->select->writable initiated by manage_read() and initiate_write() print_to_log('Following will be sent', self.write_msg) try: self.conn[0].send(self.write_msg) #self.write_msg='' #not here because write message is evaluated below except Exception as my_ex: print_to_log("Disconnection from client?", my_ex) self.write_set.remove( self.conn[0]) #now no message pending, so remove it from write set self.error_set = self.read_set.union(self.write_set) #update error set #specific code for ASTM status update #if sending: ENQ, ...LF, EOT is sent #ff receiving: ACK, NAK sent (ACK sending donot need to change status, it activates only alarm if (self.write_msg == b'\x04'): #if EOT sent self.main_status = 0 self.send_status = 0 print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), '.. because EOT is sent') signal.alarm(0) print_to_log('Neutral State', '.. so stopping alarm') elif (self.write_msg[-1:] == b'\x0a'): #if main message sent if (self.current_line >= self.total_lines): self.send_status = 3 print_to_log( 'main_status={} send_status={}'.format( self.main_status, self.send_status), '.. because message is sent(LF)') else: pass #donot change status elif (self.write_msg == b'\x05'): #if enq sent self.send_status = 1 print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), '.. because ENQ is sent') elif (self.write_msg == b'\x06'): #if ack sent print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), '.. no change in status ACK is sent') elif (self.write_msg == b'\x15'): #if NAK sent = EOT sent self.main_status = 0 self.send_status = 0 print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), '.. because NAK is sent. going neutral') signal.alarm(0) print_to_log('Neutral State', '.. so stopping alarm') else: #if data stream is incomplate/inappropriate containing EOT etc self.send_status = 3 print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), '.. incomplate message (without LF) sent. EOT will be sent in next round' )
def initiate_write(self): print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), 'Entering initiate_write()') if (self.main_status == 0): print_to_log('main_status=={}'.format(self.main_status), 'initiate_write() will find some pending work') if (self.get_first_outbox_file() == True ): #There is something to work signal.alarm(0) self.main_status = 2 #announce that we are busy sending data print_to_log( 'main_status=={}'.format(self.main_status), 'initiate_write() changed main_status to 2 to send data') self.write_set.add( self.conn[0] ) #Add in write set, for next select() to make it writable self.error_set = self.read_set.union( self.write_set) #update error set self.write_msg = b'\x05' #set message ENQ #self.send_status=1 #status to ENQ sent only when written print_to_log('send_status=={}'.format(self.send_status), 'initiate_write() sent ENQ to write buffer') signal.alarm(self.alarm_time) #wait for receipt of 1st ACK else: print_to_log('main_status=={}'.format(self.main_status), 'no data in outbox. sleeping for a while') return else: print_to_log( 'main_status={} send_status={}'.format(self.main_status, self.send_status), 'busy somewhre.. initiate_write() will not initiate anything')
def __init__(self, host_address, host_port, select_timeout): #logging.basicConfig(filename=conf.log_filename,level=logging.CRITICAL) #logging.basicConfig(filename=conf.log_filename,level=logging.DEBUG) #self.logger = logging.getLogger('astm_bidirectional_general') self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) self.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 1) self.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 3) self.s.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 5) self.select_timeout = select_timeout #second self.host_address = host_address self.host_port = host_port try: self.s.bind( (self.host_address, int(self.host_port))) #it is a tuple except Exception as my_exception: print_to_log(my_exception, 'bind() failed, ip/port correct??Quitting') quit() self.s.listen(2) print_to_log(self.s, 'select() is waiting..') self.readable, self.writable, self.exceptional = select.select( (self.s, ), (self.s, ), (self.s, )) print_to_log(self.s, 'select() detected activity') if (self.s in self.exceptional): print_to_log(self.s, 'some error on socket s. quitting') quit() if (self.s in self.writable): print_to_log(self.s, 'Can not understand why s is writting') quit() if (self.s in self.readable): self.conn = self.s.accept() print_to_log(self.s, 'Connection request is read') self.conn[0].setblocking(0)
def astmg_loop(self): #First set #not tuple it is unmutable #not list, we need uniq values in error list which is sum of read and write self.read_set = {self.s, self.conn[0]} self.write_set = set( ) #must be managed when send is required, otherwise use 100% CPU to check writable buffer self.error_set = self.read_set.union(self.write_set) while True: #self.print_to_log('','before select') self.readable, self.writable, self.exceptional = select.select( self.read_set, self.write_set, self.error_set, self.select_timeout) #self.print_to_log('','after select') self.list_wait() ###if anybody else try to connect, reject it if (self.s in self.exceptional): print_to_log(self.s, 'some error on socket s. quitting') self.s.shutdown(socket.SHUT_RDWR) self.s.close() break if (self.s in self.writable): print_to_log(self.s, 'Can not understand why s is writting') self.s.shutdown(socket.SHUT_RDWR) self.s.close() break if (self.s in self.readable): dummy_conn = self.s.accept() print_to_log( self.s, 'Connection request is read, This is second connection. We do not want it. shutdown, close' ) dummy_conn[0].shutdown(socket.SHUT_RDWR) dummy_conn[0].close() ###For client do work if (self.conn[0] in self.exceptional): print_to_log(self.conn[0], 'some error on socket conn. quitting') self.s.shutdown(socket.SHUT_RDWR) self.s.close() break if (self.conn[0] in self.writable): #sending message (somewhere else conn[0] was added in writable and self.write_msg was given value print_to_log(self.conn[0], 'conn is writable. using manage_write()') self.manage_write() if (self.conn[0] in self.readable): print_to_log( self.conn[0], 'Conn have sent some data. now using recv() and manage_read()' ) try: data = self.conn[0].recv(1024) print_to_log('Following is received:', data) self.manage_read(data) except Exception as my_exception: print_to_log( my_exception, 'recv() failed. something sent and then connection closed' ) self.s.shutdown(socket.SHUT_RDWR) self.s.close() '''to prevent: DEBUG:root:[Errno 110] Connection timed out recv() failed. something sent and then connection closed DEBUG:root:[Errno 98] Address already in use bind() failed, ip/port correct??Quitting''' break #only EOF is handled here, rest is handled in manage_read() #if EOF 1)close socket 2)remove from list 3)accept new if (data == b''): print_to_log(self.conn[0], 'Conn have closed, accepting new connection') #1) close socket try: self.conn[0].shutdown(socket.SHUT_RDWR) self.conn[0].close() except Exception as my_ex: print_to_log('Connection from client closed??', my_ex) #2)remove from read list #no if:() for read_set because, we reached here due to its presence in read_set self.read_set.remove(self.conn[0]) #write list with if exist if (self.conn[0] in self.write_set): self.write_set.remove(self.conn[0]) #error list is union #so, no need to manage #finally error_set self.error_set = self.read_set.union(self.write_set) #3) Accept new, add to read set, this is blocking here. No need to go for initiate_write, because nothing to do self.conn = self.s.accept() print_to_log(self.s, 'New Connection request is read') self.conn[0].setblocking(0) self.read_set.add(self.conn[0]) self.error_set = self.read_set.union(self.write_set) self.initiate_write()