IdTCPでソケット通信をして再接続

解決


たかのん  2016-03-05 04:27:37  No: 48047

DelphiXE7、Indy10の環境でソケット通信のプログラムを作りました。
IdTCPServerでクライアントからの要求を受け、
TIdContext(IdTCPServer1.Contexts.LockList[0]).Connection.IOHandler.WriteLn('OK');
で応答を返しました。
その応答を受けたクライアントはソケットを切断し、再度別の要求を出そうと
接続を行いましたが、サーバでConnectできませんでした。
サーバが応答を返さない場合は再接続できています。
応答の仕方が悪く、切断できなくなっているのでしょうか?
強制的に切断する方法とかありますでしょうか?


通りすがり  2016-03-05 05:14:55  No: 48048

まずはWiresharkなどでパケットをキャプチャしてやりとりが正常かどうか確認してみてはいかがでしょう?


take  2016-03-07 17:48:32  No: 48049

>再度別の要求を出そうと接続を行いましたが、サーバでConnectできませんでした。

クライアント側もコネクトできなかったという事でしょうか?

想像だけで実際の処理がつかみにくくなっていると思われますので
クライアント、サーバーそれぞれの重要イベント内で
ログを残すような処理を追加すれば解決に繋がりそうです。

>サーバが応答を返さない場合は再接続できています。
ということはクライアント側がクローズした後  再接続出来ていないということですかね?


KHE00221  2016-03-08 15:40:16  No: 48050

サーバーが返した 'OK' をクライアント側で受信してないのでは?


たかのん  2016-03-11 02:09:12  No: 48051

>通りすがり様
Wiresharkを使用してみましたが、PC内のアプリケーション間に対しての
プロセス通信のためか、送信したと思われるパケットが見つかりませんでした。
使い方が間違っていたのでしょうか。

>KHE00221様
サーバ側が返した'OK'はクライアント側で受信できていることは確認しています。

>take様
IdTCPClient1.Connectedはtrueを返していますが、データを送信してもサーバ側の
IdTCPServer1Execute()に来ていないことまでは確認しています。
以下のようなプログラムで接続をしています。

・クライアント側の接続処理
IdTCPClient1.Host := 'XXX.XXX.XXX.XXX';
IdTCPClient1.Port := XXXX;
IdTCPClient1.Connect;
if (IdTCPClient1.Connected = false) then begin
    MessageDlg('サーバに接続できません。', mtError, [mbOK], 0);
    Exit;
end;

・クライアント側の切断処理
if (IdTCPClient1.Connected = true) then begin
    if (IdTCPClient1.IOHandler <> nil) then begin
        if (IdTCPClient1.IOHandler.InputBuffer <> nil) then begin
            if (IdTCPClient1.IOHandler.InputBuffer.Size > 0) then begin
                IdTCPClient1.IOHandler.InputBuffer.Clear;
            end;
        end;
    end;
    IdTCPClient1.Disconnect;
    IdTCPClient1.IOHandler.Free;
end;

・サーバ側の受信→応答
procedure IdTCPServer1Execute(AContext: TIdContext);
begin
    sBuf := AContext.Connection.IOHandler.ReadLn();
    サーバ側で処理を実行
    TIdContext(IdTCPServer1.Contexts.LockList[0]).Connection.IOHandler.WriteLn('OK');
end;


たかのん  2016-03-11 03:32:15  No: 48052

>通りすがり様
PCを2台使ってソケット通信を行ったところ、Wiresharkでパケットを取ることが
できました。
クライアント側から送信したデータ、サーバ側から送信されたデータ共に、
意図したデータ、バイト数になっていました。
やり取りは正常に行われていると思います。


たかのん  2016-03-11 04:21:24  No: 48053

サーバ側の応答部分をネット上にあるサンプルに置き換えたら
うまく動作するようになりました。今回はこれで行こうと思います。ですが、
・何故これでうまく動作するようになるのか。
・送信してきたクライアントだけに応答を返す方法。
この辺の事がわかる方がいましたら、教えて頂けますでしょうか。

procedure IdTCPServer1Execute(AContext: TIdContext);
var
    sBuf : String;
    List : TList;
    i : Integer;
begin
    sBuf := AContext.Connection.IOHandler.ReadLn();
    サーバ側で処理を実行
    List := IdTCPServer1.Contexts.LockList;
    try
        for i := 0 to List.Count -1 do begin
            TIdContext(List.Items[i]).Connection.IOHandler.Writeln('OK');
        end;
    finally
        IdTCPServer1.Contexts.UnlockList;
    end;
end;


take  2016-03-11 17:49:54  No: 48054

Indyをあまり使ったことがないのですが
とにかくサーバー側が複数のクライアントを相手に
正常な通信を行える仕組みを提供していています。

サンプルに置き換えたということですが

 List := IdTCPServer1.Contexts.LockList;
   接続中のクライアント情報をTIdContextクラスのリスト型で提供するとともに
   処理中にクライアント情報が増減しないように固定
 
 IdTCPServer1.Contexts.UnlockList;
  LockListで固定したのを開放

で問題の部分
procedure IdTCPServer1Execute(AContext: TIdContext);

何らかのイベントが発生したとき?の受信イベントで
引数は  送信してきたクライアント情報を管理しているクラス

なので

sBuf := AContext.Connection.IOHandler.ReadLn();

で、クライアントから受信ができる。

だったらそのクライアントに対して返信する場合は

AContext.Connection.IOHandler.Writeln('OK');

になるのでは?

どうして
    TIdContext(IdTCPServer1.Contexts.LockList[0]).Connection.IOHandler.WriteLn('OK');

だと1回目しか正常に動かないかというのは

【1回目】
サーバー  ←  クライアント接続
サーバーがLockList[0]  にクライアント情報を確保
LockList[0]に対して返信

【2回目】
サーバー  ←  クライアント接続
サーバーがLockList[1]  にクライアント情報を確保
LockList[0]に対して返信←NG

LockList更新のタイミングは内部を見てみないと分からないけど
クライアント側が接続を切断しても
少しの間残るようにしているのかも


たかのん  2016-03-12 06:23:31  No: 48055

>take様
何となく理由が分かったような感じがします。
ソケット通信は、うまくいっていると思ってもちょっとしたことで
最後にエラーになってしまうことがありますね。
扱い方が難しいです。

皆さま、ご回答ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加