Gå til innhold

[python]Overføre .tar arkiv ved bruk av socket


Anbefalte innlegg

Hepp!

 

Skjønner ikke helt hvordan jeg skal overføre en .tar fil fra en server til en klient. Dette er det jeg har så langt.

 

Server:

import tarfile
import os
import socket
import select

#Lager .tar arkiv
archive = tarfile.open('./archive.tar', 'w')
print "Creating archive"
for file in os.listdir('./'):
archive.add(file)
archive.close()

#Instillinger for server-socket
HOST = ''				 # Symbolic name meaning all available interfaces
PORT = 50001			  # Arbitrary non-privileged port
CONNECTION_LIST = []
RECV_BUFFER = 4096

#Starter server-socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))
s.listen(1)

CONNECTION_LIST.append(s)

print "Waiting for connection..."

while 1:
read_socket, test1, test2 = select.select(CONNECTION_LIST, [], [], 0)

for socket in read_socket:
	if socket == s:
		conn, addr = s.accept()
		CONNECTION_LIST.append(conn)
		print "Connected by: (%s, %s) " % addr
		print "Sending update to client..."
		conn.send('U') #Send U til klient så den forstår at en fil er på vei
		archive = open('./archive.tar', 'r')
		data = archive.read()
		conn.sendall(data)
		print "Client updated..."
		archive.close()
	else:
		# Data recieved from client, process it
		try:
			#In Windows, sometimes when a TCP program closes abruptly,
			# a "Connection reset by peer" exception will be thrown
			data = socket.recv(RECV_BUFFER)
		except IOError.errno == 35:
			print "Client (%s, %s) is offline" % addr
			socket.close()
			CONNECTION_LIST.remove(socket)
			break
		except IOError.errno:
			pass
		if data:
			# The client sends some valid data, process it
			if data == "q" or data == "Q":
				print "Client (%s, %s) quits" % addr
				socket.close()
				CONNECTION_LIST.remove(socket)
			else:
				print data

 

Klient:

import tarfile
import os
import socket
import select
import string

#archive = tarfile.open('./archive.tar', 'w:bz2')
#print "Creating archive"
#for file in os.listdir('./'):
#	archive.add(file)
#archive.close()

HOST = raw_input("Ip adress:")	  # Symbolic name meaning all available interfaces
PORT = 50001			  # Arbitrary non-privileged port
RECV_BUFFER = 4096

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
s.setblocking(0)
data = ""

while s:
try:
	#In Windows, sometimes when a TCP program closes abruptly,
	# a "Connection reset by peer" exception will be thrown
	data = s.recv(RECV_BUFFER)
except IOError.errno == 35:
	print "Server (%s, %s) is offline"
	s.close()
	break
except:
	pass
finally:
	# The client sends some valid data, process it
	if data == 'U': #Server har sendt U, det betyr at det neste som blir sendt er et .tar arkiv
		print "Receiving update..."
		s.setblocking(1) #Blokker andre hendelser til vi har mottatt hele fila
		archive = open('./archive.tar', 'w')
		data = s.recv(RECV_BUFFER)
		archive.write(data) #Lagre fila
		archive.close()
		print "Update successfully retrieved!"
		print "Extracting archive"
		archive = tarfile.open('./archive.tar', 'w')
		archive.extractall('./test') #Pakk ut innholdet i fila, for test...
		archive.close()
		print "Update successfully applied!"
		s.setblocking(0)

if data != '':
	print data

 

Når jeg kjører disse vil alt gå greit frem til Klienten mottar U fra server. Etter dette sier den at den har mottatt alt og etter et par sekunder spyr 'print data' setningen ut innholdet av .tar fila jeg prøver å sende...

 

Er 100% fersk i python så er sikkert en del feil jeg har gjort her. Noen som vet hvordan jeg får sendt en .tar fil til en annen klient ved hjelp av socket?

Lenke til kommentar
Videoannonse
Annonse
Er 100% fersk i python så er sikkert en del feil jeg har gjort her. Noen som vet hvordan jeg får sendt en .tar fil til en annen klient ved hjelp av socket?

Ikke skriv din egen overføringspotokoll, og hvis du absolutt må gjøre det, ikke jobb med sockets direkte. Dette er ting 100 prosjekter har gjort før deg, bruk en av de, ikke finn opp hjulet på nytt.

Lenke til kommentar

Det er lærerikt å jobbe direkte med sockets, og dersom det er det som er meningen her så er vel dette en fin ting å prøve seg på.

 

Håper ikke dette er ditt første forsøk, siden du bruker select.

 

Du gjør flere feil her, og vi kan begynne med de enkleste.

  1. Du leser ikke all data som sendes fra tjeneren. Når du bruker socket.recv(BUFFER_SIZE), så leses maksimalt BUFFER_SIZE antall bytes fra socket. Her må du sjekke mengden data lest, og lese flere ganger fra socket for å lese all data. Det du gjør her er å bare lese de første 4096 bytene med data og bygger en tar fil fra disse. Jeg klarte ikke lage en .tar fil i linux som var mindre en 10K...
     
  2. Når du først sender en 'U' og deretter sender resten av dataene, er det helt tilfeldig, eller iallefall forskjellig fra os til os om dataene blir bufret opp og sendes i en melding eller ikke. Derfor kan du ikke på klientsiden stole på at i det første recv() kallet bare kommer en 'U' og deretter kan kalle recv() igjen for resten. Ofte kommer U'en med den første delen av dataene du skal sende.
  3. Du har satt timeout til 0 på select funksjonskallet. I linux resulterer det i at timeout er 0 sekunder og løkken i tjeneren kjøres hele tiden selv om det ikke er data og lese på noen sockets, det er litt mot hensikt til select. select blokker helt til det skjer noe på noen av de IO kildene du har registrert i kallet. Enten fjern det siste parameteret (ingen timeout), eller sett det til et høyere tall (f.eks. ett par sekunder)
  4. Du oppdager ikke hos tjeneren når klienten er ferdig å lese og lukker forbindelsen. Når klienten er ferdig havner dens socket i read_socket hos tjeneren. Så vidt jeg husker kan du sjekke her om du mottar en tom melding fra klienten som indikerer at den lukker forbindelsen. Da fjerner du klientens socket fra CONNECTION_LIST, ellers vil tjeneren stå å spinne i løkka igjen, da den ikke har fått gjort seg ferdig med socketen.

Prøv å skriv ut dataene du mottar (siden de allikevel er tekstbaserte) og lengden på dataene du har lest, da ser du om du har mottat mindre en total størrelse på filen, og evt om du har mottatt mindre en bufferet (RECV_BUFFER).

 

Du må også lagre dataene i et buffer, slik at du sitter igjen med all dataene i ett buffer til slutt.

 

BUFFER = BUFFER + data i hver runde av løkka skulle være en begynnelse.

 

Prøv å teste deler av programmet av gangen. Et godt tips er å bruke time.sleep() på strategiske punkter, for å forsinke programmet. Evt stoppe for å lese input fra tastaturet.

Lenke til kommentar
Det er lærerikt å jobbe direkte med sockets, og dersom det er det som er meningen her så er vel dette en fin ting å prøve seg på.

 

Håper ikke dette er ditt første forsøk, siden du bruker select.

 

Du gjør flere feil her, og vi kan begynne med de enkleste.

  1. Du leser ikke all data som sendes fra tjeneren. Når du bruker socket.recv(BUFFER_SIZE), så leses maksimalt BUFFER_SIZE antall bytes fra socket. Her må du sjekke mengden data lest, og lese flere ganger fra socket for å lese all data. Det du gjør her er å bare lese de første 4096 bytene med data og bygger en tar fil fra disse. Jeg klarte ikke lage en .tar fil i linux som var mindre en 10K...
     
  2. Når du først sender en 'U' og deretter sender resten av dataene, er det helt tilfeldig, eller iallefall forskjellig fra os til os om dataene blir bufret opp og sendes i en melding eller ikke. Derfor kan du ikke på klientsiden stole på at i det første recv() kallet bare kommer en 'U' og deretter kan kalle recv() igjen for resten. Ofte kommer U'en med den første delen av dataene du skal sende.
  3. Du har satt timeout til 0 på select funksjonskallet. I linux resulterer det i at timeout er 0 sekunder og løkken i tjeneren kjøres hele tiden selv om det ikke er data og lese på noen sockets, det er litt mot hensikt til select. select blokker helt til det skjer noe på noen av de IO kildene du har registrert i kallet. Enten fjern det siste parameteret (ingen timeout), eller sett det til et høyere tall (f.eks. ett par sekunder)
  4. Du oppdager ikke hos tjeneren når klienten er ferdig å lese og lukker forbindelsen. Når klienten er ferdig havner dens socket i read_socket hos tjeneren. Så vidt jeg husker kan du sjekke her om du mottar en tom melding fra klienten som indikerer at den lukker forbindelsen. Da fjerner du klientens socket fra CONNECTION_LIST, ellers vil tjeneren stå å spinne i løkka igjen, da den ikke har fått gjort seg ferdig med socketen.

Prøv å skriv ut dataene du mottar (siden de allikevel er tekstbaserte) og lengden på dataene du har lest, da ser du om du har mottat mindre en total størrelse på filen, og evt om du har mottatt mindre en bufferet (RECV_BUFFER).

 

Du må også lagre dataene i et buffer, slik at du sitter igjen med all dataene i ett buffer til slutt.

 

BUFFER = BUFFER + data i hver runde av løkka skulle være en begynnelse.

 

Prøv å teste deler av programmet av gangen. Et godt tips er å bruke time.sleep() på strategiske punkter, for å forsinke programmet. Evt stoppe for å lese input fra tastaturet.

 

Takk for fyldig og god tilbakemelding. Er vanskelig å finne svar om dette på internett :) Har også oppdaget asyncore og asynchat nå også, og ved å studere disse to har jeg kommet frem til andre metoder å bruke (Som f.eks. terminator). Takk igjen, nå har jeg noe å leke med :)

Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...