Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2009.08.30;
Скачать: [xml.tar.bz2];

Вниз

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

 
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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.49 MB
Время: 0.006 c
15-1246470296
@!!ex
2009-07-01 21:44
2009.08.30
Сколько стоит интернет?


15-1246568526
Kostafey
2009-07-03 01:02
2009.08.30
Наклепал пару заметок про EMaxima


15-1246307568
Юрий
2009-06-30 00:32
2009.08.30
С днем рождения ! 30 июня 2009 вторник


15-1245082595
DillerXX
2009-06-15 20:16
2009.08.30
Советский усилитель


9-1182001225
Li-ion
2007-06-16 17:40
2009.08.30
Насчет графики





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский