Klijent i server koristim za "kratku" komunikaciju koristeći sopstveni kvazi-protokol koji se zasniva na slanju UTF8 stringova sa separatorima. Npr: klijent pošalje DATA1,DATA2,DATA3, server primi, parsira i vrati odgovor tipa ODGOVOR1,ODGOVOR2,ODGOVOR3. Ovakav način komunikacije sam testirao i testirao i sve radi kako treba.
Međutim, nedavno sam dobio potrebu da sa servera pošaljem binarni fajl na klijent, na zahtev klijenta. Osmislio sam komunikaciju ovako:
1) klijent pošalje specifičan zahtev za download fajla
2) server primi zahtev, isparsira ga i pošalje odgovor u formatu IME,VELIČINA (šalje kao string), a zatim šalje fajl
3) klijent prima odgovor i parsira ga, pravi FileStream f u folderu sa imenom iz odgovora servera, pravi byte[] buffer = new byte[VELIČINA], i zatim prima fajl
Fajl je binarni, šaljem array bajtova "u komadu" jer se nisam snašao sa implementacijom algoritma koji bi podatke slao u delovima (npr po 1024 bajta odjednom).
U C# kodu bi to izgledalo ovako:
Klijent:
// slanje zahteva serveru
string msg = "DL,FILE1";
byte[] tosend = new UTF8Encoding().GetBytes(msg);
socket.Send(tosend, tosend.Length, SocketFlags.None);
// prijem odgovora
byte[] received = new Byte[1024];
int bRead = socket.Receive(received);
string serverMsg = Encoding.UTF8.GetString(received, 0, bRead);
// parsiranje odgovora, odgovor stiže u formatu IME,VELIČINA
string[] split = serverMsg.Split(",".ToCharArray());
string ime = split[0];
long length = Convert.ToInt64(split[1]);
// priprema za download fajla
string curFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName);
string fname = curFolder + "\\" + ime;
byte[] fajl = new byte[length]; // buffer za prijem fajla
// prijem fajla
long primljeno = socket.Receive(fajl, SocketFlags.None);
// upis fajla na disk
FileStream fs = new FileStream(fname, FileMode.Create);
fs.Write(fajl, 0, fajl.Length);
fs.Close();
Server:
TcpClient client; // ovde ovo navodim samo radi razjašnjavanja koda, client je već konektovan u ovom koraku!!!!
NetworkStream ns = client.GetStream();
// server je već primio i parsirao zahtev za download ispravno, pa krećem od dela gde server šalje odgovor na klijentov zahtev
FileStream fs = new FileStream(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName) + "\\file.bin", FileMode.Open);
FileInfo fi = new FileInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetModules()[0].FullyQualifiedName) + "\\file.bin");
long total = fi.Length; // dužina fajla
string msg = fi.Name + "," + total.ToString(); // poruka za slanje, "IME,DUŽINA"
byte[] tosend = new UTF8Encoding().GetBytes(msg);
// odgovor koji sadrži dužinu fajla i ime
ns.Write(tosend, 0, tosend.Length);
byte[] fajl = new byte[total]; // buffer za čitanje fajla sa diska i slanje klijentu
fs.Read(fajl, 0, fajl.Length);
fs.Close();
// slanje fajla klijentu
ns.Write(fajl, 0, fajl.Length);
Ono što se dešava nakon što pokušam da fajl prenesem je da prenos u stvari uspe. Međutim, u testovima fajl ni jednom nije stigao do klijenta a da nije bio corrupted. Napominjem da sam pokušavao i sa dodavanjem BinaryReader-a na serverskoj strani za čitanje fajla i BinaryWriter-a na klijentskoj za upis fajla, ali i pored toga sam dobijao identične rezultate.
Da li neko zna gde grešim? Možda je način slanja celog fajla odjednom loš, s'obzirom na to da je veličina fajla oko 5MB? Ako je tako, kako da implementiram slanje "deo-po-deo"?
Unapred zahvalan na odgovorima,
Yeremiya