Could not bind socket address and port are already in use как исправить

unit Server;

    unit Server;

    interface

    uses

      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

      Dialogs, StdCtrls, IdContext, IdBaseComponent, IdComponent, IdCustomTCPServer,

      IdTCPServer, IdCmdTCPServer, IdExplicitTLSClientServerBase, IdFTPServer,

      IdIntercept, IdServerInterceptLogBase, IdServerInterceptLogFile, ShlObj;

    type

      TForm1 = class(TForm)

        Memo1: TMemo;

        Button1: TButton;

        Button2: TButton;

        Edit3: TEdit;

        edit2: TEdit;

        edit1: TEdit;

        Label1: TLabel;

        Label2: TLabel;

        Label3: TLabel;

        IdFTPServer1: TIdFTPServer;

        logfile1: TIdServerInterceptLogFile;

        Label4: TLabel;

        procedure Button1Click(Sender: TObject);

        procedure Button2Click(Sender: TObject);

        procedure IdFTPServer1ChangeDirectory(ASender: TIdFTPServerContext;

          var VDirectory: string);

        procedure IdFTPServer1DeleteFile(ASender: TIdFTPServerContext;

          const APathName: string);

        procedure IdFTPServer1FileExistCheck(ASender: TIdFTPServerContext;

          const APathName: string; var VExist: Boolean);

        procedure IdFTPServer1GetFileDate(ASender: TIdFTPServerContext;

          const AFilename: string; var VFileDate: TDateTime);

        procedure IdFTPServer1GetFileSize(ASender: TIdFTPServerContext;

          const AFilename: string; var VFileSize: Int64);

        procedure IdFTPServer1MakeDirectory(ASender: TIdFTPServerContext;

          var VDirectory: string);

        procedure IdFTPServer1RetrieveFile(ASender: TIdFTPServerContext;

          const AFileName: string; var VStream: TStream);

        procedure IdFTPServer1RenameFile(ASender: TIdFTPServerContext;

          const ARenameFromFile, ARenameToFile: string);

        procedure IdFTPServer1RemoveDirectory(ASender: TIdFTPServerContext;

          var VDirectory: string);

        procedure IdFTPServer1UserLogin(ASender: TIdFTPServerContext;

          const AUsername, APassword: string; var AAuthenticated: Boolean);

        procedure IdFTPServer1StoreFile(ASender: TIdFTPServerContext;

          const AFileName: string; AAppend: Boolean; var VStream: TStream);

        procedure IdFTPServer1AfterUserLogin(ASender: TIdFTPServerContext);

        procedure IdFTPServer1Exception(AContext: TIdContext;

          AException: Exception);

        procedure IdFTPServer1Status(ASender: TObject; const AStatus: TIdStatus;

          const AStatusText: string);

        procedure IdFTPServer1Stat(ASender: TIdFTPServerContext;

          AStatusInfo: TStrings);

        procedure IdFTPServer1Execute(AContext: TIdContext);

      private

        { Private declarations }

      public

        { Public declarations }

      end;

    var

      Form1: TForm1;

      homedir: string;

    implementation

    {$R *.dfm}

    function setSlashes(APath:String):String;

    var

    slash:string;

    begin

    slash := StringReplace(APath, ‘/’, », [rfReplaceAll]);

    slash := StringReplace(slash, », », [rfReplaceAll]);

    Result :=  slash;

    end;

    procedure TForm1.Button1Click(Sender: TObject);

    begin

    idftpserver1.DefaultPort:=strtoint(edit3.text);

    idftpserver1.Active:=true;

    showmessage(‘Подключено’);

    end;

    procedure TForm1.Button2Click(Sender: TObject);

    begin

    idftpserver1.Active:=false;

    close;

    end;

    procedure TForm1.IdFTPServer1AfterUserLogin(ASender: TIdFTPServerContext);

    begin

    //Установить домашний каталог

    homedir:=’c:’;

    end;

    procedure TForm1.IdFTPServer1ChangeDirectory(ASender: TIdFTPServerContext;

      var VDirectory: string);

    begin

    ASender.CurrentDir:= VDirectory;

    end;

    procedure TForm1.IdFTPServer1DeleteFile(ASender: TIdFTPServerContext;

      const APathName: string);

    begin

    if fileexists(APathName) then

    begin

    DeleteFile(APathName);

    end;

    end;

    procedure TForm1.IdFTPServer1Exception(AContext: TIdContext;

      AException: Exception);

    begin

    showmessage(AException.Message);

    end;

    procedure TForm1.IdFTPServer1Execute(AContext: TIdContext);

    begin

    logfile1.DoLogWriteString(AContext.Connection.IOHandler.AllData);

    end;

    procedure TForm1.IdFTPServer1FileExistCheck(ASender: TIdFTPServerContext;

      const APathName: string; var VExist: Boolean);

    begin

    if fileexists(APathName) then

    begin

    VExist:=true;

    end

    else

    begin

    VExist:=False;

    end;

    end;

    procedure TForm1.IdFTPServer1GetFileDate(ASender: TIdFTPServerContext;

      const AFilename: string; var VFileDate: TDateTime);

    var

      fdate:tdatetime;

    begin

    //Поставить дату файла в переменную

    fdate:= FileAge(AFilename);

     if  not (fdate=-1) then  begin

    VFileDate:=fdate;

    end;

    end;

    procedure TForm1.IdFTPServer1GetFileSize(ASender: TIdFTPServerContext;

      const AFilename: string; var VFileSize: Int64);

    Var

      LFile : String;

      rec:tsearchrec;

      ASize: Int64 ;

    begin

     LFile := setslashes(homedir + AFilename );

    try

    if FindFirst(Lfile, faAnyFile, rec) = 0 then  repeat

                 Asize:=rec.Size;

                 until FindNext(rec) <> 0;

            finally

                FindClose(rec);

    end;

    if Asize > 1 then

    VFileSize:= Asize

    else

    VFilesize:=0;

    end;

    procedure TForm1.IdFTPServer1MakeDirectory(ASender: TIdFTPServerContext;

      var VDirectory: string);

    var

    ldir:string;

    begin

    ldir:= setslashes(homedir + VDirectory);

    if not DirectoryExists(ldir) then

    if not CreateDir(ldir) then

    raise Exception.Create(‘Невозможно создать ‘+ldir);

    end;

    procedure TForm1.IdFTPServer1RemoveDirectory(ASender: TIdFTPServerContext;

      var VDirectory: string);

    Var

    LFile : String;

    begin

    LFile := setslashes(homedir + VDirectory);

    if directoryexists(LFile) then begin

    RemoveDir(LFile);

    end

    else

    begin

    Raise Exception.Create(‘Не удается удалить каталог’);

       end;

    end;

    procedure TForm1.IdFTPServer1RenameFile(ASender: TIdFTPServerContext;

      const ARenameFromFile, ARenameToFile: string);

    begin

    if not Renamefile(ARenameFromFile,ARenameToFile) then

    begin

       Raise Exception.Create(‘Не удается переименовать файл ‘);

    end;

    end;

    procedure TForm1.IdFTPServer1RetrieveFile(ASender: TIdFTPServerContext;

      const AFileName: string; var VStream: TStream);

    begin

    VStream := TFileStream.Create(setSlashes

    (homedir+AFilename),fmOpenRead);

    end;

    procedure TForm1.IdFTPServer1Stat(ASender: TIdFTPServerContext;

      AStatusInfo: TStrings);

    var

    i:integer;

    begin

    for i:= 0 to astatusinfo.Count-1 do

    begin

    memo1.Lines.Add(astatusinfo.Strings[i]);

    end;

    end;

    procedure TForm1.IdFTPServer1Status(ASender: TObject; const AStatus: TIdStatus;

      const AStatusText: string);

    begin

    memo1.lines.add(Astatustext);

    end;

    procedure TForm1.IdFTPServer1StoreFile(ASender: TIdFTPServerContext;

      const AFileName: string; AAppend: Boolean; var VStream: TStream);

    begin

    if not Aappend then

    VStream := TFileStream.Create(AFileName,fmCreate)

    else

          VStream := TFileStream.Create(AFileName,fmOpenWrite)

    end;

    procedure TForm1.IdFTPServer1UserLogin(ASender: TIdFTPServerContext;

      const AUsername, APassword: string; var AAuthenticated: Boolean);

    begin

    if (AUsername = edit1.Text) and (APassword = edit2.Text) then

    begin

    AAuthenticated:=True

    end

    else

    begin

    AAuthenticated := False;

    end;

    end;

    end.

Chertenok_n_13

218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

1

Обработка ошибки

27.09.2012, 11:57. Показов 3973. Ответов 14

Метки нет (Все метки)


Такая возникла проблема.
по умолчанию порт у сервера 10001, а у клиента 10000.
Пыталась сделать, чтобы если порт у сервера занят уже, то поменять значения портов наоборот.
но проблема в том, что если это сделать, все равно вылетает ошибка Could not bind socket. Address and port are already in use. Причем если при обработке указать два совсем других порта (которые не заняты) все равно после обработки исключительной ситуации возникает эта же ошибка.

Delphi
1
2
3
4
5
6
7
8
9
10
11
try
      IdUDPClient1.Active:= true;
      IdUDPServer1.Active:= true; //как сделать, чтобы ошибка не показывалась тут, если порт занят
      except
      IdUDPClient1.Active:= false;
      IdUDPServer1.Active:= false;
      IdUDPServer1.DefaultPort:=10000;
      IdUDPClient1.Port:=10001;
      IdUDPClient1.Active:= true;
      IdUDPServer1.Active:= true;//и вот тут
      end;

и вообще, может ли вызывать эту ошибку то, что к порту уже подключен клиент?



0



2649 / 2270 / 279

Регистрация: 24.12.2010

Сообщений: 13,725

27.09.2012, 12:18

2

Зачем вообще в одном и том же приложении понадобился и сервер и клиент ?
В случае udp вполне достатно одного — либо сервера либо клиента.



0



218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

27.09.2012, 13:20

 [ТС]

3

mss, для передачи звука.
насколько мне известно (может быть плохо известно) клиент принимать не может. А мне надо в двух направлениях. что-то типа голосового чата.
Один пользователь выбирает из базы, которая хранит все IP адреса, адресата и звонит ему. Тот в совю очередь может ответить, сбросить, либо быть занят.
В одном направлении не проблем нету сделать, а для двух сделала так.
просто по другому как сделать ума не приложу. Со всем этим впервые имею дело.



0



210 / 169 / 24

Регистрация: 24.04.2012

Сообщений: 615

27.09.2012, 13:49

4

Цитата
Сообщение от Chertenok_n_13
Посмотреть сообщение

Пыталась сделать, чтобы если порт у сервера занят уже, то поменять значения портов наоборот.

И кем же он уже занят? И смысл менять, тогда клиент не сможет занять этот порт и опять исключение.

Цитата
Сообщение от Chertenok_n_13
Посмотреть сообщение

и вообще, может ли вызывать эту ошибку то, что к порту уже подключен клиент?

По UDP никто никуда не подключается. Пакет шлется на адрес и порт, а открыт на том адресе тот порт или нет и дошел ли вообще пакет никого не волнует.



0



2649 / 2270 / 279

Регистрация: 24.12.2010

Сообщений: 13,725

27.09.2012, 13:57

5

Цитата
Сообщение от Chertenok_n_13
Посмотреть сообщение

клиент принимать не может

Может. И в TCP и в UDP.
Принципиальная разница лишь в том что в TCP инициатором подключения может являться только клиент, но не сервер.
А в UDP понятие «подключение» отсутствует в принципе.



0



218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

27.09.2012, 19:35

 [ТС]

6

Цитата
Сообщение от Nutserus
Посмотреть сообщение

По UDP никто никуда не подключается. Пакет шлется на адрес и порт, а открыт на том адресе тот порт или нет и дошел ли вообще пакет никого не волнует.

а откуда тогда ошибка Could not bind socket. Address and port are already in use??

Добавлено через 52 секунды

Цитата
Сообщение от mss
Посмотреть сообщение

Может. И в TCP и в UDP.
Принципиальная разница лишь в том что в TCP инициатором подключения может являться только клиент, но не сервер.
А в UDP понятие «подключение» отсутствует в принципе.

просто тогда придется разграничивать,что на одном запускается клиент, а на другом сервер. это удобно если два компьютера. а если больше?



0



2649 / 2270 / 279

Регистрация: 24.12.2010

Сообщений: 13,725

27.09.2012, 20:36

7

Цитата
Сообщение от Chertenok_n_13
Посмотреть сообщение

придется разграничивать,что на одном запускается клиент, а на другом сервер. это удобно если два компьютера. а если больше?

Это я, наверно, со стенкой говорил. С дубовой.



0



218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

27.09.2012, 20:45

 [ТС]

8

Цитата
Сообщение от mss
Посмотреть сообщение

Это я, наверно, со стенкой говорил. С дубовой.

хорошо, что Вы в этом прекрасно разбираетесь. люди мне кажется тут за помощью обращаются.
место того, чтобы вот такие реплики высказывать, можно просто помочь или что-нибудь посоветовать, если видите, что человек не может разобраться.



1



2649 / 2270 / 279

Регистрация: 24.12.2010

Сообщений: 13,725

27.09.2012, 21:03

9

Цитата
Сообщение от Chertenok_n_13
Посмотреть сообщение

что-нибудь посоветовать

Я УЖЕ посоветовал — выкинь из воего приложения либо IdUDPClient либо IdUDPServer.
Заявленная тобой функциональность приложения ничуть не пострадает, при этом идея-фикс «поменять значения портов наоборот» рассосется сама собой.



0



218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

27.09.2012, 22:42

 [ТС]

10

mss, ты сказал, что вполне хватит. после этого я сказала для чего мне это надо и больше ты ничего вразумительного по этому поводу не сказал.
будь так добр, объясни каким образом это организовать. Клиент-сервер? клиент-клиент? или как?
я обратилась просто за помощью. Я не прошу для меня ничего писать, просто хочу разобраться.
как заставить принимать данные клиенту? я этого просто не понимаю.



0



210 / 169 / 24

Регистрация: 24.04.2012

Сообщений: 615

28.09.2012, 00:33

11

Кладешь на форму IdUDPServer1, чтобы послать пакет вызываешь IdUDPServer1.Send(); , принимаешь процедурой IdUDPServer1UDPRead.



1



218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

28.09.2012, 11:00

 [ТС]

12

Nutserus, ага.
порты должны же быть одинаковые? так?
просто тут самая проблемка, что если порты одинаковые, то вылазит ошибка и компонент не становится активным.
или я чего-то не понимаю…
если пытаюсь отправлять сервером и принимать сервером, то вылазит ошибка «Socket Error #10054 Connection reset by peer»

Добавлено через 34 минуты
и плюс, даже если отправлять простое сообщение, а не буффер, то OnRead все равно не реагирует…



0



210 / 169 / 24

Регистрация: 24.04.2012

Сообщений: 615

28.09.2012, 11:53

13

Цитата
Сообщение от Chertenok_n_13
Посмотреть сообщение

порты должны же быть одинаковые? так?

Одинаковые с чем? У тебя один компонент и один порт.

Я конечно понимаю, читать сдк скучно и неинтересно, сразу хочется руками потрогать, тогда почему бы не скачать готовый исходник и пилить его под себя? Идея неновая и реализаций ее множество.



0



218 / 124 / 99

Регистрация: 14.03.2011

Сообщений: 628

28.09.2012, 12:12

 [ТС]

14

искала исходники. в одном направлении клиент-сервер есть и много, а сервер-сервер например ни разу не встретила тоже самое как клиент-клиент.
искала, читала статьи по созданию таких приложений, только все что не попадались были клиент-сервер и в одну сторону.



0



2649 / 2270 / 279

Регистрация: 24.12.2010

Сообщений: 13,725

28.09.2012, 19:02

15

У udp-компонента, в твоем случае например TIdUDPClient, есть методы приема и есть методы передачи данных.
В соответствии с протоколом инф.обмена в каждый момент времени один из партнеров по обмену передает данные (вызывает методы передачи), другой ожидает и принимает эти данные (вызывает методы приема). Вот и всё. И никаких коннектов, серверов, клиентов.
Т.е. обе стороны обмена инициируют свои компоненты, занимая каждый свой порт, естественно свободный и заранее известный партнеру.
Передающая сторона указывает адрес-порт принимающей и передает ей сообщение. Получив сообщение другая сторона может зная адрес и порт партнера может

точно таким же

образом ответить.
Вопросы прохождения NAT и файрволов здесь для простоты не рассматриваем — это отдельная песня, во много раз сложнееп твоей примитивной задачи.



1



IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

28.09.2012, 19:02

Помогаю со студенческими работами здесь

Обработка ошибки
Здравствуйте,подскажите,пожалуйста,как обработать ошибку:при вводе заданной точности я ввожу…

Обработка ошибки
Я получаю таги таким образом:
TAG_ID3 *id3=(TAG_ID3*)BASS_ChannelGetTags(chan, BASS_TAG_ID3); //…

Обработка ошибки
добавляю данные в таблицу:
Form1-&gt;ADOTable1-&gt;Insert();
Form5-&gt;ADOTable1-&gt;Insert();…

Обработка ошибки
Почему в любом случае есть лист или нет с таким названием все равно срабатывает
If MsgBox(&quot;Лист…

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:

15

What is this?

This knowledgebase contains questions and answers about PRTG Network
Monitor and network monitoring in general. You are invited to get involved by asking and
answering questions!

Learn more

PRTG Network Monitor

Intuitive to Use. Easy to manage.
More than 500,000 users rely on Paessler PRTG every day. Find out how you can
reduce cost, increase QoS and ease planning, as well.

Free Download

After upgrading to the latest version I am getting this error on three sensors. One is an Amazon CloudWatch sensor, one is an HTTP sensor and the other is an FTP sensor. All are on the same server. Restarting the probe sometimes will bring one of them back, but then the others throw the error. The sensors were fine before the upgrade.

cloudwatch
ftp
sockets

Best Answer

Accepted Answer

If this message is shown by PRTG sensors, this means that the PRTG Probe is not able to open any outgoing TCP ports for outgoing monitoring requests.

There are two possible reasons for the message «Could not bind socket. Address and port are already in use»:

  • In the probe settings you have selected an outgoing IP (and with that a network interface) that can not reach the desired target system. The IP stack on the probe system tries to open an outgoing port but the selected network interface can’t provide it.
  • Sometimes overloading situations (which can be caused by PRTG, or other programs, or other problems) can cause the IP stack of the probe system to run out of outgoing port numbers. This can sometimes be cured by rebooting the probe system.

Disclaimer: The information in the Paessler Knowledge Base comes without warranty of any kind. Use at your own risk. Before applying any instructions please exercise proper system administrator housekeeping. You must make sure that a proper backup of all your data is available.

Корректное отключение

Для корректного завершения сетевого подключения обе стороны должны послать пакеты с сигналом о завершении (FIN), которые указывают что стороны не будут больше отсылать данные, также каждая сторона должна подтвердить (ACK) получение сигнала о завершении сетевого обмена данными. FIN инициируется когда приложение вызывает метод close(), shutdown() или exit(). После завершения работы метода close() ядро переходит в режим ожидания подтверждения от второй стороны приема сигнала о завершении. Это делает возможной ситуацию когда процесс инициировавший отключение будет завершен прежде чем ядро освободит рессурсы связанные с подключением, и снова разрешит использовать порт для связывания с другим процесоом (в этом случае, при попытке использования порта мы получим исключение AddressAlreadyInUse).

На изображении:

  1. Имеется установленное соединение, состояние ESTABLISHED
  2. Клиент инициирует окончание подключения, посылает серверу сигнал о завершении подключения (FIN), переходит в состояние ожидания ответа сервера (FIN_WAIT_1)
  3. Сервер получает сигнал о завершении подключения и отправляет подтверждение (ACK), переходит в состояние ожидания завершения подключения (CLOSE_WAIT) (вызывает close())
  4. Сервер отсылает клиенту сигнал о том что успешно закрыл подключение (FIN) и пробует прочитать подтверждение клиента (ACK), после чего не дожидаясь его отключается.
  5. Теперь клиенту может придти два сигнала в разной очередности

    ACK — клиент получил подтверждение о том что сервер понял его намерение закрыть подключение

    • Клиент переходит в состояние ожидания сигнала об окончании закрытия подключения (FIN) от сервера (FIN_WAIT_2)
    • Клиент получает сигнал о закрытии подключения сервером (FIN), отправляет подтверждение (ACK), некоторое время ждет(TIME_WAIT) и отключается (ядро освобождает рессурсы) (CLOSED)

    FIN — клиент получает сигнал о закрытии подключения на стороне сервера(FIN), раньше чем подтверждение от сервера (ACK), о получении инициирующего сигнала о закрытии от клиента (FIN)

    1. Клиент отправляет подтверждение приема сигнала о том что сервер закрывает соединение, и переходит в состояние отключения (CLOSING)
    2. После отключения пробует считать сигнал подтверждения от сервера (который был отправлен сервером сразу после получения от клиента сигнала о завершении работы, пункт 2), некоторое время ожидает(TIME_WAIT) и ядро освобождает рессурсы (CLOSING).

На рисунке показаны все воможные состояния, которые могут быть во время корректного завершения, в зависимости от порядка получения пакетов FIN и ACK от удаленной стороны. Обратите внимание, если вы инициировали завершение подключения (левая половина рисунка), то другая сторона не будет ожидать подтверждения получения вами пакета FIN (правая половина рисунка). Сотояние TIME_WAIT требуется на случай если подтверждение (ACK) которое вы отправили не было получено на другой стороне, или на случай появления ложных пакетов по какой-то причине. Я не знаю почему на стороне сервера не сделали состояние TIME_WAIT, хотя если клиент инициирует закрытие, это безусловно и не должно требовать ожидания. Состояние TIME_WAIT может удерживать порт в течение нескольких минут после завершения процесса. Время удержания варьируется в зависимости от операционной системы, в некоторых операционных системах оно является динамическим, стандартные значения лежат в диапазоне от 1 до 4 минут.

Если обе стороны успеют инициировать сигнал завершения, раньше чем получат его от другой стороны, то обе стороны будут вынуждены пройти через ожидание (TIME_WAIT).

Корректное отключение слушающей стороны

Слушающий сокет может быть закрыт немедленно, при отсутствии входящих подключений, его состояние переходит сразу в CLOSED. При наличии входящих подключений, будет произведен переход к FIN_WAIT_1 и затем к TIME_WAIT.

Обратите внимание, на стороне слушающего сокета невозможно гарантировать чистое закрытие. Пока вы проверяете использование соединения методом select() до закрытия, существует крошечная, но реальная возможность появления входящего подключения после вызова select() и до вызова close().

Непредвиденное отключение удаленной стороны

При внезапном отключении сервера, локальная сторона инициирует закрытие соединения, и в этом случае TIME_WAIT неизбежен. Если удаленная сторона исчезает из-за сбоя сети или перезагрузки машины (редкие случаи), локальный порт будет оставаться привязанным вплоть до истечения таймаута состояния TIME_WAIT. Хуже того, некоторые старые операционные системы, не реализуют таймаут для состояния FIN_WAIT_2, и могут оставаться в нем бесконечно долго, в этом случае спасти может только перезагрузка системы.

Если же локальное приложение (клиент) падает в процессе активного соединения, порт будет занят пока не завершится состояние TIME_WAIT, то же верно для приложений, закрытых в процессе подключения к удаленной стороне(pending).

Способы избежания проблем

Опция SO_REUSEADDR

Можно использовать метод setsockopt(), для установки опции SO_REUSEADDR, что позволит создавать привязку к порту даже если он еще находится в состоянии TIME_WAIT (привязка к порту будет разрешена только для одного процесса). Это самый простой и эффективный метод избежать сообщения «address already in use».

Но, как ни странно, использование опции SO_REUSEADDR, может привести к более трудноотлавливаемым ошибкам чем «address already in use». SO_REUSEADDR позволяет использовать порт застрявший в TIME_WAIT, но вы все еще сможете использовать этот порт в том процессе, в котором он привязан изначально.

WHAT?

Предположим я использую локальный порт 1010 и подключаюсь на порт 300 сервера foobar.com, затем клиент отключается и порт переходит в состояние TIME_WAIT, и я могу использовать этот порт (1010) в любом подключении за исключением подключения к foobar.com на порт 300.
Ситуация в которой это может вызвать проблему может быть такой: моя программа пытается найти зарезервированный локальный порт(<1024) для привязки, чтобы подключиться к службе которая требует зарезервированный порт, и если я буду использовать опцию SO_REUSEADDR, то при каждом запуске программы на моей машине я буду получать тот же зарезервированный порт, даже если он висит в TIME_WAIT, и могу получить «Address already in use», в том месте где порт был использован в последний раз. В этом случае нужно отказаться от использования опции SO_REUSEADDR.

Некоторые не любят использовать SO_REUSEADDR, т.к. эта опция имеет проблемы с безопасностью. В некоторых операционных системах эта опция может позволить разным процессам использовать один и тот же порт одновременно. И это проблема, потому что большинство серверов привязываются к порту не используя конкретный адрес, вместо этого они используют INADDR_ANY (команда netstat отобразит их как *.8080). Таким образом, если сервер связывается с адресом *.8080, то другой процесс, от другого пользователя локальной машины, может подключиться к адресу local_machine.8080 (и намерения его могут быть совсем не хорошими), и перехватывать все ваши подключения, т.к. он указал более конкретный адрес. Эта проблема проявляется только на многопользовательских системах, не имеющих ограничений для учетных записей, и это не является уязвимостью которая доступна снаружи локальной машины, ее можно легко избежать используя привязку к конкретному адресу машины (не используя INADDR_ANY).

Другим не нравится что ядро системы тратит свои рессурсы на сотни или даже тысячи TIME_WAIT состояний, этой проблемы также можно избежать используя подход описанный ниже.

Клиент отключается первым

Глядя на рисунок выше, мы видим, что состояния TIME_WAIT можно избежать когда закрытие инициируется на удаленной стороне, а значит проблем можно избежать, если сервер позволяет клиенту инициировать отключение первым. Для этого можно построить архитектуру пользовательского протокола таким образом, что клиент знает когда ему нужно инициировать закрытие. Сервер может произвести безопасное отключение получив команду EOF от клиента, однако нам все равно придется установить таймаут ожидания отключения клиента, чтобы тот смог корректно завершить работу. Почти всегда достаточно подождать несколько секунд, пока соединение с сервером не будет корректно завершено.

Эту концепцию, вероятно, имеет смысл назвать «удаленная сторона отключается первой», иначе мы будем зависить от того что мы называем клиентом и что сервером. Если вы разрабатываете некую систему, состоящую из нескольких клиентских программ, которые находятся на одной машине и обращаются к разным серверам, то вы захотите перенести ответственность за отключение на сервера, для сберегания рессурсов клиентской машины.

К примеру, я написал скрипт который использует remote shell(rsh), для общения со всеми машинами моей сети, и он выполняет работу параллельно, постоянно используя несколько открытых соединения. Для rsh доступно меньше 1024 портов. Сначала я использовал команду «rsh -n», которая вызывает отключение локальной стороны в первую очередь. После нескольких тестов все свободные порты меньше 1024, оказались в состоянии TIME_WAIT, и процесс остановился. Удаление опции -n приводит к инициированию отключения на удаленной стороне, и проблема TIME_WAIT устраняется, однако это может привести rsh к зависанию в ожидании входаящего подключения. И если вы закрываете входящее подключение локально, порт снова окажется в состояние TIME_WAIT. В конечном счете я просто отказался от использования rsh и написал свою реализацию на perl (текущую версию можно скачать тут)

Уменьшение таймаута

Если, по какой-то причине, ни один из изложенных вариантов вам не подходит, есть возможность сократить таймаут состояния TIME_WAIT. Возможность и реализация такой операции зависит от операционной системы которую вы используете. Стоить помнить, что слишком короткий таймаут может иметь негативные последствия, в частности при потере пакетов или в перегруженных сетях.

Понравилась статья? Поделить с друзьями:
  • Could not be reactivated in namespace root cimv2 because of error 0x80041003
  • Could not be opened operating system error code 5 access is denied
  • Could not allocate memory как исправить
  • Could not acquire management access for administration runtime error
  • Could not acquire management access for administration mysql workbench ошибка