16. januar 2007 - 23:20
Der er
3 kommentarer
serversocket fejler når klienter fjernes uden disconnect
Hey
Jeg har lavet et chat program baseret på borlands eksempel, det der ligger i Cbuilder6.
Det spiller også max. Problemet opstår når en af klienterne logger af uden at sige ”pænt” farvel til serven.
Eksempel vis hvis man lukker klienten fra proceslisten. Når serveren så skal sende ud til klienterne igen sender også til den klient som ikke længere findes, dette resultere at serveren går ned.
Jeg kan detektere at der er opstået en fejl. Men hvordan får jeg slettet forbindelsen til den klient som er blevet ”væk”??
Serveren kører ”stNonBloking”.
/Peter
17. januar 2007 - 17:00
#1
Når klienten lukker uden at sende et "farvel", så sker der det at dit receive loop begynder at kører uendeligt, fordi den bare modtager "\0" konstant, så derfor kan du f.eks. gøre således at hvis inputtet fra dit receive er == \0 så skal den udføre et closesocket(ID).
01. februar 2007 - 15:31
#2
Har været lidt væk fra PC men er tilbage nu.
Det du skriver, giver god mening men jeg kan ikke lige se hvordan det kan implementeres. Derfor lige lidt server kode.
Har prøvet at lave noget ligende dit forslag nede i MyServerClientError eventen.
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
TServerSocket *MyServer;
TStringList *UserName = new TStringList(); // Der oprettes en pointer der
//kaldes UserName.
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
MyServer = new TServerSocket(Form1);
MyServer->OnClientConnect = MyServerClientConnect;
MyServer->OnClientDisconnect = MyServerClientDisconnect;
MyServer->OnClientRead = MyServerClientRead;
MyServer->OnClientError = MyServerClientError;
ConnectedList = new TStringList();
//FileName = "log.txt"
}
//------------------------------------------------------------------------------
void __fastcall TForm1::StartButtonClick(TObject *Sender)
{
MyServer->Port = PortEdit->Text.ToIntDef(5200); // Sætter porten
Caption = MyServer->Port; // Sætter caption til port nr
MyServer->Active = true; // Aktivere server
StopButton->Enabled = true; // Gør Stopknappen synlig
StartButton->Enabled = false; // Gør Startknappen usynlig
PortEdit->Enabled = false; // Gør at ændring af port ikke er mulig
//LogMemo->Lines->LoadFromFile("log.txt"); // Loader log.txt ved start af server
}
//------------------------------------------------------------------------------
void __fastcall TForm1::StopButtonClick(TObject *Sender)
{
MyServer->Active = false; // Deaktivere server
StartButton->Enabled = true; // Gør Startknappen synlig
StopButton->Enabled = false; // Gør Stopknappen usynlig
PortEdit->Enabled = true; // Gør at det er muligt at ændre port
}
//------------------------------------------------------------------------------
void __fastcall TForm1::MyServerClientConnect(TObject *Sender,
TCustomWinSocket *Socket)
{
LogMemo->Lines->Add(Socket->RemoteAddress + " has connected.");
// Den skriver på skærmen hvem der har connected.
ConnectedList->AddObject(Socket->RemoteAddress, Socket);
// Opdaterer ConnectedList.
}
//------------------------------------------------------------------------------
void __fastcall TForm1::MyServerClientDisconnect(TObject *Sender,
TCustomWinSocket *Socket)
{
AnsiString bruger = Format("%s",OPENARRAY(TVarRec,(ConnectedList->Strings[ConnectedList->IndexOfObject(Socket)])));
int slet_socket = Memoadr->Lines->IndexOf(ConnectedList->IndexOfObject(Socket));
LogMemo->Lines->Add(Socket->RemoteAddress + " er logget af.");
SendMessage(Format("%s er logget af.",
OPENARRAY(TVarRec,
(ConnectedList->Strings[ConnectedList->IndexOfObject(Socket)]))), "Server");
// Den skriver på skærmen hvem der har Disconnected
ConnectedList->Delete(ConnectedList->IndexOfObject(Socket));
// Opdatere ConnectedList
int slet_bruger = UserMemo->Lines->IndexOf(bruger);
if(slet_bruger != -1) {
UserMemo->Lines->Delete(slet_bruger);
Memoadr->Lines->Delete(slet_socket);
Timer->Enabled = true;
}
}
//------------------------------------------------------------------------------
void __fastcall TForm1::SendMessage(AnsiString aMessage, AnsiString aForm)
{
AnsiString besked;
AnsiString error;
for(int i = 0;i<MyServer->Socket->ActiveConnections;i++)
{
if(aForm == "Server") {//Hvis serveren skal sende noget
MyServer->Socket->Connections[i]->SendText(
error = Format("%s", OPENARRAY(TVarRec,(aMessage))));
}
else { //hvis brugeren sender beskeden
MyServer->Socket->Connections[i]->SendText(
Format("%s sagde: %s", OPENARRAY(TVarRec,(aForm, aMessage))));
}
besked = Format("%s sagde: %s", OPENARRAY(TVarRec,(aForm, aMessage)));
LogMemo->Lines->Add(besked);
}
}
//------------------------------------------------------------------------------
void __fastcall TForm1::MyServerClientRead(TObject *Sender,
TCustomWinSocket *Socket)
{
try {
bool commando = false;
AnsiString TextIn, CurrentName;
int iIndex;
//ConnectedList->BeginUpdate();
TextIn = Socket->ReceiveText();
iIndex = ConnectedList->IndexOfObject(Socket);
if(TextIn == "\0") {
MyServer->Socket->Disconnect(iIndex);
}
if(iIndex == -1 ) // Hvis brugeren er disconnected
return;
if(TextIn.Pos("UserName=") == 1)
{
//Set User Name
UserName->Text = TextIn; // User Name oprettes
ConnectedList->Strings[iIndex] = UserName->Values["UserName"];
UserMemo->Lines->Add(UserName->Values["UserName"]);
Memoadr->Lines->Add(ConnectedList->IndexOfObject(Socket));
SendMessage(Format("%s er logget på.",
OPENARRAY(TVarRec,
(UserName->Values["UserName"]))),"Server");
// User name skrives på skærmen efterfulgt af has
// connected. User Name placeres i
SendUsers("");
commando = true;
}
if(TextIn.Pos("<specuser>") == 1) {
AnsiString Tekst = TextIn;
int spect = NULL;
spect = Tekst.SubString(Tekst.Pos("<specuser>")+10,Tekst.Pos("</specuser>")-11).ToInt();
//LogMemo->Lines->Add(spect);
AnsiString Texttilspec = Tekst.SubString(Tekst.Pos("</specuser>")+11,(Tekst.Length()-(Tekst.Pos("</specuser>")+10)));
CurrentName = ConnectedList->Strings[iIndex];
SendMessageSpec(Texttilspec, CurrentName, spect);
}
else
{
if(!commando) {
//Send Message
CurrentName = ConnectedList->Strings[iIndex];
SendMessage(TextIn, CurrentName);
Label->Caption = MyServer->Socket->ActiveConnections;
}
}
}// try
catch (...) {
MessageBox(0, "", "FEJL", MB_ICONINFORMATION|MB_OK);
}
//delete UserName;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::SendUsers(AnsiString disconnedusers)
{
AnsiString userlist = "";
int usersconneted = UserMemo->Lines->Count;
for (int i = 0; i < usersconneted; i++) {
if (i == 0) {
userlist = "";
}
userlist += "<user>";
userlist += UserMemo->Lines->operator [](i);
userlist += "</user>";
}
SendMessage(Format("UserList=%s ",OPENARRAY(TVarRec,(userlist))),"Server");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TimerTimer(TObject *Sender)
{
Timer->Enabled = false;
SendUsers("bruger");
}
//---------------------------------------------------------------------------
void __fastcall TForm1::SendMessageSpec(AnsiString aMessage, AnsiString aForm, int aResiver)
{
MyServer->Socket->Connections[aResiver]->SendText(
Format("%s sagde til dig: %s", OPENARRAY(TVarRec,(aForm, aMessage))));
}
//---------------------------------------------------------------------------
void __fastcall TForm1::MyServerClientError(TObject *Sender,
TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
AnsiString error = ErrorCode;
ErrorCode = 0;
int iIndex;
iIndex = ConnectedList->IndexOfObject(Socket);
AnsiString TextIn = Socket->ReceiveText();
if(TextIn == "\0") {
MyServer->Socket->Disconnect(iIndex);
}
}
//--------------------------------------------------------------------------
void __fastcall TForm1::ButtonClick(TObject *Sender)
{
Memo->Lines->Clear();
for (int i = 1; i <= ConnectedList->Count; i++) {
Memo->Lines->Add(ConnectedList->Strings[i-1]);
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Memo->Lines->Clear();
for (int i = 1; i <= MyServer->Socket->ActiveConnections; i++) {
String test = "false";
if (MyServer->Socket->Connections[i-1]->Connected == true)
test = "true";
Memo->Lines->Add(test);
}
}
//---------------------------------------------------------------------------
13. august 2008 - 14:26
#3
Altså, jeg har ikke så meget forstand på C++ igen, men med at lukke på \0 løste mit eget problem da jeg lavede noget lignende .. Men kan en timeout på en socket ikke være en løsning?
Men er det ikke forholdsvist simpelt? Hvor henne i koden modtager du inputtet? Når du modtager inputtet, tjekker du om inputtet er == '\0' .. Hvis sandt, så luk forbindelsen, ellers fortsæt :)