2012-04-25 12 views
2

私は現時点で書いているプログラムに少し問題があります。ネットワーク通信 - 一部の情報がサーバーに届かない

まず、達成すべきことを説明しましょう。

チャットプログラムとよく似ているので、クライアントウィンドウで起こるいくつかのことに関するデータを格納する情報クラス(私はそれをパケットと呼ぶ)を基本的に持っています。プログラム自体は、2つの異なるクライアントウィンドウと1つのサーバーウィンドウで構成されています。 (そしてそれぞれのウィンドウを開くには1つのウィンドウが重要ですが、今は重要ではありません)

クライアントはウィンドウ上で何かを変更して送信を押します。

次に、私のパケットはこの情報をすべて保存します。ニックネーム、テキスト、いくつかの異なる数値(基本的にはダイスロール結果)、および「種類」変数で構成されています。

3種類のメッセージがあります。この 'パケット'は送信可能です。

  1. 通常のテキストメッセージ(完全に動作。)
  2. ダイスメッセージ、3つのダイス-結果、困難値と才能値からなります。
  3. もう1つのダイスメッセージですが、今回は1つのダイス結果のみで構成されています。

メッセージが送信され(したがってサーバーによって受信されると)、プログラムは(kind-variableを介して)どんな種類のメッセージであるかを読み取って、それに応じてメッセージを処理するためのさまざまな手順を開始します。です。

チャットメッセージは単にチャットで表示され、各クライアントに送信されて表示されます。バグや何もせずに動作します。

ダイスメッセージには、表示される特殊な形状があります。 ダイスロールが成功したかどうかを示すために、基本的にこの図形で生成されたチャットメッセージ。


今私の問題は来る:

私は(私のロケール-IPを使用して)LAN-バージョンでプログラムをテストし、それは完全に働きました。問題はなく、バグもありません。

しかし、今私は友人にインターネットでのテストを手伝うように頼んだ。 Portforwardingが機能し、クライアントは接続できます。

メッセージを送信します。 チャットメッセージ:正常に動作します Dice1-Message:ニックネームと2つのダイス結果のみが送信されます。 3番目のサイコロの結果は表示されず、他のすべての情報が空白のまま残り、表示されます。 2番目のサイコロのメッセージにも同じです。ダイス結果とニックネームのみを表示しますが、その他の情報は表示しません。

このプログラムは、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:サーバーは、単に情報を受け取り、それを同じように表示するには、任意のクライアントを行い、その後、受信して表示し、各クライアントに直接送信...念のため、この重要かもしれません。


編集:

フム「その他の情報」は本当に違うアレントので、私は、それがあったような情報が良かったと思いました。

でも大丈夫〜詳細は

「その他情報」の全てを:)は、そのうちの一つがブールであるとにかく、[255]タイプの文字列を持っています。

Dice1 -Typeは、以下を使用します。

ニックネーム、結果1、結果2、Result3、難しさ、Skillpoints、タレント(才能だけの名前、ダイスロールテストの成功または失敗を上)とブール値は「成功」です。

Dice2は使用しています:

ニックネーム、結果、難易度、タレント(才能の名前値^^ためいけないミス。)、TalentPoints(Basiciallyタレントの値マイナスdice-の難易度ロール)、ブール値「Success」、「Kind」という文字列[255]が含まれます。種類の機能は上で説明した。

プロトコルに関しては、私は私が思うに、特別なものを使用していけない...

私は単にSendBufとReceiveBufを経由して、クラス全体の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} 

今、再び私TPacketの全体宣言^^ ..私はどんなミスをしないことを望みます。..重要かもしれない

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レコードの整列によって解決されるかもしれない、ちょうど今の推測です)。しかし、少なくとも「OtherInfo」が含むかもしれないものを見せて、どんなライブラリ(私がIndyと思う)やどのプロトコルを使っているのかといった詳細を追加できますか?あなたのプログラムがどのように動作するかについての記述は、ここの詳細よりもあまり役に立たないと思います。とにかく、データがサーバーに到達していないと確信していますか? Wiresharkなどのツールを使用しようとしましたか? – TLama

+0

あなたの「メッセージには「コードは、あなたが 'TPacket'のために私たちに与えた情報と一致しません。必要な情報を提供していないときにどのように問題を把握するのに役立つのでしょうか? :)あなたの質問を編集し、長い説明文の一部を削除し、一緒になって詳細が重要な部分を更新してください。それはあなたの助けを得るチャンスを大幅に向上させます。ありがとう。 :) –

答えて

0

どのプロトコルを使用していますか?

これがUDPの場合、これらの問題はこのプロトコルの「設計上の問題」です。

信頼性が欠如しているため、UDPアプリケーションは、通常、損失、エラーまたは重複を受け入れる必要があります。

は条件が最適であるため、それは、問題なく動作しますが、ローカルネットワークにわたり

http://en.wikipedia.org/wiki/User_Datagram_Protocolを参照してください。

インターネット経由では、一部のパケットが失われます。

データをルーティングしたい場合は、UDP経由でアプリケーションに肯定応答+再送メカニズムを追加するか(単純な場合はTFTPプロトコルを参照)、TCPなどのより高度なプロトコルを使用するか、パケット損失の場合にデータを送信するために再試行しますが、オーバーヘッドが大きくなります。

プレーンチャットのプログラムでは、TCPを使用している(必要に応じてHTTPレイアウトを使用していても、これはファイアウォールの方が親切です)。このプロトコルがデュプレックスではない場合でも、定期的な期間(1秒など)にデータを取得して、サーバーに保留中のメッセージを要求することができます。

ところで、string[255]を使用すると、アプリケーション層では少し失礼です。 #13#10(改行)で終了したテキスト、または#0で終了したテキストを使用することは意味があります。あなたのパケットのほとんどはチャット中に使用されず、テキストに255文字を超える文字を使用することはできません。また、メッセージレイアウトにXMLやJSONなどの標準形式を使用することもできます。

+0

私はUDP、TCPまたは別のプロトコルを使用しているwheaterを実際に知りません..編集された情報から多分、私は何を使用するか、明確に追加しました。あなたが言ったことで推測できる限り、それはUDPでなければなりません。 (コメントでは入力できません。さようなら)しかし興味深いのは、常に同じ情報が欠落していることです。私にとって(パケットの損失は何だと思いますか)、情報の固定された種類ではなく、ランダムな情報を失うはずです。私はまだネットワーク通信を十分に理解していないと思います... – user1016675

+0

これはあなたの作成方法ソケット。私はこれがUDPだと思います。あなたの目的に合わせて、あなたのメッセージにJSONフォーマットを使って、TCPまたはHTTPを頼りにしてください。 –

+0

私はそれがTCPだと疑っています。 TServerSocketコンポーネントのように見えます。 –

2

Hmm ..私が考えているコンポーネントであれば、ClientReadイベントがデータでいっぱいの完全なメッセージで起動されるロックではありません。

この場合、メッセージの転送を許可するにはTCPの上にプロトコルが必要です。 TCP自体はメッセージを転送することはできず、バイトストリームのみを転送します。

TCustomWinSocketのドキュメント、(私のイタリック体):

「を使用ReceiveBuf WindowsソケットオブジェクトのOnSocketEventイベントのイベントハンドラ内またはソケットコンポーネントのOnReadまたはOnClientReadイベントハンドラでソケット接続から読み取るします。

ReceiveBufバイトの数が実際(コールに要求された数未満であってもよい)を読み取る返します。

バイトが読み取られない場合、ReceiveBufは-1を返します。

したがって、オーバーサイズバッファを宣言し、受信したデータの解析を避けるために常にロットを転送するという狡猾な計画は機能しませんでした。

正しく実行する必要があります。 ClientRead()イベントがメッセージ全体、部分的なメッセージ、結合されたメッセージ、または毎回1バイトの多くの時間だけ起動するかどうかにかかわらず動作するプロトコルを設計します。私の好みのデザインは、 'byteLoad(thischar:char):boolean;'で 'PDU'クラスを使用することです。メソッドで内部ステートマシンを使用してプロトコルを処理し、コアデータを使用してフィールドをロードし、完全なメッセージがアセンブルされたときにのみtrueを返します。すべてのClientReadコールで、すべての受信バイトを反復し、trueを返すまで 'byteLoad'を呼び出し、PDUを処理して(おそらくどこかでキューに入れて)、別のPDUを作成してロードを開始します。

非スレッド型の非TServerSocket選択肢を使用しているため、PDUインスタンスなどのクライアント固有のデータやClientRead呼び出しで保持する必要があるその他のデータを、TCustomWinSocketインスタンスのフィールドとして格納する必要があります。これは、このデータを保持するTCustomWinSocketの子孫か、ベースのTCustomWinSocketの 'data'プロパティを使用して別のオブジェクトまたはレコードを保持することを意味します。そのものは通常、OnConnectedイベントで作成/初期化/ロードされます。切断時のTCustomWinSocketの動作に注意してください - データフィールド内の無限のポインタを解放しようとするという面白い気持ちがあります。

関連する問題