2012-04-25 5 views
2

현재 제가 쓰고있는 프로그램에 약간의 문제가 있습니다.네트워크 통신 - 일부 정보가 서버에 도달하지 않습니다.

먼저 무엇을 달성해야하는지 설명하겠습니다.

채팅 프로그램과 매우 비슷하므로 클라이언트 창에서 일어나는 일들에 관한 데이터를 저장하는 정보 등급 (기본적으로 패킷이라고 함)이 기본적으로 있습니다. 프로그램 자체는 두 개의 서로 다른 클라이언트 창과 서버 창으로 구성됩니다. (그리고 각각의 창을 열려면 하나의 창,하지만 지금은 중요하지 않습니다.)

그래서 클라이언트는 창에서 몇 가지 사항을 변경하고 보내기를 누릅니다.

그러면 내 패킷은이 모든 정보를 저장합니다. 닉네임, 텍스트, 다른 숫자 값 (기본 주사위 결과) 및 '종류'변수로 구성됩니다.

세 가지 종류의 메시지가 있습니다.이 '패킷'은 전송할 수 있습니다. 3 주사위 결과, 어려움 값과 재능 값으로 구성된

  1. 정상적인 문자 메시지 (완벽하게 작동합니다.)
  2. 주사위 메시지.
  3. 이번에는 하나의 주사위 결과만으로 구성된 또 다른 주사위 메시지입니다.

메시지가 전송 될 때마다 (따라서 서버가 수신 할 때) 프로그램은 어떤 종류의 메시지인지 (종류 변수를 통해) 읽은 다음 메시지 처리 방법을 다른 단계로 시작합니다 입니다.

채팅 메시지는 채팅에 표시되고 각 클라이언트에 보내져 표시됩니다. 이것은 버그 나 무언가없이 작동합니다.

Dice-Messages는 둘 다 표시 할 특별한 모양을 가지고 있습니다. 기본적으로 주사위 - 굴림이 성공적인지 여부를 보여주기 위해이 모양으로 생성 된 채팅 메시지입니다.

나는 (내 로케일 IP 사용) LAN-버전에서 프로그램을 테스트하고 완벽하게 작동 :


지금 내 문제가 온다. 문제가없고 버그도 없습니다.

하지만 이제 친구에게 인터넷을 통해 테스트 해 볼 것을 요청했습니다. Portforwarding이 작동하여 클라이언트가 연결할 수 있습니다.

이제 메시지를 보냅니다. 채팅 메시지 : 잘 작동 Dice1- 메시지 : 닉네임과 주사위 결과 두 개만 전송됩니다. 세 번째 주사위 결과는 나타나지 않으며 모든 다른 정보는 빈 칸을 남겨 두었습니다. 두 번째 주사위 메시지에 대해서도 동일합니다. 주사위 결과와 닉네임 만 표시하지만 다른 정보는 표시하지 않습니다.

알아두면 프로그램은 Delphi 6.0으로 작성되었습니다 (예, 조금 오래되었지만이 프로그램에 익숙하지만 아직 프로그래밍에 익숙하지 않습니다 ... 그래서 기본 학습에 사용합니다). 그리고 저의 첫 번째 큰 프로그램을 작성하기위한 것입니다.)

패킷 자체가 다음과 같이 정의된다 :

ServerSocket.Socket.ReceiveBuf (Packet, SizeOf(Packet)); 

MemMessage.Lines.Add(Packet.Nickname + 'succeeded his dice roll for ' + Packet.Talent + 'Restofmessage ' + Packet.OtherInformation) 

을 따라서 .. 얼마나 한게 :

type 
TPacket = Record 
    Nickname : string[255]; 
    Text  : string[255]; 
    OtherInfo: VarType; 
end; 

등 메시지는 다음과 같이 정보를 말한다. 문제가 무엇인지 알고있는 사람 (이 경우 LAN과 인터넷의 문제를 야기하는 것이 무엇인지)을 알고이를 해결할 수있는 방법을 알고 있기를 바랍니다. 사전에

감사합니다 ~

PS : 서버는 그냥 경우이에 .. 단순히 모든 클라이언트가하는, 그것을 같은 방식으로 표시 정보를 받아 다음 수신을 표시하는 각 클라이언트에 직접 전송 중요 할 수도 있습니다.


편집 :

흠 나는 '다른 정보는'정말 다른이 arent 때문에 그것이이었다 같은 정보가 좋았다고 생각했다.

는하지만 괜찮아요 ~ 자세한 내용은 '기타 정보'모든 타입 문자열 [255]이

을 :), 어쨌든 그 중 하나는 부울입니다.

Dice1 - 유형은 다음과 같은 사용

닉네임, 결과 1, Result2, Result3, 난이도, Skillpoints, 탤런트 (재능 그냥 이름, 주사위 롤 테스트의 성공 또는 실패를) 및 'Success'라는 부울

Dice2는 사용

닉네임, 결과, 난이도, 탤런트 (재능의 이름 값 ^^위한 청춘의 실수.), TalentPoints (Basicially 인재의 값을 뺀 dice-의 어려움을 roll)과 부울 'Success'뿐만 아니라 'Kind'라는 문자열 [255]이 포함됩니다. 종류의 기능은 위에 설명되어 있습니다.

프로토콜에 관해서는, 나는 내가 생각, 특별한 하나를 사용하지 말아 ..

는 단순히 sendbuf는하고 ReceiveBuf를 통해 전체 클래스 TPacket을 보낼 수 있습니다. 프로그래밍에 익숙하지 않아 유감스럽게 생각보다 많은 정보를 드릴 수는 없습니다./

TPacket을 수신하고 리디렉션하는 절차에 대한 코드를 알려 드리겠습니다.

는 내가 독일어에 프로그램을 작성하기 때문에 내가 어떤 실수를하지 않았 으면, 그래서 나는 지금 그것을 번역해야 .. ^^ 내 TPacket의

procedure TFormServer.ServerSocketClientRead (Sender:TObject; Socket: TCustomWinSocket); 
var 
Message : TPacket; 
i  : ShortInt; 
begin 
    {receive Message} 
Socket.ReceiveBuf (Message, SizeOf(Message)) 

{checking, what kind the message is of} 
    if Message.Kind = 'Chat' then begin 
    MemMessage.Lines.Add ('By' + Message.Nickname + ': ' + Message.Text); 

    {Redirecting to other clients} 
    with ServerSocket.Socket do begin 
     for i := 0 to ActiveConnections -1 do 
     Connections[I].SendBuf(Message, SizeOf(Message)) 
    end; 
    end; 


    if Message.Kind = 'DiceRoll_1' then begin 
    if Message.Success = true 
     then MemMessage.Lines.Add (Message.Nickname + ' succeeded his Dice Roll for ' + Message.Talent + ' (Difficulty: ' + Message.Difficulty + ') with the results: (' + Message.Result1 + '/' + Message.Result2 + '/' + Message.Result3 + '), Skill lvl: ' + Message.Skillpoints); 

//The Message should look like that 
{Dummy succeeded his Dice Roll for climbing (Difficulty: 4) with the results: (12/14/13), Skill lvl: 8} 

//For Comparison, the buggy message looks like that 
{Dummy succeeded his Dice Roll for (Difficulty: ) with the results: (12/14/), Skill lvl: } 
     else {The same as when it succeeded, just saying it did 'not' succeed..} 


    {redirection} 
    with ServerSocket.Socket do begin 
     for i := 0 to ActiveConnections -1 do 
     Connections[i].SendBuf (Message, SizeOf(Message)) 
    end; 

    end; {if kind = DiceRoll_1} 

{Same goes for the DiceRoll_2 now. This time it uses: Message.Nickname, Message.Talent, Message.Difficulty, Message.Result, Message.TalentPoints, Message.Success} 

이제 다시 전체 선언 .. 중요 할 수도 있습니다

type TPacket = Result 
    Kind  : string[255]; 
    Nickname : string[255]; 
    Text  : string[255]; 
    Result  : string[255]; 
    Result1  : string[255]; 
    Result2  : string[255]; 
    Result3  : string[255]; 
    Difficulty : string[255]; 
    Talent  : string[255]; 
    Skillpoints : string[255]; 
    TalentPoints: string[255]; 
    Succeed  : boolean; 
end; 

나는 그것이 더 이해할 수 있도록하기를 바랍니다. 나는 설명하기에 좋지 않습니다./

그러나 여기에 저를 환영 해 주셔서 감사합니다. 저는 여러분 모두를 괴롭히지 않고 하나 또는 다른 경우에도 도움이되기를 바랍니다.

+0

StackOverflow에 오신 것을 환영합니다! 나는'OtherInfo'와 전체'SizeOf (Packet)'을 여기에서 의심 할 것입니다. (packed record alignment로 해결할 수있는 것은, 지금 당장 추측입니다.) 그러나 적어도 "OtherInfo"가 포함 할 수있는 것을 우리에게 보여주고 어떤 라이브러리 (Indy라고 추측합니다)와 어떤 프로토콜을 사용하고 있는지와 같은 세부 사항을 추가 할 수 있습니까? 나는 당신의 프로그램이 어떻게 작동하는지에 대한 설명이이 세부 사항보다 덜 유용하다고 생각한다. 어쨌든, 데이터가 서버에 도달하지 않았습니까? Wireshark 또는 이와 유사한 도구를 사용하려고 했습니까? – TLama

+0

"귀하의"메시지는 "코드가 'TPacket'에 대해 귀하가 제공 한 정보와 일치하지 않습니다. 필요한 정보를 제공하지 않을 때 문제를 파악하는 데 도움을주기 위해 어떻게해야합니까? :) 질문을 편집하고 긴 설명 텍스트를 제거하고 함께 사용되는 세부 사항으로 중요한 부분을 업데이트하십시오. 도움을받을 가능성이 크게 높아집니다. 감사. :) –

답변

0

어떤 프로토콜을 사용하고 있습니까?

UDP이면이 프로토콜의 "의도적"입니다.

안정성이 부족한 UDP 응용 프로그램은 일반적으로 일부 손실, 오류 또는 복제를 기꺼이 받아 들여야합니다.

조건이 최적이기 때문에 그것은 문제없이 작동, 로컬 네트워크에 걸쳐

http://en.wikipedia.org/wiki/User_Datagram_Protocol를 참조하십시오.

하지만 인터넷을 통해 일부 패킷이 손실됩니다.

데이터를 라우팅하려면 UDP를 통해 응용 프로그램에 확인 응답 + 재전송 메커니즘을 추가하거나 (TCP/IP 프로토콜을 참조하십시오) TCP, 패킷 손실의 경우 데이터를 전송하려고 시도하지만 더 큰 오버 헤드가 발생합니다.

일반 채팅 프로그램의 경우 TCP를 사용합니다 (필요한 경우 HTTP 레이아웃을 사용하는 경우에도 방화벽 친화적입니다). 이 프로토콜이 양방향이 아니더라도 일정 기간 (예 : 1 초)에 데이터를 가져와 서버에 보류중인 메시지를 요청할 수 있습니다.

그런데 string[255]을 사용하는 것은 응용 프로그램 계층에서 약간 무례합니다. # 13 # 10 (줄 바꿈) 종료 텍스트 또는 # 0 종료 텍스트까지 사용할 수 있습니다. 채팅 중에는 대부분의 패킷이 사용되지 않으므로 텍스트에 255자를 초과하여 사용할 수 없습니다. 또는 메시지 레이아웃을 위해 XML 또는 JSON과 같은 표준 형식을 사용하십시오.

+0

나는 UDP, TCP 또는 심지어 다른 프로토콜을 사용하고있다. 실제로 편집 된 정보에서 나는 내가 사용하는 것을 명확하게 추가했다. 내가 말한 것에 의해 추측 할 수있는 한 UDP이어야합니다. (코멘트를 입력 할 필요는 없습니다 .. 예 ~) 그러나 흥미로운 점은 항상 같은 정보가 빠져 있다는 것입니다. 나에게 (그리고 내가 생각하는 패킷 손실), 그것은 정보의 고정 된 종류가 아닌 무작위 정보를 잃어 버려야한다. 나는 단지 네트워크 통신을 아직 충분히 이해하지 못한다고 생각한다 ... – user1016675

+0

그것은 당신이 어떻게 소켓. 나는 이것이 UDP라고 생각한다. 귀하의 목적을 위해 메시지의 JSON 형식으로 TCP 또는 HTTP를 사용하는 것이 좋습니다. –

+0

나는 그것이 TCP라는 의혹을 가지고 있습니다. TServerSocket 구성 요소처럼 보입니다. –

2

흠 .. 내가 생각하고있는 구성 요소라면 ClientRead 이벤트가 전체 메시지로 가득 차있는 잠금이 아닙니다.

메시지 전송을 허용하려면 TCP 위에 프로토콜이 필요합니다. TCP 자체는 메시지를 전송할 수 없으며 바이트 스트림 만 전송할 수 있습니다.

TCustomWinSocket 워드 프로세서, (내 이탤릭체) :

'를 사용 ReceiveBuf 소켓 구성 요소의하여 OnRead 윈도우 소켓 객체의 OnSocketEvent 이벤트 처리기에서 또는 소켓 연결 또는 OnClientRead 이벤트 처리기에서 읽을 수 있습니다.

ReceiveBuf 바이트의 수는 실제로 (어떤 전화에서 요청 된 수보다 적을 수 있습니다)를 읽어 반환합니다.

읽을 바이트가없는 경우 ReceiveBuf는 -1을 반환합니다. '

그래서 초대형 버퍼를 선언하고 수신 된 데이터를 구문 분석하지 않도록 항상 로트를 전송하는 교활한 계획은 효과가 없습니다.

올바르게 처리해야합니다. ClientRead() 이벤트가 전체 메시지, 부분 메시지, 결합 된 메시지 또는 매번 한 바이트 씩 여러 번 발생하는지 여부에 상관없이 작동하는 프로토콜을 디자인하십시오.내가 선호하는 디자인은 'PDUL'클래스를 'byteLoad (thischar : char) : boolean;'과 함께 사용하는 것이다. 메서드를 사용하여 프로토콜을 처리하고 내부 데이터를 사용하여 필드를로드하고 완전한 메시지가 어셈블 된 경우에만 true를 반환합니다. 모든 ClientRead 호출에서 모든 수신 바이트를 반복하고 'true'를 반환 할 때까지 'byteLoad'를 호출 한 다음 PDU를 처리합니다 (어딘가에 대기열에 넣음으로써). 다른 PDU를 만들고이를로드하기 시작합니다.

스레드가 아닌 비동기 TServerSocket 선택 사항을 사용하기 때문에 PDU 인스턴스 또는 ClientRead 호출에서 보존해야하는 다른 모든 데이터는 다음과 같은 TCustomWinSocket 인스턴스의 필드로 저장해야합니다. 이 데이터를 보유하는 TCustomWinSocket 자손이거나 다른 객체 또는 레코드를 보유하기 위해 기본 TCustomWinSocket의 'data'속성을 사용하는 것을 의미합니다. 그 물건은 보통 OnConnected 이벤트에서 생성/초기화 /로드됩니다. 연결 해제시 TCustomWinSocket의 동작을 조심하십시오 - 데이터 필드의 비 - 무제한 포인터를 해제하려고하는 재미있는 느낌이 들었습니다.

관련 문제