DelphiXE7、Indy10の環境でソケット通信のプログラムを作りました。
IdTCPServerでクライアントからの要求を受け、
TIdContext(IdTCPServer1.Contexts.LockList[0]).Connection.IOHandler.WriteLn('OK');
で応答を返しました。
その応答を受けたクライアントはソケットを切断し、再度別の要求を出そうと
接続を行いましたが、サーバでConnectできませんでした。
サーバが応答を返さない場合は再接続できています。
応答の仕方が悪く、切断できなくなっているのでしょうか?
強制的に切断する方法とかありますでしょうか?
まずはWiresharkなどでパケットをキャプチャしてやりとりが正常かどうか確認してみてはいかがでしょう?
>再度別の要求を出そうと接続を行いましたが、サーバでConnectできませんでした。
クライアント側もコネクトできなかったという事でしょうか?
想像だけで実際の処理がつかみにくくなっていると思われますので
クライアント、サーバーそれぞれの重要イベント内で
ログを残すような処理を追加すれば解決に繋がりそうです。
>サーバが応答を返さない場合は再接続できています。
ということはクライアント側がクローズした後 再接続出来ていないということですかね?
サーバーが返した 'OK' をクライアント側で受信してないのでは?
>通りすがり様
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;
>通りすがり様
PCを2台使ってソケット通信を行ったところ、Wiresharkでパケットを取ることが
できました。
クライアント側から送信したデータ、サーバ側から送信されたデータ共に、
意図したデータ、バイト数になっていました。
やり取りは正常に行われていると思います。
サーバ側の応答部分をネット上にあるサンプルに置き換えたら
うまく動作するようになりました。今回はこれで行こうと思います。ですが、
・何故これでうまく動作するようになるのか。
・送信してきたクライアントだけに応答を返す方法。
この辺の事がわかる方がいましたら、教えて頂けますでしょうか。
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;
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更新のタイミングは内部を見てみないと分からないけど
クライアント側が接続を切断しても
少しの間残るようにしているのかも
>take様
何となく理由が分かったような感じがします。
ソケット通信は、うまくいっていると思ってもちょっとしたことで
最後にエラーになってしまうことがありますね。
扱い方が難しいです。
皆さま、ご回答ありがとうございました。
ツイート | ![]() |