Techster Skrevet 15. desember 2009 Del Skrevet 15. desember 2009 I programmet mitt kan klient-siden sende forespørsler til serveren om å laste opp eller ned filer, chat meldinger, ++. Når klienten kobler til får den en egen tråd på serveren som tar imot dataene den sender. Problemer oppstår hvis klienten vil sende en chat melding mens det pågår en opp- eller nedlasting fra samme klient. Den blir da mixet inn i fildataene på serversiden fordi serveren da forventer data tilhørende filen. Har lagd et system hvor klienten starter en ny socket og sender port til serveren som da kobler til og hele overføring skjer via en ny socket, men dette skaper problemer med brannmurer og port forwardinger etc. Har kommet fram til at det beste kanskje er at det blir det slik at klienten ikke KAN sende noe andre data før filen(e) er ferdig ned- eller opplastet. Har også sett på om WebClient.DownloadFile/UploadFile kan være noe; og det er mulig. Håper det hele er forståelig. Trenger innspill fra noen mer erfarne utviklere! Lenke til kommentar
HDSoftware Skrevet 16. desember 2009 Del Skrevet 16. desember 2009 Dette kommer vel helt an på hvordan du gjkør dette. Hvis du bruker .NET bibliotekene fullt ut og overlater alt til .NET så har du vel liten kontroll, men hvis du selv håndterer streamen så kan du vel "mærke" pakkene for å fortelle hvor de tilhører... Lenke til kommentar
Techster Skrevet 16. desember 2009 Forfatter Del Skrevet 16. desember 2009 Ja, har gjort dette, men syns "merkingen" generer mye data. Har et system som sender forskjellige objekter(serialiserte) avhengig av forespørselen og det funker forsåvidt greit. Men hvor mye data er tålelig å sende over nettet bare for å levere en kort chat melding liksom? Hvis jeg skal merke hver eneste pakke med fildata med fil referanse og "offset index", blir det kanskje litt mye overhead? Filene sendes i 4096 byte chunks; men dette er muligens litt for finkornet. Lenke til kommentar
GeirGrusom Skrevet 16. desember 2009 Del Skrevet 16. desember 2009 (endret) struct StreamHeader { public int stream_id; public int num_packets; public int total_size; public dictionary<string, string> stream_info; } struct StreamPacket { public int stream_id; public int packet; public int size; public byte[] data; } Da slipper du flere sockets og porter. Bare sorter ut pakkene basert på stream_id. Det er mulig du får til slik også med litt triksing: public unsafe struct StreamPacket { public int stream_id; public int packet; public int size; // Always 4096 public fixed byte[4096] data; } Det kan kanskje være fornufig å øke fra 4k store pakker (dette er ingenting i dag) edit: Jeg skrev dette i litt sykdom, så jeg tenkte meg for dårlig om. Ikke bruke struct, ikke bruk arrays slik, og ikke bruk fixed. Det er bortkastet alt. Bruk heller class, og List<T> e.l. fordi det fører til mindre minnekopiering. Endret 17. desember 2009 av GeirGrusom Lenke til kommentar
Techster Skrevet 18. desember 2009 Forfatter Del Skrevet 18. desember 2009 (endret) Tror ikke jeg skjønte det opplegget du skisserte. Hva mente du med å sortere på stream_id? Hva slags id skal dette være? edit: Hvordan ser du for deg at dette skal sendes? Skal det være objekter som serialiseres? Endret 18. desember 2009 av Techster Lenke til kommentar
GeirGrusom Skrevet 18. desember 2009 Del Skrevet 18. desember 2009 (endret) id-en kan være hva som helst, bare den er unik. For eksempel en hash av filnavnet, eller innholdet i hele meldingen. Poenget er bare at den skal identifisere hver "kanal" i alle meldingene som kommer. Du kan skrive en Socket klasse som brukes for å lese eller skrive til en stream. For eksempel har du en tråd som sitter og leser pakker, og sorterer dem ut til riktig socket, som deretter leser ut kun dataene og som igjen kan leses av en annen del av programmet (meldingsklienten, eller den delen som lagrer filer) Når du sender og mottar kan det være greit å bare sende dataene manuelt. Men objekter kan godt serialiseres til 4k store pakker for eksempel. Endret 18. desember 2009 av GeirGrusom Lenke til kommentar
Techster Skrevet 18. desember 2009 Forfatter Del Skrevet 18. desember 2009 Hmm. Virker som et smart opplegg, men vet fortsatt ikke om jeg helt skjønner konseptet. Sånn som det virker i dag da har hver klient sin Socket på serveren hvor all kommunikasjon til den klienten går igjennom(begge veier), tenker du at man skal bruke èn Socket for all kommunikasjon til alle klientene? "Poenget er bare at den skal identifisere hver "kanal" i alle meldingene som kommer." ...IDen identifiserer da klienten som sendte meldingen? Lenke til kommentar
GeirGrusom Skrevet 18. desember 2009 Del Skrevet 18. desember 2009 På en måte. Man åpner en stream (og gir den en unik ID) mottaker finner ut at en ny stream som ikke er registrert ble mottatt, leser ut data, og finner ut hva som skal gjøres med alle nye pakker som inneholder den stream ID-en. For eksempel kan den første pakken alltid starte med et integer som forteller hva som skal gjøres med streamen. Dette blir da dirigert videre til en funksjon eller lignende som behandler streamen korrekt, og alle påfølgende pakker blir sendt til enten en Stream klasse (som du har skrevet). Fordelen med å bruke en stream klasse, er at funksjonen i andre enden ikke lenger er klar over at dataene er blitt segmentert, fordi klassen setter dem sammen igjen. Jeg kan prøve å lage et enkelt eksempelprogram på dette. Lenke til kommentar
Techster Skrevet 18. desember 2009 Forfatter Del Skrevet 18. desember 2009 Jeg kan prøve å lage et enkelt eksempelprogram på dette. Jeg holder på med en implementasjon basert på det har skrevet her. Lenke til kommentar
GeirGrusom Skrevet 18. desember 2009 Del Skrevet 18. desember 2009 Ah ok. Men da poster jeg klientsiden som jeg skrev, så slutter jeg å kaste bort noe mer tid på det Klikk for å se/fjerne innholdet nedenfor using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Net; using System.Net.Sockets; namespace StreamTutorial { public class StreamInitializerPackage : Package { public long SizeOfStream { get { return BitConverter.ToInt64(new byte[] { data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] }, 0); } } public int NumberOfPackets { get { return BitConverter.ToInt32( new byte[] { data[16], data[17], data[18], data[19] } , 0); } } public int CheckSum { get { return BitConverter.ToInt32( new byte[] { data[20], data[21], data[22], data[23] } , 0); } } public int StreamType { get { return BitConverter.ToInt32( new byte[] { data[24], data[25], data[26], data[27] } , 0); } } internal StreamInitializerPackage(Package original) : base(original.StreamID, original.Packet, original.Data) { } } public class Package : ICloneable { protected int stream_id; protected int packet; protected List<byte> data; public int StreamID { get { return stream_id; } } public int Packet { get { return packet; } } public int Size { get { return data.Count; } } public System.Collections.ObjectModel.ReadOnlyCollection<byte> Data { get { return data.AsReadOnly(); } } internal static readonly byte[] init_stream = Encoding.ASCII.GetBytes("Init_Stm"); public Package(Stream src) { System.IO.BinaryReader rd = new BinaryReader(src); stream_id = rd.ReadInt32(); packet = rd.ReadInt32(); int size = rd.ReadInt32(); data = new List<byte>(rd.ReadBytes(size)); } public StreamInitializerPackage GetInitializerPackage() { // Check if stream-header is well-formed. if (BitConverter.ToInt64(data.GetRange(0, 8).ToArray(), 0) == BitConverter.ToInt64(init_stream, 0) && data.Count == 28) return new StreamInitializerPackage(this); return null; } public object Clone() { Package ret = new Package(stream_id, packet, data); } public Package(int id, int pack, IEnumerable<byte> data) { stream_id = id; packet = pack; this.data = new List<byte>(data); } } public class PacketStream : System.IO.Stream { public void WritePacket(Package pack) { Write(pack.Data.ToArray(), 0, pack.Size); } } public delegate Stream NewStream(StreamInitializerPackage init); public class StreamInfo { private StreamInitializerPackage init; private Stream dst; internal StreamInfo(StreamInitializerPackage init_pack, Stream stream) { init = init_pack; dst = stream; } public int StreamID { get { return init.StreamID; } } public int StreamType { get { return init.StreamType; } } public int StreamPackets { get { return init.NumberOfPackets; } } public long StreamSize { get { return init.SizeOfStream; } } public int StreamCheckSum { get { return init.CheckSum; } } public Stream Stream { get { return dst; } } } public class Client { TcpClient client; Dictionary<int, StreamInfo> stream_handlers; NetworkStream stream; public event NewStream StreamInitializing; public event Action<Stream, int> EndOfStream; System.Threading.Thread read_thread; volatile bool reading; public Client(IPEndPoint endpoint) { client = new TcpClient(); client.Connect(endpoint); } public void BeginListen() { read_thread = new System.Threading.Thread(new System.Threading.ThreadStart(DoRead)); read_thread.Start(); } public void DoRead() { reading = true; while (client.Connected && reading) { if (client.Available > 0) PerformRead(); System.Threading.Thread.Sleep(0); } reading = false; } public void EndListen() { reading = false; read_thread.Join(); } private void PerformRead() { var pack = new Package(stream); if (pack != null) { // Check if stream is already initialized. if (stream_handlers.ContainsKey(pack.StreamID)) { var info = stream_handlers[pack.StreamID]; info.Stream.Write(pack.Data.ToArray(), 0, pack.Size); // Check if this is the last packet in the stream if (pack.Packet == info.StreamPackets) { if (EndOfStream != null) EndOfStream(info.Stream, info.StreamID); // Remove stream handler stream_handlers.Remove(pack.StreamID); } } else { // Check if this is an initializer package var init = pack.GetInitializerPackage(); if (init != null) { // Use callback to register a new stream if (StreamInitializing != null) { var s = StreamInitializing(init); if (s != null) stream_handlers.Add(pack.StreamID, new StreamInfo(init, s)); } } } } } } } Lenke til kommentar
Techster Skrevet 18. desember 2009 Forfatter Del Skrevet 18. desember 2009 Shit. Hjertelig takk! Lenke til kommentar
Techster Skrevet 19. desember 2009 Forfatter Del Skrevet 19. desember 2009 Da har jeg noe som virker. Klientene sender nå all dataene i formen: [header][packet] hvor header har en 'id' og 'totalt_antall_packets' og packet har en 'index' og 'data(byte[])' property. Serveren mottar pakkene, sjekker om header.id allerede har blitt mottatt og hvis ikke lager den et 'transfer' objekt og legger til packet.data til det. Hvis header.id allerede er mottatt legger den til packet.data til transfer objektet som allerede er opprettet for denne headeren. Hver gang en packet.data blir lagt til sjekker den om packet.index == header.totaltAntallPackets og fyrer en event hvis true. Funker ganske bra egentlig. Takker for inspirasjonen og hjelpen. Lenke til kommentar
GeirGrusom Skrevet 20. desember 2009 Del Skrevet 20. desember 2009 Flott! Bare hyggelig å være til hjelp Lenke til kommentar
Anbefalte innlegg
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 kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå