Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2009.08.30;
Скачать: CL | DM;

Вниз

Блокирующие сокеты   Найти похожие ветки 

 
Michael   (2009-06-28 15:21) [0]

Доброго времени суток!
Вроде бы детская проблема, а решить не получается. Поиск по Мастерам и КС королевства решить проблему не смог.
Пытаюсь разобраться в работе блокирующих сокетов. Пишу, основываясь на материалах статьи Антона Григорьева «Использование сокетов в Delphi».
Код выложу полностью, дабы не упустить чего важного. Комментарии мои – как я понимаю работу.
Сервер. Расположены 5 кнопок, эдит для передавамой строки и мемо для отчета.

unit f_Main;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, Winsock;

type
 TForm1 = class(TForm)
   Memo1: TMemo;
   bt_Open: TButton;
   bt_Connect: TButton;
   bt_Close: TButton;
   bt_Write: TButton;
   Edit1: TEdit;
   bt_Read: TButton;
   procedure bt_OpenClick(Sender: TObject);
   procedure bt_ConnectClick(Sender: TObject);
   procedure bt_CloseClick(Sender: TObject);
   procedure bt_WriteClick(Sender: TObject);
   procedure bt_ReadClick(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;
 // буфер для чтения/записи
 TByteBuf  = array[$0000..$FFFF] of BYTE;
 TpByteBuf = ^TByteBuf;

var
 Form1: TForm1;
 S,AcceptedSock:TSocket;
 Addr:TSockAddr;
 Data:TWSAData;
 Len:Integer;
 pByteBuf : TpByteBuf;
implementation

{$R *.dfm}

// Открытие серверного сокета
procedure TForm1.bt_OpenClick(Sender: TObject);
begin
 // Инициализируем бибилотеку Winsock
 if  WSAStartup($101,Data) <> 0 then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при инициализации библиотеки");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Библиотека инициализирована");
 end;
 // Создаем серверный (слушающий) сокет
 S:=Socket(AF_Inet,Sock_Stream,0);
 // Проверяем корректность создания
 if S = INVALID_SOCKET then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при создании серверного сокета");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Серверный сокет создан");
 end;
 // Заполнение структуры адреса
 Addr.sin_family:=PF_Inet;
 Addr.sin_port:=HToNS(3030);
//  Addr.sin_addr.S_addr:=InAddr_Any;
 Addr.sin_addr.S_addr:=Inet_Addr("127.0.0.1");
 FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
 // Связываем серверный сокет с адресом
 if Bind(S,Addr,SizeOf(TSockAddr)) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при связывании сокета с адресом");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Серверный сокет связан с адресом");
 end;

end;

// Подключение клиента
procedure TForm1.bt_ConnectClick(Sender: TObject);
begin
 // Ожадание подключения клиента
 Memo1.Lines.Add(TimeToStr(Now)+"-> Начало ожидания подключения клиента...");
 if Listen(S,SoMaxConn) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при подключении клиента");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Клиент подключен");
 end;
 Len:=SizeOf(TSockAddr);
 // Создание клиентского сокета
 AcceptedSock:=Accept(S,@Addr,@Len);
 if AcceptedSock = INVALID_SOCKET then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при создании клиентского сокета");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Клиентский сокет создан");
 end;
end;

// Закрытие сокетов
procedure TForm1.bt_CloseClick(Sender: TObject);
begin
 // Закрытие соединения
 if ShutDown(AcceptedSock, SD_Both) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при закрытии соединения");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Соединение закрыто");
 end;
 // Закрытие сокетов
 if CloseSocket(AcceptedSock) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при закрытии клиентского сокета");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Клиентский сокет закрыт");
 end;
 if CloseSocket(S) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при закрытии серверного сокета");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Серверный сокет закрыт");
 end;
end;

// Передача строки
procedure TForm1.bt_WriteClick(Sender: TObject);
var
 iRet : Integer;
 iCnt, iLength : Integer;
begin
 // Выделяем память под буффер
 GetMem(pByteBuf, SizeOf(TByteBuf));
 iLength := Length(Edit1.Text);
 // Заполняем буфер содержимым Edit1
 for iCnt := 0 to (iLength-1) do
   pByteBuf^[iCnt] := ord(Edit1.Text[iCnt+1]);
 // Передаем буфер
 iRet := Send(AcceptedSock, pByteBuf, Length(Edit1.Text), 0);
 Memo1.Lines.Add(TimeToStr(Now)+"-> Записано " + IntToStr (iRet) + " Байт");
 // Осовбождаем буфер
 FreeMem(pByteBuf, SizeOf(TByteBuf));
end;

// Чтение строки
procedure TForm1.bt_ReadClick(Sender: TObject);
var
 iRet : Integer;
 iCnt : Integer;
 str : string;
begin
 // Выделяем память под буффер
 GetMem(pByteBuf, SizeOf(TByteBuf));
 // Принимаем данные
 iRet := Recv(AcceptedSock, pByteBuf, SizeOf(TByteBuf), 0);
 Memo1.Lines.Add(TimeToStr(Now)+"-> Принято " + IntToStr (iRet) + "Байт");
 if iRet > 0 then begin
   // Формируем строку
   Str := "";
   for iCnt := 0 to iRet-1 do str := Str + chr(pByteBuf^[iCnt]);
 end;
 Memo1.Lines.Add("Принято: " + str);
//  FreeMem(pByteBuf, SizeOf(TByteBuf));
end;

Вопросы следующие:
1. В процедурах чтения при освобождении памяти для буфера вылетает ошибка типа “Invalid pointer operation”. Почему? Разве буфер еще используется?
2. Главное – отчеты в Memo показывают, что передается и принимается корректное количество байт. А вот отображается какая-то ерунда. Чую, где-то детскую ошибку допустил, а вот где – не вижу.
3. Почему у сервера, функция Listen не блокирует работу до подключения клиента, а сразу успешно «проваливается» дальше?

Спасибо.


 
Michael   (2009-06-28 15:22) [1]

Клиент:

// открытие сокета
procedure TForm1.bt_OpenClick(Sender: TObject);
begin
 // Инициализируем бибилотеку Winsock
 if  WSAStartup($101,Data) <> 0 then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при иниицализации библиотеки");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Библиотека инициализирована");
 end;
 // Создаем серверный (слушающий) сокет
 S:=Socket(AF_Inet,Sock_Stream,0);
 // Проверяем корректность создания
 if S = INVALID_SOCKET then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при создании клиентского сокета");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Клиентский сокет создан");
 end;
 // Заполнение структуры адреса
 Addr.sin_family:=PF_Inet;
 Addr.sin_port:=HToNS(3030);
//  Addr.sin_addr.S_addr:=InAddr_Any;
 Addr.sin_addr.S_addr:=Inet_Addr("127.0.0.1");
 FillChar(Addr.Sin_Zero,SizeOf(Addr.Sin_Zero),0);
end;

// Подсоединение к серверу
procedure TForm1.bt_ConnectClick(Sender: TObject);
begin
 Memo1.Lines.Add(TimeToStr(Now)+"-> Начало подключения клиента...");
 if Connect(S,Addr,SizeOf(TSockAddr)) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при подключении клиента");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Клиент подключен");
 end;
end;

// закрытие сокета
procedure TForm1.bt_CloseClick(Sender: TObject);
begin
 // Закрытие соединения
 if ShutDown(S, SD_Both) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при закрытии соединения");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Соединение закрыто");
 end;
 // Закрытие сокетов
 if CloseSocket(S) = SOCKET_ERROR then begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Ошибка при закрытии клиентского сокета");
   Exit;
 end
 else begin
   Memo1.Lines.Add(TimeToStr(Now)+"-> Клиентский сокет закрыт");
 end;
end;

// Передача строки
procedure TForm1.bt_WriteClick(Sender: TObject);
var
 iRet : Integer;
 iCnt, iLength : Integer;
begin
 // Выделяем память под буфер
 GetMem(pByteBuf, SizeOf(TByteBuf));
 iLength := Length(Edit1.Text);
 // заполняем буфер содержимым Edit1
 for iCnt := 0 to (iLength-1) do
   pByteBuf^[iCnt] := ord(Edit1.Text[iCnt+1]);
 // Передаем
 iRet := Send(S, pByteBuf, Length(Edit1.Text), 0);
 Memo1.Lines.Add(TimeToStr(Now)+"-> Записано " + IntToStr (iRet) + "Байт");
 // Освобождаем память
 FreeMem(pByteBuf, SizeOf(TByteBuf));
end;

// Получение данных
procedure TForm1.bt_ReadClick(Sender: TObject);
var
 iRet : Integer;
 iCnt : Integer;
 str : string;
begin
 GetMem(pByteBuf, SizeOf(TByteBuf));
 iRet := Recv(S, pByteBuf, SizeOf(TByteBuf), 0);
 Memo1.Lines.Add(TimeToStr(Now)+"-> Принято " + IntToStr (iRet) + "Байт");
 Str := "";
 if iRet>0 then begin
   for iCnt := 0 to iRet-1 do str := Str + chr(pByteBuf^[iCnt])+ " ";
   Memo1.Lines.Add("Принято: " + str);
 end;
//  FreeMem(pByteBuf, SizeOf(TByteBuf));
end;


 
Сергей М. ©   (2009-06-28 19:54) [2]


> 1. В процедурах чтения при освобождении памяти для буфера
> вылетает ошибка типа “Invalid pointer operation”. Почему?
>


Потому что содержимое переменной pByteBuf, содержащей адрес буфера, испорчено тобой.

Испорчено здесь:


iRet := Send(S, pByteBuf, Length(Edit1.Text), 0);


либо здесь:


iRet := Recv(S, pByteBuf, SizeOf(TByteBuf), 0);


Правильным же будет

iRet := Send(S, pByteBuf^, Length(Edit1.Text), 0);


и


iRet := Recv(S, pByteBuf^, SizeOf(TByteBuf), 0);


т.е. "детская" ошибка (см. следующий п.2) заключается в отсутствии разыменования (^) указательной переменной, которая которая передана фактическим параметром, но которая в формальных параметрах этих ф-ций объявлена как передаваемая по ссылке (а не по значению).


> 3. Почему у сервера, функция Listen не блокирует работу
> до подключения клиента


Ожиданием подключения занимается ф-ция Accept() , а не Listen()
Ф-ция Listen() всего лишь переводит гнедо в режим "прослушивания", без которого работа TCP-сервера невозможна.


 
Michael   (2009-06-29 22:55) [3]

Сергей М., большое спасибо: и разъяснили, и ошибки нашли! Искренняя благодарность! А то на мастерах последнее время проявляется неприятная тенденция флудить и оффтопить вместо помощи. Еще раз спасибо.


 
sniknik ©   (2009-06-29 23:05) [4]

> А то на мастерах последнее время проявляется неприятная тенденция флудить и оффтопить вместо помощи.
и тебе спасибо, что поддержал традицию! и начал флудить и офтопить о флуде и и оффтопе вместо просто благодарности...


 
brother ©   (2009-06-30 11:38) [5]

> и тебе спасибо, что поддержал традицию! и начал флудить
> и офтопить о флуде и и оффтопе вместо просто благодарности...

хотите об этом поговорить? ;)



Страницы: 1 вся ветка

Текущий архив: 2009.08.30;
Скачать: CL | DM;

Наверх




Память: 0.5 MB
Время: 0.014 c
1-1213595605
ganda
2008-06-16 09:53
2009.08.30
DevEpress Grid + фильтрация


2-1246216548
Pasha
2009-06-28 23:15
2009.08.30
Обращение к ячейкам в БД


15-1245963387
Германн
2009-06-26 00:56
2009.08.30
Старикам, помнящим свою молодость.


6-1205991809
mrfreeman2007
2008-03-20 08:43
2009.08.30
Счетчик посещений


15-1246212338
vuk
2009-06-28 22:05
2009.08.30
Москва. Печать на цветном лазернике. Где?