forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   2D-программирование (http://forum.boolean.name/forumdisplay.php?f=13)
-   -   TCP\IP клиент-сервер загвоздка (http://forum.boolean.name/showthread.php?t=19455)

ILonion 02.10.2014 21:41

TCP\IP клиент-сервер загвоздка
 
Доброго времени суток!
Разъясните незнающему, пожалуйста, кто в теме:

Сделал образцы сервера и клиента tcp. Работают...
Загвоздка в том, что если запустить 15+ клиентов, то сервер полностью зависает (на приеме пакетов. принт показывает приход 8192 байт с одного потока, в этот момент), пока не прибить некоторое кол-во клиентов.
В чем здесь причина? Неправильный размер пакета, переполнение TCP буфера...? Или еще что-то?
Разъясните кто-нибудь...

сервер:
Код:

;//////////////////////////////////////////////////////////////////////
;//////////////////////////////////////////////////////////////////////
;SERVER
;//////////////////////////////////////////////////////////////////////
;//////////////////////////////////////////////////////////////////////

;request code (action id):
        ;SERVER > CLIENT
                ;1001 - create player
                ;1004 - update pos
               
        ;CLIENT > SERVER
                ;1002 - request
       
        ;global vars:
        Global player_count
        Global PACK_SIZE = 536 ;bytes
        Global PORT = 49001

        Type player_type
                Field player_counter
                Field x#,y#,z#
                Field mesh
                Field stream
        End Type
       
        ;global massives:
        Global S_server
        Global S_serv_bank = CreateBank(PACK_SIZE)

       
        ;default mode for pivots:
        Graphics3D 320,240,0,2

        ;CREATE SERVER:
        S_server = CreateTCPServer(PORT)
        If Not S_server
                RuntimeError "cannot create"
                End
        Else
                Print "created"
        End If





;###########################################################################################
        ;loop
;###########################################################################################
While Not KeyHit(1)
       
        ;CONNECT\CREATE NEW PLAYER:
        S_define_new_stream()

        ;READ REQUESTS FROM CLIENT:
        For p.player_type = Each player_type
                If p.player_type <> Null
                        If Not Eof(p\stream)
;debug:
If ReadAvail(p\stream) > 0
;DebugLog "incoming bytes: " + ReadAvail(p\stream)
Print "incoming bytes: " + ReadAvail(p\stream)
End If
;debug.
                                Repeat
                                        If ReadAvail(p\stream) => PACK_SIZE
                                                ReadBytes(S_serv_bank,p\stream,0,PACK_SIZE)
                                                ;request code:
                                                If PeekInt(S_serv_bank,0) = 1002
                                                        ;request type:
                                                        inner_request = PeekInt(S_serv_bank,4)
                                                        ;execute request:
                                                        S_request_execute(p.player_type,inner_request)
                                                       
                                                        ;send result of request execute to all clients:
                                                        x# = EntityX(p\mesh)
                                                        y# = EntityY(p\mesh)
                                                        z# = EntityZ(p\mesh)
                                                        yaw# = EntityYaw#(p\mesh)
                                                        p\x = x
                                                        p\y = y
                                                        p\z = z
                                                        pl_id = p\player_counter
                                                        ACTION_ID = 1004
                                                        S_send_actions_to_all(ACTION_ID,x#,y#,z#,yaw#,pl_id)
                                                       
                                                End If
                                        End If
                                Until ReadAvail(p\stream) < PACK_SIZE
                        Else
                                Print "client crashed"
                                Delete p
                        End If
                End If
        Next
       
Wend
;###########################################################################################
Function S_define_new_stream()
;connect\create new player:

        ;if new stream (client) defined:
        S_inner_stream = AcceptTCPStream(S_server)
        If S_inner_stream
               
                Print "new player connected"
               
                ;NEW PLAYER (local. create pivot like pos\rot clone (header) of client player entity):
                        p.player_type = New player_type
                        ;player link (stream) = connected stream id:
                        p\stream = S_inner_stream
                        ;player pivot:
                        p\mesh = CreatePivot()
                        ;counter (id):
                        player_count = player_count + 1
                        Print "count of players: " + player_count
                       
                ;SEND TO ALL: CREATE NEW PLAYER:
                        PositionEntity p\mesh,0,0,0
                        p\player_counter = player_count
                        pl_id = player_count
                        x# = EntityX(p\mesh)
                        y# = EntityY(p\mesh)
                        z# = EntityZ(p\mesh)
                        p\x = x
                        p\y = y
                        p\z = z
                        ACTION_ID = 1001
                        S_send_actions_to_all(ACTION_ID,x#,y#,z#,yaw#,pl_id)
                       
                ;SEND TO NEW STREAM: ALL EXIST PLAYERS:
                        For p.player_type = Each player_type
                                ;if not eof:
                                If Not Eof(S_inner_stream)
                                        ;and not self:
                                        If p\player_counter <> player_count
                                                ;action id:
                                                PokeInt S_serv_bank,0,1001
                                                ;x,y,z
                                                PokeFloat S_serv_bank,4,p\x
                                                PokeFloat S_serv_bank,8,p\y
                                                PokeFloat S_serv_bank,12,p\z
                                                ;id
                                                PokeInt S_serv_bank,16,p\player_counter
                                                ;send
                                                WriteBytes S_serv_bank,S_inner_stream,0,PACK_SIZE
                                        End If
                                Else
                                        Print "client crashed"
                                        Delete p
                                End If
                        Next
        End If
       
End Function
;###########################################################################################
Function S_request_execute(p.player_type,inner_request)
;execute request:

        speed# = 0.9

        ;request type:
        Select inner_request
                ;z+
                Case 1
                        ;move server pivot:
                        MoveEntity p\mesh,0,0,speed
                ;z-
                Case 2
                        MoveEntity p\mesh,0,0,-speed
                ;x+
                Case 3
                        TurnEntity p\mesh,0,1,0
                ;x-
                Case 4
                        TurnEntity p\mesh,0,-1,0
                ;y+
        ;        Case 5
        ;                TurnEntity p\mesh,0,1,0
                ;y-
        ;        Case 6
        ;                TurnEntity p\mesh,0,-1,0
        End Select
       
End Function
;###########################################################################################
Function S_send_actions_to_all(ACTION_ID,x#,y#,z#,yaw#,pl_id)
;send actions to all clients:

        For p.player_type = Each player_type
                If p.player_type <> Null
                        If Not Eof(p\stream)
                                ;action:
                                PokeInt S_serv_bank,0,ACTION_ID
                                ;x,y,z
                                PokeFloat S_serv_bank,4,x#
                                PokeFloat S_serv_bank,8,y#
                                PokeFloat S_serv_bank,12,z#
                                PokeFloat S_serv_bank,20,yaw#
                                ;id
                                PokeInt S_serv_bank,16,pl_id
                                ;send:
                                WriteBytes S_serv_bank,p\stream,0,PACK_SIZE
                        Else
                                Print "client crashed"
                                Delete p
                        End If
                End If
        Next
       
End Function

клиент:
Код:

;//////////////////////////////////////////////////////////////////////
;//////////////////////////////////////////////////////////////////////
;CLIENT
;//////////////////////////////////////////////////////////////////////
;//////////////////////////////////////////////////////////////////////

;request code (action id):
        ;SERVER > CLIENT
                ;1001 - create player
                ;1004 - update pos
               
        ;CLIENT > SERVER
                ;1002 - request
               
               
        ;global vars:
        Global game_limit_speed = CreateTimer(60)
        ;
        Global PACK_SIZE = 536 ;bytes
        Global PORT = 49001
        Global IP$ = "localhost" ;set ip here
        ;
        Global player_created

        Type player_type
                Field player_counter
                Field x#,y#,z#
                Field mesh
        End Type
       
        ;global massives:
        Global C_server_link
        Global C_client_bank = CreateBank(PACK_SIZE)


        ;graphics:
        Graphics3D 640,480,16,2
       
        ;3d misc for scene:
        Global light = CreateLight(1)
        PositionEntity light,0,10,0
        Global player_mesh = CreateSphere()
        HideEntity player_mesh
        Global box = CreateCube()
        Global camera = CreateCamera()
        PositionEntity camera,0,20,-15
        RotateEntity camera,50,0,0
        CameraZoom camera,2
        CameraFogRange camera,2,100
        CameraFogMode camera,True
        CameraFogColor camera,150,200,255
        CameraClsColor camera,150,200,255
        grass=CreateTexture(64,64,1)
        SetBuffer TextureBuffer(grass)
        ClsColor 22,100,22
        Cls
        For i=0 To 256
                Color 0+Rnd(10),50+Rnd(155),0+Rnd(10)
                Plot Rnd(64),Rnd(64)
        Next
        SetBuffer BackBuffer()
        ScaleTexture grass,3,3 
        Color 255,255,255
        ClsColor 0,0,0
        plane=CreatePlane()
        EntityTexture plane,grass


        ;CONNECT TO SERVER:
        ;Print "PRESS ENTER"
        Repeat
                ;If KeyHit(28);enter
                        C_connect_to_server()
                ;End If
        Until C_server_link <> 0




;###########################################################################################
        ;loop
;###########################################################################################
period = 1000 / 40
time = MilliSecs() - period

While Not KeyHit(1)

        ;waiter:
        WaitTimer(game_limit_speed)
       
        ;fractional remainder:
        Repeat
                elapsed = MilliSecs() - time
        Until elapsed
        ticks=elapsed/period
        tween#=Float(elapsed Mod period)/Float(period)
        For k=1 To ticks
                If k=ticks Then CaptureWorld
                time=time+period
                        ;##########################
                                ;
                                calculate_game()
                                ;
                        ;##########################
                UpdateWorld
        Next
        AntiAlias True
        RenderWorld tween
       
        ;wire:
        If KeyHit(57)
                wire = 1 - wire
        End If
        WireFrame wire
       
        ;pl.count:
        i = 0
        For p.player_type = Each player_type
                i = i + 1
        Next
        Text 10,40,i + " - players"
       
        Flip
Wend
;###########################################################################################
Function calculate_game()

        ;READ REQUESTS FROM SERVER:
        If Not Eof(C_server_link)
                Repeat
                        If ReadAvail(C_server_link) => PACK_SIZE
                                ReadBytes(C_client_bank,C_server_link,0,PACK_SIZE)
                                ;INITIALIZE ME:
                                If player_created = 0
                                        ;create me:
                                        If PeekInt(C_client_bank,0) = 1001
                                                ;create me:
                                                p.player_type = New player_type
                                                p\mesh = CopyEntity(player_mesh)
                                                anim_entity = p\mesh
                                                ShowEntity p\mesh
                                                p\player_counter = PeekInt(C_client_bank,16)
                                                x# = PeekFloat(C_client_bank,4)
                                                y# = PeekFloat(C_client_bank,8)
                                                z# = PeekFloat(C_client_bank,12)
                                                PositionEntity p\mesh,x,y,z
                                                ;
                                                ;player created (me):
                                                player_created = 1
                                        End If
                                       
                                ;ALL OTHER ACTIONS:
                                End If
                                If player_created = 1
                                        ;create other players:
                                        If PeekInt(C_client_bank,0) = 1001
                                                p.player_type = New player_type
                                                p\mesh = CopyEntity(player_mesh)
                                                ShowEntity p\mesh
                                                p\player_counter = PeekInt(C_client_bank,16)
                                                x# = PeekFloat(C_client_bank,4)
                                                y# = PeekFloat(C_client_bank,8)
                                                z# = PeekFloat(C_client_bank,12)
                                                PositionEntity p\mesh,x,y,z
                                        End If
                                       
                                        ;update pos (me and other):
                                        If PeekInt(C_client_bank,0) = 1004
                                                id = PeekInt(C_client_bank,16)
                                                For p.player_type = Each player_type
                                                        If p\player_counter = id
                                                                x# = PeekFloat(C_client_bank,4)
                                                                y# = PeekFloat(C_client_bank,8)
                                                                z# = PeekFloat(C_client_bank,12)
                                                                yaw# = PeekFloat(C_client_bank,20)
                                                                PositionEntity p\mesh,x,y,z
                                                                RotateEntity p\mesh,0,yaw,0
                                                        End If
                                                Next
                                        End If
                                End If
                        End If
                Until ReadAvail(C_server_link) < PACK_SIZE
        Else
                RuntimeError "server crashed"
                End
        End If
       
        ;SEND REQUESTS TO SERVER:
        If player_created = 1
                ;random player motion:
                SeedRnd MilliSecs()
                request = Rand(1,4)
                C_server_request(request)
                       
                ;manual motion:
        ;        If KeyDown(200);z+
        ;                request = 1
        ;                C_server_request(request)
        ;        End If
        ;        If KeyDown(208);z-
        ;                request = 2
        ;                C_server_request(request)
        ;        End If
        ;        If KeyDown(205);x+
        ;                request = 3
        ;                C_server_request(request)
        ;        End If
        ;        If KeyDown(203);x-
        ;                request = 4
        ;                C_server_request(request)
        ;        End If
        End If

End Function
;###########################################################################################
Function C_connect_to_server()
;connect to server:

        Print "connecting..."
        C_server_link = OpenTCPStream(IP$,PORT)
        If Not C_server_link
                RuntimeError "failed"
                End
        End If

End Function
;###########################################################################################
Function C_server_request(request)
;send request:

        If Not Eof(C_server_link)
                PokeInt C_client_bank,0,1002
                PokeInt C_client_bank,4,request
                WriteBytes C_client_bank,C_server_link,0,PACK_SIZE
        Else
                RuntimeError "server crashed"
        End If
       
End Function


moka 02.10.2014 23:37

Ответ: TCP\IP клиент-сервер загвоздка
 
Я писал сетевые приложения на блице, были проблемы с 10+ клиентами тоже. Разные, и все очень странные.
В основном дело в том что с сетью в блокирующем контексте работать по опреелению - нельзя. Причин много, и об этом не мало литературы.
Следственно серверные решения нужно писать в асинхронной либо мультипоточной модели.
Первая - асинхронная, показала себя как весьма масштабируемая и применяется в очень большом спектре реальных серверных решений.
Но блиц не поддерживает никакой асинхронности в коде. Я не знаю как там задник сделан, но проблемы я свои еще много лет назад по поводу сети на нем так и не решил.

Рассмотри другие языки для серверной логики.
Также серверная логика не имеет ничего общего к клиентской, та же система Entity и всякие рендеры, коллизия и т.п. - все очень отличается на серверах от клиентов.
Следственно тут не суть важно какое серверное решение ты выберешь, если оно не походит на клинета - это не аргумент вообще.

Если знаешь JS, пиши на node.js - так будет проще стартовать, и быстрее будет результат.

impersonalis 03.10.2014 00:24

Ответ: TCP\IP клиент-сервер загвоздка
 
Можно попробовать размазать опрос по времени, внеся правку: читать полученные сообщение не "пока не прочитан PACK целиком", а "N байт и выходить из обработки". Но такая асинхронность выйдет боком: код запутается и станет скорее изыском, лучше, конечно, запилить асинхронность на потоках. Например (при неимении возможности поступить как советует moka), написать dll на чём-то многопоточность поддерживающем, и либо общаться через блокирующие blitz-прогу функции (ЕстьЛиГотовыйПак(), ПролучитьЗагурженныйПак()), либо через общий буфер (число, возвращаемое CreateBank - реальный адрес памяти, который можно использовать в DLL), имеющий маркер (изменяемый атомарно) для управления синхронизацией ("пакет готов","ожидаю следующий пакет") и область под тело пакета.

ILonion 03.10.2014 01:21

Ответ: TCP\IP клиент-сервер загвоздка
 
moka, impersonalis, благодарю вас!
Теперь имею представление о происходящем.
Буду думать над предложениями...

L-ee-X 04.10.2014 16:14

Ответ: TCP\IP клиент-сервер загвоздка
 
Проще серверную часть на другом яп. писать . Пиши в лс могу помочь. Даже кодом поделюсь реализации серверной части.

ILonion 04.10.2014 20:20

Ответ: TCP\IP клиент-сервер загвоздка
 
L-ee-X, большущее спасибо!
Решил пока что оставить сетевые вопросы, и взяться за другое.

P.S. Если кому-то будет интересно:
В вышеприведенном примере удалось сделать нормальную работу с 54 клиентами (еще больше клиентов "матрица" видеокарты не позволяла шибко, а без граф. режима дописывать и проверять просто не стал), через реализацию ожидания ответа от сервера (ничего не предпринимать) на конкретный запрос клиента.


Часовой пояс GMT +4, время: 12:56.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot