Форум: "Начинающим";
Текущий архив: 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.005 c