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

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

Ответить
Janus
постоялец
Сообщения: 134
Зарегистрирован: 07.11.2005 16:06:49

Сообщение Janus »

Пытаюсь выполнить такую программу:

Код: Выделить всё

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. В Линуксе пока не пробовал.
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

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

Сообщение Janus »

Код: Выделить всё

  $00403F2F
  $00402D1A
  $00402508  SFUNC,  line 32 of server.pp
  $00405516
  $77E802ED line 1 of server.pp

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

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

Сообщение STAKANOV »

Readln(sin, buf)

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

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

и значения SocketError посмотри
....
....
я тут подумал, а попробуй Read вместо Readln
mif
новенький
Сообщения: 71
Зарегистрирован: 14.12.2005 09:09:27

Сообщение mif »

Не знаю мож ошибка в том, что Надо перед REadln добавить процедуру Assign.
Janus
постоялец
Сообщения: 134
Зарегистрирован: 07.11.2005 16:06:49

Сообщение Janus »

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

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

Кстати, попутный вопрос, если использовать Recv, как заставить его не ждать данных при пустом буфере, чтобы это работало не только в win32?
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

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

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

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

это неблокирующий режим называется :) по-моему как раз кроссплатформенного способа нет, т.е. стандартными средствами fpc
Гость_unC0Rr

Сообщение Гость_unC0Rr »

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

select'ом проверять, есть ли данные на сокете... и recv вызывать только если есть
Janus
постоялец
Сообщения: 134
Зарегистрирован: 07.11.2005 16:06:49

Сообщение Janus »

Каким select'ом? В мануале ничего про такую функцию. Под win32 я это делал с помощью IOCtlSocket, а как это реализовать в Линуксе?
Аватара пользователя
STAKANOV
энтузиаст
Сообщения: 1069
Зарегистрирован: 14.05.2006 21:26:24
Откуда: Зеленоград

Сообщение STAKANOV »

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


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

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

Файл генерирующий значения констант для GNU AS прилагается
Janus
постоялец
Сообщения: 134
Зарегистрирован: 07.11.2005 16:06:49

Сообщение Janus »

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

Сообщение STAKANOV »

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

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

Код: Выделить всё

{$IFDEF WIN32}
    IOCtlSocket(...
{$ELSE}
    FpFcntl(...
{$ENDIF}


Ну и в uses что-то в этом роде
Guest

Сообщение Guest »

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

Сообщение STAKANOV »

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

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

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

правда я так понял, что такой путь не особо используется
Janus
постоялец
Сообщения: 134
Зарегистрирован: 07.11.2005 16:06:49

Сообщение Janus »

А если я оставлю блокирующий режим и попытаюсь узнать, пуст ли буфер? В руководстве нет функции select.
Ответить