Ошибка при чтении из сокета

Модератор: Модераторы

Сообщение Janus » 11.02.2006 20:31:23

Пытаюсь выполнить такую программу:
Код: Выделить всё
Program server;
{$mode Delphi}
{$SMARTLINK ON}
{$LONGSTRINGS ON}
uses Sockets, SysUtils;

const
 ServerPort = 4589;

function sfunc(sock: pointer): LongInt;
var
 buf      : string;
 sin, sout: Text;
begin
 writeln('Convert sock 2 text');
 Sock2Text(LongInt(sock), sin, sout);
 
 writeln('reset sin');
 Reset(sin);
 
 Writeln('rewrite sout');
 Rewrite(sout);
 
 writeln('Files opened');

 buf := '';
 repeat
   writeln('Check input');
   while not eof(sin) do
   begin
     writeln('Try ot read from socket');
     Readln(sin, buf); //******************ЗДЕСЬ ОШИБКА
   
     writeln('Check red data');
     if buf <> '' then
     begin
       writeln('Try to answer');
       Writeln(sout, '200 OK');
       
       writeln('Answered');
       writeln(buf);
     end
   end;

   write(#13#10'- Step -'#13#10);    
   Sleep(100);
 until buf = 'quit';
 
 Close(sin);
 Close(sout);
 CloseSocket(LongInt(sock));
 Writeln(#13#10'Connection closed');
 Result := 0;
end;
 
function ConnectionsManager: Longint;
var
 MainSocket, ClientSocket: Longint;
 sAddrSize               : LongInt;
 sAddr                   : TINetSockAddr;
begin
 ConnectionsManager := 0;
 writeln('Try to start connection manager');
 MainSocket := Socket(AF_INET, SOCK_STREAM, 0);
 if MainSocket = -1 then
 begin
   writeln('Error: Socket(AF_INET, SOCK_STREAM, 0)');
   halt(1);
 end;

 sAddr.Family := AF_INET;
 sAddr.Port   := htons(ServerPort);
 sAddr.Addr   := LongWord(StrToNetAddr('127.0.0.1'));

 if not Bind(MainSocket, sAddr, SizeOf(sAddr)) then
 begin
   writeln('Error: Bind(MainSocket, sAddr, SizeOf(sAddr))');
   halt(1);
 end;

 if not Listen(MainSocket, 2) then
 begin
   writeln('Error: Listen(MainSocket, x)');
   halt(1);
 end;

 repeat
   sAddrSize := SizeOf(sAddr);
   writeln('Waiting for connection');
   ClientSocket := Accept(MainSocket, sAddr, sAddrSize);
   if ClientSocket <> -1 then
   begin
     writeln('Connection!');
     BeginThread(@sfunc, pointer(ClientSocket));
   end;
 until false;

 Shutdown(MainSocket, 2);
 writeln('Close connection');
end;

Begin
 ConnectionsManager;
end.


Запускаю этот сервер, потом подключаюсь клиентом. В консоли следующее:
Код: Выделить всё
E:\My Projects\fpc\cross-sock>server
Try to start connection manager
Waiting for connection
Connection!
Waiting for connection
Convert sock 2 text
reset sin
rewrite sout
Files opened
Check input
Try ot read from socket
An unhandled exception occurred at $00403F2F :
EAccessViolation : Access violation
 $00403F2F
 $00402D1A
 $00402508
 $00405516
 $77E802ED
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection
Waiting for connection

E:\My Projects\fpc\cross-sock>

Из-за чего может быть такая ошибка и как ее исправить? Работаю в win32. В Линуксе пока не пробовал.
Janus
постоялец
 
Сообщения: 134
Зарегистрирован: 07.11.2005 17:06:49

Сообщение STAKANOV » 11.02.2006 20:42:28

Надо попробовать собрать с параметром -gl и посмотреть кто это выдает. Если одна из функций для работы с сокетами, то надо будет посмотреть номер ошибки (SocketError). В Windows значение совпадает с указанными например на <a href='http://www.sockets.com/err_lst1.htm' target='_blank'>http://www.sockets.com/err_lst1.htm</a>
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение Janus » 11.02.2006 21:36:07

Код: Выделить всё
 $00403F2F
 $00402D1A
 $00402508  SFUNC,  line 32 of server.pp
 $00405516
 $77E802ED line 1 of server.pp

Строка 32 - это как раз и есть Readln(sin, buf), отмеченная выше. Строка 1... Т.е. ошибка в функции Readln.

Самое интересно, что когда я не делал Rewrite и Reset, то ошибки не было. При этом, в первый раз читался какой-то бред, а потом ничего не читалось. А данные клиенту отправлялись вообще без проблем.
Janus
постоялец
 
Сообщения: 134
Зарегистрирован: 07.11.2005 17:06:49

Сообщение STAKANOV » 12.02.2006 01:40:31

Readln(sin, buf)

не уверен, что это корректно работает в Windows (хотя наверно должно), попробуй уйти от работы с сокетами как с файлами и работай с ними с помощью recv/send

да и sin качестве названия переменной я бы не рискнул использовать, мало ли что ... :rolleyes:

и значения SocketError посмотри
....
....
я тут подумал, а попробуй Read вместо Readln
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение mif » 12.02.2006 03:49:19

Не знаю мож ошибка в том, что Надо перед REadln добавить процедуру Assign.
mif
новенький
 
Сообщения: 71
Зарегистрирован: 14.12.2005 10:09:27

Сообщение Janus » 12.02.2006 18:06:42

Assign с чем? Имени файла нету.

С read таже проблема, что и с ReadLn. А вот с Recv и Send все работает прекрасно. Просто я хотел сделать работу поудобнее... Странно...

Кстати, попутный вопрос, если использовать Recv, как заставить его не ждать данных при пустом буфере, чтобы это работало не только в win32?
Janus
постоялец
 
Сообщения: 134
Зарегистрирован: 07.11.2005 17:06:49

Сообщение STAKANOV » 12.02.2006 19:52:48

С read таже проблема, что и с ReadLn. А вот с Recv и Send все работает прекрасно. Просто я хотел сделать работу поудобнее... Странно...

Ничего странного, socket в теории должен работать одинаково на всех платформах, на практктике глюков еще полно, похоже этот способ не очень популярен. Например, как я понял, библиотека lNet делает кроссплатформенность по другому, может даже лучше.

Кстати, попутный вопрос, если использовать Recv, как заставить его не ждать данных при пустом буфере, чтобы это работало не только в win32?

это неблокирующий режим называется :) по-моему как раз кроссплатформенного способа нет, т.е. стандартными средствами fpc
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение Гость_unC0Rr » 12.02.2006 21:39:56

Janus писал(а): Кстати, попутный вопрос, если использовать Recv, как заставить его не ждать данных при пустом буфере, чтобы это работало не только в win32?

select'ом проверять, есть ли данные на сокете... и recv вызывать только если есть
Гость_unC0Rr
 

Сообщение Janus » 14.02.2006 20:48:26

Каким select'ом? В мануале ничего про такую функцию. Под win32 я это делал с помощью IOCtlSocket, а как это реализовать в Линуксе?
Janus
постоялец
 
Сообщения: 134
Зарегистрирован: 07.11.2005 17:06:49

Сообщение STAKANOV » 14.02.2006 21:06:07

Под win32 я это делал с помощью IOCtlSocket, а как это реализовать в Линуксе?


в модуле BaseUnix функция FpFcntl
описания в Линуксе man fcntl
этой функцией для сокета можно установить неблокирующий режим
Прмерно так FpFcntl(MySocket, F_SETFL, O_NONBLOCK)

опеределены или нет две последующие константы в модулях fpc я не помню, в крайнем случае их всегда можно узнать - я лично для ассемблера специальную прогу использовал написанную на си, она значения констант выводила, т.е. состояла из сплошных printf :)

Файл генерирующий значения констант для GNU AS прилагается
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение Janus » 15.02.2006 17:13:26

А как это сделать в режиме кросс? Я пытаюсь переделать свою библиотеку из win-onli в кросс-пратформенную.
Janus
постоялец
 
Сообщения: 134
Зарегистрирован: 07.11.2005 17:06:49

Сообщение STAKANOV » 15.02.2006 18:34:39

А как это сделать в режиме кросс?

Если дело только во включении неблокирущего режима, то

Код: Выделить всё
{$IFDEF WIN32}
   IOCtlSocket(...
{$ELSE}
   FpFcntl(...
{$ENDIF}


Ну и в uses что-то в этом роде
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение Guest » 16.02.2006 12:20:19

А узнать, не пуст ли буфер?
Guest
 

Сообщение STAKANOV » 16.02.2006 16:29:56

А узнать, не пуст ли буфер?

это по-моему как раз select делает

но тут видно все по другому :rolleyes: т.е. если recv вернет 0, то значит оно ничего не прочитало и буфер был пустой

правда я так понял, что такой путь не особо используется
Аватара пользователя
STAKANOV
энтузиаст
 
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение Janus » 16.02.2006 20:00:20

А если я оставлю блокирующий режим и попытаюсь узнать, пуст ли буфер? В руководстве нет функции select.
Janus
постоялец
 
Сообщения: 134
Зарегистрирован: 07.11.2005 17:06:49


Вернуться в Сети

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Рейтинг@Mail.ru