Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Сети > TClientSocket, TServerSocket


Автор: Dayana 21.10.2002, 03:18
Пишу клиент-серверное приложение. Опыта в этом нет. Серверная часть выдает ошибку на методе чтения, когда клиент посылает серверу данные.
Ошибка:
Read Error 59: AN unexpected network error occurred;

По ходу вопрос: свойство Service классов TClienSocket и TServerSocket, что должно содержать в моем случае?

вот код:
Клиент:

var
 i : byte;
 ClientSocket1: TClientSocket;
 StreamSocket  : TWinSocketStream;
 CN : array[0..10] of char;
 LenCN: integer;
 AddCN: Pointer;
 strCN : string;

begin
 ClientSocket1 := TClientSocket.Create(nil);
 if ClientSocket1.Active then
   ClientSocket1.Close;
 ClientSocket1.Host := '10.10.100.24';
 ClientSocket1.Address := '10.10.100.24';
 ClientSocket1.Port := 7770;
 ClientSocket1.ClientType := ctBlocking;
 ClientSocket1.Open;
 Sleep(400);
 strCN := 'CompName';
 FillChar(CN, 11, 0);
 StrPCopy(CN, strCN);
 StreamSocket := TWinSocketStream.Create(ClientSocket1.Socket, 60000);
 StreamSocket.Write(CN, Length(CN));
 Sleep(1000);
 StreamSocket.Free;
 ClientSocket1.Socket.Close;
end.

Сервер:

var
 ServerSocket1: TServerSocket;
 StreamSocket  : TWinSocketStream;
 Buffer : array[0..10] of char;
 sRcvMsg : string;
 T : TDateTime;
 TimeInt : extended;
 LenMsg : integer;
begin
 sRcvMsg := 'Test Passed';
 ServerSocket1 := TServerSocket.Create(nil);
 if ServerSocket1.Active then
   ServerSocket1.Close;
 ServerSocket1.Port := 7770;
 ServerSocket1.ServerType := stThreadBlocking;
 ServerSocket1.ThreadCacheSize := 4;
 ServerSocket1.Socket.Listen('Имя сервера', '10.10.100.24', '', 7770, 4);
 T := Now;
 FillChar(Buffer, 11, 0);
 StreamSocket := TWinSocketStream.Create(ServerSocket1.Socket, 10000);
 repeat
   if not StreamSocket.WaitForData(10000) then
   begin
     Application.ProcessMessages;
     SaveToFile(FloatToStr((Now-T)*SecsPerDay));
   end
   else
   begin
     LenMsg := StreamSocket.Read(Buffer, 11);
     if LenMsg <> 0 then
     begin
       sRcvMsg := StrPas(Buffer);
       SaveToFile(sRcvMsg);
       Break;
     end
     else
       SaveToFile('Nothing read');
   end;
 until (Now - T)*SecsPerDay > 240;
 SaveToFile(sRcvMsg);
 TimeInt := (Now - T)*SecsPerDay;
 SaveToFile(FloatToStr(TimeInt));
 if ServerSocket1.Active then
   ServerSocket1.Close;

Заранее благодарю за ответ!

Автор: Fantasist 21.10.2002, 04:01
Прежде чем общаться, надо дождаться соеденения. Посмотри для сервера событие OnGetThread - его тебе нужно обработать. Для клиента тоже неплохо бы дождаться соеденения, а потом уж посылать.

Автор: Fantasist 21.10.2002, 04:39
Да, позволь пару реккомендаций.
Создай какой-нибудь класс типа сесии TSession - который будет описывать конкретное соеденение.
По возможности его лучше сделать максимально абстрактным - включить в него метод отсылки данных, событие сигнализирующие о приеме данных.
Я лично создавал в таком классе два TMemoryStream: StreamIn и StreamOut, виртуальный метод DataAccepted - вызывается когда принимается очередная порция данных, а также переменную типа TCustomWinSocket - значение которой задавалось при устонавлении соеденения и через нее сессия и обменивалась данными.
 Над таким классом удобнее определять наследников типа TClientSession и TServerSession - для описания твоего протокола.

Я помниться тоже начинал как-то без опыта клиент-сервера писать, со своим протоколом. Все оказалось не так просто, как казалось в начале. Одним из неприятных моментов было как раз то, что VCL-ные сокеты дают мало пространства для абстракции  плюс к этому  возникают в них какие-то непонятные глюки. Возможно лучше воспользоваться PowerSock от NetMasters - он такой более универсальный. Или TCPServer и TCPClient от Indy - в них никакой универсальности, но на вид гораздо более удобны.

Автор: Dayana 21.10.2002, 18:27
Fantasist, спасибо большое за советы!

Автор: Dayana 21.10.2002, 21:28
Когда я устанавливаю ClientType и ServerType = ctNonBlocking, то клиент соединяется с сервером и сервер реагирует на событие OnClientConnect. Если же ставлю ClientType = ctBlocking и ServerType = ctThreadBlocking, то соединения не происходит. В чем проблема? Какая разница для самого соединения?

Автор: Fantasist 22.10.2002, 02:17
А ты уверена, что соеденения не происходит? Я клиента никогда блокирующим не делал - у меня блокирующий только сервер. В последний раз я, правда, так сделал, когда понял, что без блокирующего сервера не обойтись, а клиента переделывать не стал.
 Так вот разница есть, и есть она именно на уровне сокетов Windows. Для сервера VCL-ный сокет с ctThreadBlocking создает отдельный поток для каждого соеденения, но перед этим генерирует событие OnGetThread, в котором тебе и нужно подставить свой поток.  Правда соеденение в любом случае, по идее, должно устанавливаться. Попробу клиента сделать не блокирующим.

Автор: Serg 25.10.2002, 18:39
Для примера можешь исходник борландовского сокета пощупать. Там все как на лодони. Но все что ты городила до этого можно немного упростить.
Сделай вот такую вещь:

На сервере для блокированного сокета:

procedure TForm1.ClientEvent(Sender: TObject; Socket: TCustomWinSocket;
 SocketEvent: TSocketEvent);
begin
 with ServerSocket1.Socket do begin
   case SocketEvent of
     seAccept,
     seLookup,
     seConnecting,
     seListen:
       begin end;
     seConnect: if Assigned(OnClientConnect) then OnClientConnect(Sender, Socket);
     seDisconnect: if Assigned(OnClientDisconnect) then OnClientDisconnect(Sender, Socket);
     seRead: if Assigned(OnClientRead) then OnClientRead(Sender, Socket);
     seWrite: if Assigned(OnClientWrite) then OnClientWrite(Sender, Socket);
   end;
 end;
end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
 Socket: TCustomWinSocket);
begin
 MessageBeep(0);
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
 ServerSocket1.Socket.OnSocketEvent := ClientEvent;
 ServerSocket1.Active := true;
end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
 Socket: TCustomWinSocket);
begin
 MessageBox(0,PChar(Socket.ReceiveText),'!!!', MB_OK);
end;

А это на клиенте:

procedure TForm1.Button1Click(Sender: TObject);
begin
 ClientSocket1.Socket.SendText('123456789');
end;

Работает изумительно, ничего дополнительно лепить не нужно.
Единственное неудобство- OnClientConnect не работает. Либо же включи в свой проект явно модуль ScktComp(скопируй в свою директорию) и замени
вот это

constructor TServerClientWinSocket.Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);
begin
 FServerWinSocket := ServerWinSocket;
 if Assigned(FServerWinSocket) then
 begin
   FServerWinSocket.AddClient(Self);
   if FServerWinSocket.AsyncStyles <> [] then begin
     OnSocketEvent := FServerWinSocket.ClientEvent;
     OnErrorEvent := FServerWinSocket.ClientError;
   end
   else
     with FServerWinSocket do begin
       OnSocketEvent := ClientEvent;
       OnErrorEvent := ClientError;
     end;
 end;
 inherited Create(Socket);
 if FServerWinSocket.ASyncStyles <> [] then begin
   DoSetAsyncStyles;
   if FConnected then Event(Self, seConnect);
 end
 else
   if FConnected then FServerWinSocket.Event(Self, seConnect);
end;

Дальше пользуй просто обработчики событий.

procedure TForm1.FormActivate(Sender: TObject);
begin
 ServerSocket1.Socket.OnSocketEvent := ClientEvent;
^^^^^^^^^^
Это не понадобиться

 ServerSocket1.Active := true;
end;

Автор: Fantasist 26.10.2002, 02:07
То что написал Serg - это годиться для НЕ блокирующего сокета. Но для него схему можно и получше придумать, но даже с хорошой схемой, не блокирующий сервер не способен обслуживать множество соеденений.

Автор: Serg 26.10.2002, 23:31
Могу спорить хоть на ящик шампанского, что работает. :)
Это действительно работало бы только для неблокируемых сокетов в стандартной реализации. Но, если ты внимательно читал сообщение, я предлагаю сделать некоторые изменения в исходнике сокета - поправить ScktComp, заменить реализацию конструктора TServerClientWinSocket
вот оригинальная редакция

constructor TServerClientWinSocket.Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);
begin
 FServerWinSocket := ServerWinSocket;
 if Assigned(FServerWinSocket) then
 begin
   FServerWinSocket.AddClient(Self);
   if FServerWinSocket.AsyncStyles <> [] then
   begin
     OnSocketEvent := FServerWinSocket.ClientEvent;
     OnErrorEvent := FServerWinSocket.ClientError;
   end;
 end;
 inherited Create(Socket);
 if FServerWinSocket.ASyncStyles <> [] then DoSetAsyncStyles;
 if FConnected then Event(Self, seConnect);
end;

А это то чем необходимо заменить

constructor TServerClientWinSocket.Create(Socket: TSocket; ServerWinSocket: TServerWinSocket);
begin
FServerWinSocket := ServerWinSocket;
if Assigned(FServerWinSocket) then
begin
  FServerWinSocket.AddClient(Self);
  if FServerWinSocket.AsyncStyles <> [] then begin
    OnSocketEvent := FServerWinSocket.ClientEvent;
    OnErrorEvent := FServerWinSocket.ClientError;
  end
  else
    with FServerWinSocket do begin
      OnSocketEvent := ClientEvent;
      OnErrorEvent := ClientError;
    end;
end;
inherited Create(Socket);
if FServerWinSocket.ASyncStyles <> [] then begin
  DoSetAsyncStyles;
  if FConnected then Event(Self, seConnect);
end
else
  if FConnected then FServerWinSocket.Event(Self, seConnect);
end;

Заметь, разница существует.
Не буду распространяться на тему синхронизаций и иных мелочей.
Просто попробуй сделать как я посоветовал. Все работает.

Автор: Fantasist 27.10.2002, 02:54
Спасибо, но у меня и так все работает  :)
А поповоду неблокирующего сервера - он у меня тоже работал, для многих подключений пока надо было только принять запрос и отправить ответ. Ка только понадобилось вести, так сказать, диалог с клиентом, тут то он и обламался в сфере приема информации.

Всместо модератора:

Старайтесь заключать листинги в теги [CODE] [/CODE], для этого есть удобная кнопочка наверху.

Автор: Dayana 28.10.2002, 01:27
У меня сейчас все работает с неблокирующим сокетом. А с блокирующим еще пробовала...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)