Как лучше организовать TCP Сервер и Клиент

Вопросы программирования и использования среды Lazarus.

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

mig-31
постоялец
Сообщения: 224
Зарегистрирован: 14.07.2011 13:46:48

Сообщение mig-31 »

Есть еще сетевая библиотека Lnet.
https://lnet.wordpress.com/

К тому же есть PascalScada, которую можно разширить под ваши требования. MODBUS TCP/RTU уже реализованы. Подключение к базам данных тоже через Zeos. Нехватает только MODBUS TCP сервера.
Vadim
долгожитель
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение Vadim »

vvvch
В таком случае не заморачивайтесь ресурсами, а лучше заморочтесь вопросом удобства разработки. ;-) Объекты, по сравнению с голыми сокетами, более "человеколюбивы". Поэтому лучше начать с какой-нибудь высокоуровневой библиотеки, типа Synapse, чтобы код был понятен с первого прочтения. Вы так быстрее добъётесь результата.
Аватара пользователя
vvvch
постоялец
Сообщения: 105
Зарегистрирован: 26.04.2013 11:05:39
Откуда: г.Боровичи, Новг. обл.

Сообщение vvvch »

mig-31
PascalScada мне не понравилась, по тому и взялся писать своё. (Впрочем это частности, просто моё мнение)
А чем lnen отличается от synapse?
Аватара пользователя
AlphaBlend
постоялец
Сообщения: 207
Зарегистрирован: 22.05.2016 09:13:10

Сообщение AlphaBlend »

lnet попроще будет )
Linus
новенький
Сообщения: 47
Зарегистрирован: 11.01.2013 22:01:28

Сообщение Linus »

Здравствуйте! Юзаю socket юнит и не могу понять почему периодически не биндится сокет.

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

//существуем пока не  мрет приложение
 while (not terminated) do
 begin
   Fready:=false;
    //'создаем сокет, слушаем, реагируем'
   SockH:=fpSocket(AF_INET, SOCK_STREAM,0);

   SockAddr.sin_family:=AF_INET;
   SockAddr.sin_port:=htons(22409);
   SockAddr.sin_addr.s_addr:=HostToNet((127 shl 24) or 1); //127.0.0.1

   sizeofSockAdr:=sizeof(SockAddr);

   while (fpBind(SockH,@SockAddr, sizeofSockAdr)=-1) do
   begin
      writeln('Ошибка связывания');
      sleep(2000);
   end;


      writeln('Сервер запущен....');


          if (fpListen(SockH,1)=-1) then
             writeln('Ошибка ожидания ')
          else
          begin
             writeln('Ожидание соединения....');


            SockH:=fpaccept(SockH,@SockAddr, @sizeofSockAdr);
            if SockH=-1 then
               writeln('error in Accept')
            else
            begin
                 //*******************************************************
                 writeln('есть соединение...ok');
                 Fready:=true;

                 setlength(in_buf,512);

                 //получаем вход поток, пока сокет на месте (запроc не привышает 36Байт))
                 while (SockH<>-1) do
                 begin
                      fprecv(SockH,@in_buf[0],512,0);

                      if length(PChar(in_buf))=0 then break;

                      writeln('<<'+PChar(in_buf));
                      //шлем на обработку!
                      proc_in_query(PChar(in_buf));
                      in_buf[0]:=0;
                 end;

                 Fready:=false;

                 writeln('потеряно соединение');
                 writeln('Закрываю сокет! =',CloseSocket(SockH));
                 SockH:=-1;
                 sleep(2000);
          end;
   end;
end; //end while
end;                 
MysticCoder
постоялец
Сообщения: 154
Зарегистрирован: 14.09.2013 00:20:28

Сообщение MysticCoder »

Linus писал(а): почему периодически не биндится сокет.


ОС сокет с прошлого запуска не убила, бывает если сокет не закроешь перед завершением программы.
olegy123
долгожитель
Сообщения: 1643
Зарегистрирован: 25.02.2016 11:10:20

Сообщение olegy123 »

опция SO_REUSEADDR
Linus
новенький
Сообщения: 47
Зарегистрирован: 11.01.2013 22:01:28

Сообщение Linus »

как бы

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

CloseSocket(SockH)


Ладно это пол беды, как сделать так чтобы сокет при потери клиента мог принять другого? (данный мною выше код данного не выполняет, хотя это и странно)
Аватара пользователя
serbod
постоялец
Сообщения: 449
Зарегистрирован: 16.09.2016 10:03:02
Откуда: Минск
Контактная информация:

Сообщение serbod »

Рекомендую набор компонентов DataPort (также доступен через OnlinePackageManager) - это простая и удобная обертка над synapse, где есть не только TCP/UDP но и UART, FTDI, LPT. Опрос порта/сокета выполняется в отдельном потоке и не тормозит. А используя базовый абстрактный класс TDataPort в качестве свойства класса, можно подключать к объекту разные порты по выбору.

Если нужно что-то посерьезнее, то у меня есть исходники TCP/UDP сервера, рассчитанного на десятки тысяч подключений (используется lnet) с использованием очередей пакетов. И библиотеки для USB/FTDI/CAN а также для RAS (Dial-up), IP Helper (системные настройки сетевых устройств), GSM modem (AT-команды) , но они заточены под конкретное оборудование. Оттуда можно почерпнуть принципы самонастройки, самодиагностики и самовосстановления после сбоев. Как в аналоговой аппаратуре: нет контакта - не работает, есть контакт - заработало. Или может проще тут рассказать?
Linus
новенький
Сообщения: 47
Зарегистрирован: 11.01.2013 22:01:28

Сообщение Linus »

Это мне ответ?)

Нет, клиент один и только один - другое приложение. Средства только нативные. (желательно одинаковые как для c++ так и для fpc).
Задача на текущий момент сводится к тому что сервер ждет подключение, если клиент после передачи умирает - забыть про него и ждать и принять его еще раз (возможно ни один раз).
В чем пролема в коде? После смерти клиента закрываю сокет, но вот беда, он обратно не хочет прослушиваться/орендоваться и т.д.

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

fpsetsockopt(SockH, SOL_SOCKET, SO_REUSEADDR, @opt, sizeof(opt) ); \0
добавил
Аватара пользователя
serbod
постоялец
Сообщения: 449
Зарегистрирован: 16.09.2016 10:03:02
Откуда: Минск
Контактная информация:

Сообщение serbod »

Для TCP-сервера алгоритм такой (для линукса, в винде в принципе то же самое):
1. создаем сокет, функция socket()
2. привязываем сокет к локальному IP-адресу, функция bind()
3. переводим сокет в пассивный режим приема входящих подключений, функция listen()
4. принимаем входящее подключение, функция accept(), при этом создается новый сокет, похожий на исходящий, только уже подключенный
.. пункт 4 повторяем для каждого входящего подключения
5. закрываем сокет, функция close()

Для UDP достаточно только bind(), а listen() не нужен, он не создает новых сокетов, а общается со всеми через один и тот же.

setsockopt() нужен только для извращений и тонкой настройки, в обычном порядке не нужен.
Linus
новенький
Сообщения: 47
Зарегистрирован: 11.01.2013 22:01:28

Сообщение Linus »

serbod писал(а):Для TCP-сервера алгоритм такой (для линукса, в винде в принципе то же самое):
4. принимаем входящее подключение, функция accept(), при этом создается новый сокет, похожий на исходящий, только уже подключенный
.. пункт 4 повторяем для каждого входящего подключения


Вот какую забыл важную мелочь, создается новый сокет "клиента"...

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

procedure ServerSockThread.Execute;
var
  SockAddr:TInetSockAddr;
  sizeofSockAdr:longint;
  opt:integer;

  in_buf:TSockBuf;
begin
 //существуем пока не  мрет приложение
   Fready:=false;
    //'создаем сокет, слушаем, реагируем'
   SockH:=fpSocket(AF_INET, SOCK_STREAM,0);
   opt:=1;
   fpsetsockopt(SockH, SOL_SOCKET, SO_REUSEADDR, @opt, sizeof(opt) );

   SockAddr.sin_family:=AF_INET;
   SockAddr.sin_port:=htons(22409);
   SockAddr.sin_addr.s_addr:=HostToNet((127 shl 24) or 1); //127.0.0.1

   sizeofSockAdr:=sizeof(SockAddr);


   while (fpBind(SockH,@SockAddr, sizeofSockAdr)=-1) do
   begin
      writeln('Ошибка связывания');
      sleep(2000);
   end;


      writeln('Сервер запущен....');


          if (fpListen(SockH,1)=-1) then
             writeln('Ошибка ожидания ')
          else
          begin
             writeln('Ожидание соединения....');


            while (not terminated) do
            begin


                 Client:=fpaccept(SockH,@SockAddr, @sizeofSockAdr);
                 if Client=-1 then
                    writeln('error in Accept')
                 else
                 begin
                      //*******************************************************
                      writeln('есть соединение...ok');
                      Fready:=true;

                      setlength(in_buf,36);

                      //получаем вход поток, пока сокет на месте (запроc не привышает 36Байт))
                      while (Client<>-1) do
                      begin

                           fprecv(Client,@in_buf[0],35,0);

                           if length(PChar(in_buf))=0 then break;

                           writeln('<<'+PChar(in_buf));
                           //шлем на обработку!
                           proc_in_query(PChar(in_buf));
                           fillchar(in_buf[0],36,0);
                      end;

                      Fready:=false;

                      writeln('потеряно соединение');
                      writeln('Закрываю сокет! =',CloseSocket(Client));
                 end;
            end; //end while
    end;
end;


Все работает (для указанных выше задач).
son
новенький
Сообщения: 39
Зарегистрирован: 22.11.2011 11:50:58

Сообщение son »

Про MODBUS кстати делал так:
1) ставим драйвера MBX7 (у него можно настроить разные типы подключения)
2) ходим через интерфейс этого драйвера
olegy123
долгожитель
Сообщения: 1643
Зарегистрирован: 25.02.2016 11:10:20

Сообщение olegy123 »

полная реализация протокола MODBUS есть в различных компонентах. Он не сложен.
Linus
новенький
Сообщения: 47
Зарегистрирован: 11.01.2013 22:01:28

Сообщение Linus »

Дополняю тему вопросом: как установить таймаут на чтение из сокета? (fprecv)
а так же проверить сколько скопилось в буфере отправки.
Ответить