def le_dados_arquivo(file_name, tamanho_pcte, tamanho_arquivo):
    deslocamento = 0
    qtd_pctes = 1
    global fila_de_pacotes, pacotes_a_enviar
    #Percorre o arquivo até o final
    while deslocamento < tamanho_arquivo:
        if(len(fila_de_pacotes) == buffer_size): #Tamanho máximo da fila
            continue
        else:
            # Realiza o deslocamento até o ponto correto do arquivo
            file_name.seek(deslocamento)
            # Lê os dados para montar o pacote
            buf = file_name.read(tamanho_pcte)
            checksum = gera_md5(buf)
            # Cria o pacote e o insere na fila de pacotes
            pacote = Pacote(qtd_pctes, buf,checksum)
            fila_de_pacotes.append(pacote)
            qtd_pctes = qtd_pctes + 1
            #Incrementa a quantidade de pacotes
            deslocamento = deslocamento + 1460
            pacotes_a_enviar = len(fila_de_pacotes)
    fecha_arquivo(file_name)
def recebe_pacotes(udp,nome_arquivo):
    #Definição das variáveis globaix
    global fila_de_pacotes
    print 'Para sair use CTRL + c\n'
    NFE = 1
    pacotes_confirmados = deque([])
    while True:
        pacote, cliente = udp.recvfrom(1560)
        #Só ocorrerá na primeira mensagem ou no fim'
        if(len(pacote) < 40 and len(pacote) != 1): #Pois somente o cabeçalho já tem 40 bytes, jamais um pacote terá tamanho menor que 40
            string = 'c i 16s'
            pacote = struct.unpack(string, pacote)
            if(pacote[0] == 'w' or pacote[0] == 'g' or pacote[0] == 'a'):
                flag = pacote[0]
                pacotes_a_receber = int(pacote[1])
                checksum_arquivo = pacote[2]
                udp.sendto('OK', cliente) # Envia para o cliente que está configurado
                continue
        elif(pacote == 'c'):
            #Lê os dados do arquivo e verifica o checksum
            arquivo = abre_arquivo_leitura(nome_arquivo)
            buf = arquivo.read()
            checksum_final = gera_md5(buf)
            if(checksum_final == checksum_arquivo):
                status = 'OK'
            else:
                status = 'ERRO'
            print 'Enviando status do arquivo escrito no disco'
            udp.sendto(status, cliente)
            fecha_arquivo(arquivo)
            continue
        else:
            tamanho_pcte = len(pacote) - 40 # Calcula o tamanho do pacote - o cabeçalho
            tamanho_pcte = str(tamanho_pcte)
            string = '10s 10s 16s i ' + tamanho_pcte + 's' #Monta a string que será utilizada para unpack
            pacote = struct.unpack(string, pacote)
            #ACK = randomico(pacote[3], taxa)
            ACK = pacote[3]
            #if (ACK != 0):
            '''
                Na atual implementação, os pacotes só chegarão sequencialmente, por isso
                o go-back-n e o stop-and-wait tem o mesmo receptor
            '''
            if(flag == 'w' or flag == 'g'):
                if(ACK < NFE or ACK in pacotes_confirmados):
                #Reenvia o ACK pois o pacote já foi recebido
                    ACK = str(ACK)
                    udp.sendto(ACK, cliente)
                else:
                    if(ACK == NFE):
                        checksum = gera_md5(pacote[4]) #Verifica o checksum do pacote
                        if(checksum == pacote[2]):
                            dados_arquivo = Pacote(pacote[3], pacote[4], checksum)
                            fila_de_pacotes.append(dados_arquivo) #Insere na fila de pacotes
                            pacotes_confirmados.append(ACK) #Insere na fila de pacotes confirmados
                            NFE = NFE + 1
                            ACK = str(ACK)
                            udp.sendto(ACK,cliente)
            #Selective ACK
            elif(flag == 'a'):
                if(ACK in pacotes_confirmados):
                    #Reenvia o ACK
                    ACK = str(ACK)
                    udp.sendto(ACK, cliente)
                else:
                    checksum = gera_md5(pacote[4])
                    #Checksum do pacote chegou OK
                    if(checksum == pacote[2]):
                        dados_arquivo = Pacote(pacote[3], pacote[4], checksum)
                        fila_de_pacotes.append(dados_arquivo) #Insere na fila de confirmados
                        pacotes_confirmados.append(ACK)
                        ACK = str(ACK)
                        udp.sendto(ACK, cliente)
    udp.shutdown(socket.SHUT_RDWR)
    udp.close()
def main():
    try:
        global pacotes_a_enviar, tempo_de_execucao
        tempo_de_execucao = time.time()
        #Define a leitura dos argumentos de linha de comando        
        parser = argparse.ArgumentParser(description='Cliente UDP', add_help=False)
        parser.add_argument('-f','--arquivo', action='store', dest='file_name')
        parser.add_argument('-h','--endereco', action='store', dest='address')
        parser.add_argument('-p','--porto', action='store', dest='port')

        #Conforme especificação, -w, -g e -a são mutualmente exclusivos
        grupo = parser.add_mutually_exclusive_group()
        grupo.add_argument('-w', action='store_true')
        grupo.add_argument('-g', action='store_true')
        grupo.add_argument('-a', action='store_true')
        #parser.add_argument('-t', '--taxa', action='store', dest='taxa')
        argumentos = parser.parse_args()

        #Se os argumentos não estiverem OK, termina a execução do programa 
        if (verifica_argumentos(argumentos) == 1):
            print 'Exemplo : python -i arquivo -f arquivo_entrada -h endereco -p numero_porto \'-w ou -g ou -a i\''
            print parser.parse_args()
            return

        #taxa = int(argumentos.taxa)
        flag = armazena_flag(argumentos)
        #Abre e fecha o arquivo para gerar o checksum    
        arquivo = abre_arquivo_leitura(argumentos.file_name)
        buf = arquivo.read()
        checksum_arquivo = gera_md5(buf)
        fecha_arquivo(arquivo)

        # abre o arquivo para leitura
        arquivo = abre_arquivo_leitura(argumentos.file_name)

        # Lê o tamanho do arquivo e define o tamanho do pacote
        tamanho_pcte = 1460
        arquivo.seek(0,os.SEEK_END)
        tamanho_arquivo = arquivo.tell()
        total_de_pacotes = int (math.ceil(tamanho_arquivo/1460))

        #Conexao        
        HOST = argumentos.address #Endereco do servidor
        PORT = int(argumentos.port) #Porta na qual o servidor sera acessado
        CLIENT = '10.0.0.2'
        dest = (HOST, PORT)
        udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        # Uma thread le os dados do arquivo e os armazena no buffer
        thread1 = threading.Thread(target=le_dados_arquivo,args= (arquivo, tamanho_pcte, tamanho_arquivo))
        thread1.daemon = True #Termina quando a thread principal terminar
        thread1.start()
        #A outra thread envia os pacotes
        thread2 = threading.Thread(target=envia_pacotes,args=(udp,HOST,PORT,CLIENT,total_de_pacotes , checksum_arquivo, flag))
        thread2.daemon = True #Termina quando a thread principal terminar
        thread2.start()
        while pacotes_a_enviar != 0:
            pass
    except KeyboardInterrupt:
        print 'Encerrando cliente.'
        return
    except Exception, IOError:
        traceback.print_exc(file=sys.stdout)
        sys.exit()