无法从 TIdTCPServer 发送文本到服务器挂起(不响应)的 TIdTCPClient ,它只是在尝试向客户端发送文本时冻结。
我最近开始使用Indy TIdTCPClient和TIdTCPServer,当从服务器表单发送和接收响应时,我的客户端应用程序运行良好, 然后问题是来自服务器,它不发送数据,当我尝试发送数据(如其文档中提供的Indy创建者)时,它只是冻结,然后停止响应(崩溃):(,奇怪的是服务器发回了Execute Event上的响应,而不是使用send函数发送数据,因此这是我使用的代码:
服务器执行事件:
void __fastcall TServerMain::IdTCPServer1Execute(TIdContext *AContext)
{
UnicodeString uMessage;
uMessage = AContext->Connection->IOHandler->ReadLn();
MessageDisplay1->Lines->Add(uMessage);
AContext->Connection->IOHandler->WriteLn("Response OK!"); // i can receive the response from the client
}
服务器发送功能:
void TServerMain::itsSendMessage(TIdTCPServer *itsName, UnicodeString uMessage) {
TIdContextList *Clients;
TIdContext *icContext;
if ( uMessage.Length() != 0 && itsName->Active ) {
Clients = itsName->Contexts->LockList();
for (int i = 0; i < Clients->Count; i++) {
icContext = (TIdContext*)Clients->Items[i];
icContext->Connection->IOHandler->WriteLn(uMessage);
}
itsName->Contexts->UnlockList();
}
} // this function doesn't send text to the clients however, it just hangs the application for ever.
附加说明: :即使客户端断开连接,TIdTCPServer仍会从OnExecute
事件中停止发送文本!
更新:
void __fastcall TMyContext::AddToQueue(TStream *AStream)
{
TStringList *queue = this->FQueue->Lock();
try {
queue->AddObject("", AStream);
this->FMessageInQueue = true;
}
__finally
{
this->FQueue->Unlock();
}
}
void __fastcall TMyContext::CheckQueue()
{
if ( !this->FMessageInQueue )
return;
std::unique_ptr<TStringList> temp(new TStringList);
TStringList *queue = this->FQueue->Lock();
try {
temp->OwnsObjects = true;
temp->Assign(queue);
queue->Clear();
this->FMessageInQueue = false;
}
__finally
{
this->FQueue->Unlock();
}
for (int i = 0; i < temp->Count; i++) {
this->Connection->IOHandler->Write( static_cast<TStream*>(temp->Objects[i]), static_cast<TStream*>(temp->Objects[i])->Size, true );
}
}
服务器发送功能:
void __fastcall TServerMain::IdSendMessage(TIdTCPServer *IdTCPServer, TStream *AStream)
{
if ( !IdTCPServer->Active )
return;
TIdContextList *Clients = IdTCPServer->Contexts->LockList();
try {
for (int i = 0; i < Clients->Count; i++) {
static_cast<TMyContext*>(static_cast<TIdContext*>(Clients->Items[i]))->AddToQueue(AStream);
}
}
__finally
{
IdTCPServer->Contexts->UnlockList();
}
}
客户接收功能:
void __fastcall TReadingThread::Receive() {
TMemoryStream * ms = new TMemoryStream();
this->IdTCPClient1->IOHandler->ReadStream(ms);
ms->Position = 0;
ClientMain->Image1->Picture->Bitmap->LoadFromStream(ms);
delete ms;
}
此功能是Synchronized
中的TThread
。
这就是我使用TBitmap
发送TMemoryStream
的方式:
void __fastcall TServerMain::CaptureDesktop()
{
// Capture Desktop Canvas
HDC hdcDesktop;
TBitmap *bmpCapture = new TBitmap();
TMemoryStream *Stream = new TMemoryStream();
try {
bmpCapture->Width = Screen->Width;
bmpCapture->Height = Screen->Height;
hdcDesktop = GetDC(GetDesktopWindow());
BitBlt(bmpCapture->Canvas->Handle, 0,0,Screen->Width, Screen->Height, hdcDesktop, 0,0, SRCCOPY);
bmpCapture->SaveToStream(Stream);
Stream->Position = 0;
IdSendMessage(IdTCPServer1, Stream);
}
__finally
{
ReleaseDC(GetDesktopWindow(), hdcDesktop);
delete bmpCapture;
delete Stream;
}
}
答案 0 :(得分:0)
TIdTCPServer
是一个多线程组件,您无需考虑其正确性。
服务器的各种事件是在内部工作线程的上下文中触发的,而不是在主UI线程的上下文中触发的。您的OnExecute
代码在访问MessageDisplay1
时未与主UI线程同步,这可能会导致各种问题,包括但不限于死锁。您必须与主UI线程同步,例如与TThread::Synchronize()
或TThread::Queue()
同步。例如:
void __fastcall TServerMain::IdTCPServer1Execute(TIdContext *AContext)
{
String Message = AContext->Connection->IOHandler->ReadLn();
// see http://docwiki.embarcadero.com/RADStudio/en/How_to_Handle_Delphi_Anonymous_Methods_in_C%2B%2B
TThread::Queue(nullptr, [](){ MessageDisplay1->Lines->Add(Message); });
AContext->Connection->IOHandler->WriteLn(_D("Response OK!"));
}
此外,您有2个彼此不同步的线程(无论哪个线程正在调用itsSendMessage()
和OnExecute
线程),因此它们可以潜在地编写文本同时发送给同一客户,彼此的文本重叠,从而破坏了您的通信。当从服务器向客户端发送未经请求的消息时,我通常(根据情况)建议您对消息进行排队,并让客户端线程的OnExecute
代码决定何时可以安全地发送队列。这样做的另一个原因是,如果一个客户端被阻止,而您又不想阻止对其他客户端的访问,则可以避免死锁。尽可能在客户端自己的OnExecute
事件中为每个客户端完成工作。例如:
class TMyContext : public TIdServerContext
{
private:
TIdThreadSafeStringList *FQueue;
bool FMsgInQueue;
public:
__fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = nullptr)
: TIdServerContext(AConnection, AYarn, AList)
{
FQueue = new TIdThreadSafeStringList;
}
__fastcall ~TMyContext()
{
delete FQueue;
}
void AddToQueue(const String &Message)
{
TStringList *queue = FQueue->Lock();
try
{
queue->Add(Message);
FMsgInQueue = true;
}
__finally
{
FQueue->Unlock();
}
}
void CheckQueue()
{
if (!FMsgInQueue)
return;
std::unique_ptr<TStringList> temp(new TStringList);
TStringList *queue = FQueue->Lock();
try
{
temp->Assign(queue);
queue->Clear();
FMsgInQueue = false;
}
__finally
{
FQueue->Unlock();
}
Connection->IOHandler->Write(temp.get());
}
bool HasPendingData()
{
TIdIOHandler *io = Connection->IOHandler;
bool empty = io->InputBufferIsEmpty();
if (empty)
{
io->CheckForDataOnSource(100);
io->CheckForDisconnect();
empty = io->InputBufferIsEmpty();
}
return !empty;
}
};
__fastcall TServerMain::TServerMain(...)
{
IdTCPServer1->ContextClass = __classid(TMyContext);
...
}
void __fastcall TServerMain::IdTCPServer1Execute(TIdContext *AContext)
{
TMyContext *ctx = static_cast<TMyContext*>(AContext);
ctx->CheckQueue();
if (!ctx->HasPendingData())
return;
String Message = AContext->Connection->IOHandler->ReadLn();
TThread::Queue(nullptr, [](){ MessageDisplay1->Lines->Add(Message); });
AContext->Connection->IOHandler->WriteLn(_D("Response OK!"));
}
void TServerMain::itsSendMessage(TIdTCPServer *itsName, const String &Message)
{
if ( Message.IsEmpty() || !itsName->Active )
return;
TIdContextList *Clients = itsName->Contexts->LockList();
try
{
for (int i = 0; i < Clients->Count; ++i)
{
static_cast<TMyContext*>(static_cast<TIdContext*>(Clients->Items[i]))->AddToQueue(Message);
}
}
__finally
{
itsName->Contexts->UnlockList();
}
}