Gå til innhold

Server leser data for sent fra klienten


Anbefalte innlegg

Hei!

 

Jeg har bruk for å kunne sende en liten array fra en klient til en server, så fort som mullig (helst ned mot 20 ms eller noe sånt). Så jeg tenkte at en fin måte å lære meg dette var å implementere Nivå 3 i Red Sled Down som et "online" spill. Er det kjapt nok for spilling, er det kjapt nok til det egentlige formålet.

 

Jeg har basert med på eksempelet som er vedlagt (er hentet fra "101 Code Samples for VB.NET 2003"). I grunnen er det en server som lytter på en port og klientene sender til denne porten. Handlinger er definert i strengen som sendes. Hvis jeg skal sende min nåværende posisjon til serveren, har jeg definert at jeg skal sende:

"POSITION|x_posisjon|y_posisjon|z_posisjon|x_vinkel|y_vinkel"

... som definerer hvor jeg er og hvor jeg ser. Serveren bruker da Array = mottatt_streng.split("|") for å legge det i en liten matrise som senere oppdaterer posisjonen på serveren.

 

Men her er problemet: For at dette skal gå noenlunde flytende når man spiller, må man oppdatere ganske ofte. Oppdaterer jeg oftere enn hvert 200 millisekund, så får jeg problemer. Da vil strengen som serveren mottas forskyves slik at den starter med y_vinkel (eller større/mindre deler av forrige sendte streng) og så skriver "POSITION" og resten av strengen.

 

Dette skjer forøvrig kun når jeg tester serveren på en maskin og klienten på en annen. Er server og klient på samme maskin, går det greit ned til ca. 60-70 ms.

 

Hvorfor i alle dager skjer dette?!? Og hva kan jeg gjøre med det?

 

 

EDIT: Jeg glemte å nevne at jeg er ganske amatør når det gjelder "programmering i nettverk", så finn frem te-skjeen... :D

Use_Sockets.zip

Endret av moskus
Lenke til kommentar
Videoannonse
Annonse

Mange nettverksenheter, og sågar operativsystemer, optimaliserer dataoverføringen ved å legge flere dataoversendelser i èn og enkel pakke, dersom de treffer på en mengde nært sagt tomme pakker fra samme avsender i et kort invervall. Således må du selv implemenentere en segmenteringsmetode for hver enkel kommando, eksempelvis ved å slenge en karakter i slutten av strengen (eller la de fire første byte-ene inneholde lengden på kommandoen, som du så ekstrakterer fra den mottate datastrømmen).

 

Du kan altså legge til en karakter, eksempelvis ENTER, i slutten av hver kommando. Når mottakeren får informasjonen, trekker du ut all informasjon FØR denne karakteren, såfremt den foreligger i oversendelsen. Dersom den ikke kan finnes, lagrer du informasjonen i en midlertidig buffer. Når du endelig treffer på ENTER-karakteren (eller hva enn du måtte velge), bruker du den midlertidige bufferen til å rekonstruere kommandoen og deretter eksekvere den. Skulle informasjonspakken inneholde enda en karakter, må selvsagt den kommandoen òg eksekveres. Deretter sletter du bufferen og fyller den med den evt. resisterende informasjonen som mangler en segmenteringskarakter.

Lenke til kommentar

Takker for informasjon! :) Jeg la inn et tegn (Enter) som kommando-avslutter, og nå fungerer det veldig bra. :)

 

Optimalisering av datamengden blir neste steg. :)

 

Og mens jeg har noen her som har absolutt bedre peiling enn meg på nettverksprogrammering (samt omtrent alt annet innen VB): Hva er en pakke? Er det hver forsendelse? Er "read/write buffer" størrelse det samme som pakke-størrelse?

Lenke til kommentar

Rent konkret er en pakke et segment, et ledd i IP-protokollen, med data som sendes via Layer 1 (Ethernet, Wi-Fi, Token ring, PPP .ect), i tillegg til metainformasjon om blant annet pakkens destinasjon- og avsenderadresse. Disse pakkene dirigeres av rutere over hele verden, og sammen danner Internett.

 

Informasjonen sendes altså i pakker, i små deler, som er delt opp. Hvor mange ganger du har kalt SendData er i bunn og grunn uvesentlig, da datastørrelsen på de enkelte pakkene kan ha endret seg i overføringen, især dersom de forlater lokalnettet.

 

I det siste spørsmålet - hvilken egenskap/paramenter sikter du til?

Lenke til kommentar

I VB.net kan man lage en "connection" med disse variablene

    Const READ_BUFFER_SIZE As Integer = 255

    Private client As TcpClient

    Private readBuffer(READ_BUFFER_SIZE) As Byte

... og jeg lurer på i den sammenhengen hva som skjer hvis jeg stiller READ_BUFFER_SIZE opp eller ned.

 

 

For å gi en grundigere forklaring på hva jeg gjør:

Som sagt har jeg planer om å lage Level 3 i Red Sled Down til multiplayer. Jeg har laget en server basert på eksempelet i første post. Denne serveren har som oppgave å hente inn informasjon fra en klient og spre den til de andre klientene.

 

Hvert 100 ms blir posisjonen, retningen og hastigheten til en spiller sendt til serveren. Serveren sprer denne informasjonen til de andre klientene med en gang. Mellom hvert 100 ms interpoleres posisjonen for å få en flytende bevegelse (siden frameraten hos alle spillerne er antatt til å være mye høyere enn 10 fps).

 

Det samme skjer hvis en spiller kaster en snøball. Startposisjonen og retningen blir sendt til de andre spillerne, og det er de andre spillernes maskiner som beregner posisjonen for denne snøballen hvert bilde.

 

Dette fungerer som bare juling når det er to personer som spiller. Bevegelsene er jevne, og de absolutt fleste snøballer blir sendt riktig. Tilogmed hvis de to spillerne og serveren står på forskjellige steder og det spilles over nettet (bredbånd). Når det er tre spillere går alt stort sett "åt skogen".

 

Informasjonsmengden blir jo mye større, men det er så lite informasjon som egentlig sendes at det (synes jeg) burde gå bra.

 

Jeg har tilogmed lagt klientenes "streamreaders" i egne threads som leses hvert millisekund, bare for å være sikker på å ikke miste informasjon (se quote under). Men det skjer tydeligvis uansett. Jeg kan se på serveren at den mottar informasjon om f.eks. at en snøball blir kastet, men om serveren ikke klarer å distribuere denne til alle "samtidig" eller om klientene mister den, er ikke godt å vite (for meg)...

 

          Private ListenThread As Threading.Thread

 

'----------------

'Når skjemaet lastes (Mybase.Load)

 

          ListenThread = New Threading.Thread(AddressOf DoReadLoop)

            ListenThread.Name = "Listening to server"

            ListenThread.IsBackground = True

            ListenThread.Start()

 

'----------------------

 

    Private Sub DoReadLoop()

        Try

            client.GetStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, AddressOf DoRead, Nothing)

        Catch ex As Exception

        End Try

        ListenThread.Sleep(1)

        DoReadLoop()

    End Sub

 

'-------------------------------------

 

    Private Sub DoRead(ByVal ar As IAsyncResult)

        Dim BytesRead As Integer

        Dim strMessage As String

        Try

            ' Finish asynchronous read into readBuffer and return number of bytes read.

            BytesRead = client.GetStream.EndRead(ar)

 

            ' Convert the byte array the message was saved into, minus two for the Chr(13) and Chr(10)

            strMessage = Encoding.ASCII.GetString(readBuffer, 0, BytesRead - 2)

 

            ProcessCommands(strMessage)

 

            ' Start a new asynchronous read into readBuffer.

            client.GetStream.BeginRead(readBuffer, 0, READ_BUFFER_SIZE, AddressOf DoRead, Nothing)

        Catch e As Exception

        End Try

    End Sub

 

    Private Sub ProcessCommands(ByVal strMessage As String)

        ' Process the command received from the server, and take appropriate action.

        Dim dataArray() As String

 

        ' Message parts are divided by "|"  Break the string into an array accordingly.

        dataArray = strMessage.Split(Chr(124))

 

        ' dataArray(0) is the command.

        Select Case dataArray(0)

            Case "JOIN"

                ' Server acknowledged login.

                bolJoined = True

                DisplayText("You have joined the game" & Chr(13) & Chr(10))

                SendData("REQUESTUSERS")

            Case "CHAT"

                ' Received chat message, display it.

                DisplayText(dataArray(1) & Chr(13) & Chr(10))

            Case "REFUSE"

                ' Server refused login with this user name, try to log in with another.

                bolJoined = False

                'AttemptLogin()

            Case "LISTUSERS"

                ' Server sent a list of users.

                ListUsers(dataArray)

            Case "BROAD"

                ' Server sent a broadcast message

                DisplayText("ServerMessage: " & dataArray(1) & Chr(13) & Chr(10))

            Case "UPDATEPOSITION"

                UpdateSnowmanPosition(dataArray)

            Case "SNOWBALL"

                If Not InStr(strUsername, dataArray(7)) > 0 Then UpdateSnowballPosition(dataArray)

                DisplayText("Snowball thrown by " & dataArray(7) & ControlChars.NewLine)

            Case "ISJOINING"

                players.Add(dataArray(1), New clsPlayer(Scene))

                players(dataArray(1)).Name = dataArray(1)

                lstUsers.Items.Add(dataArray(1))

        End Select

    End Sub

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å
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...