Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   УЧИМСЯ ИСПОЛЬЗОВАТЬ WINSOCK. ЧАСТЬ I. (https://forum.antichat.xyz/showthread.php?t=123692)

slesh 05.06.2009 00:14

УЧИМСЯ ИСПОЛЬЗОВАТЬ WINSOCK. ЧАСТЬ I.
 
УЧИМСЯ ИСПОЛЬЗОВАТЬ WINSOCK. ЧАСТЬ I.


ПРЕДИСЛОВИЕ
Статья будет состоять их 4-х частей.
1) сокеты, их создание и настройка.
2) подключение, передача и прием данных. многопоточность.
3) различного рода функции для работы с сокетами (снифанье итд итп.)
4) примеры работы с сокетами и некоторыми прикладными протоколами типа HTTP и SOCKS 5

ВВЕДЕНИЕ
Статья пригодится в первую очередь новичкам, ну и другим тоже иногда будет полезно знать некоторые вещи.
Очень часто видно на форуме, что даже для примитивной работы с сетью многие использую компоненты типа Indy и им подобные. Вот и хотелось исходя из этого, описать основные принципы работы с сокетами. Я не буду описывать работу со всеми функциями, а также работу с сокетами в не блокирующем режиме, потому как это уже не относится к основной работе. Все примеры буду приводить на Delphi потому как многим это понятнее.

ПРИНЦИПЫ РАБОТЫ
За основу работы берутся 2 DLL
wsock32.dll – устаревший вариант применяемый в Win 9x системах. (Winsock)
ws2_32.dll – более новые вариант с добавлением множества новых функций, применяется в Win NT системах. (Winsock 2)
Разница между этими библиотеками лишь в наборе функций и работе некоторых отдельных функций.

В независимости от используемой версии библиотеки можно выделить следующие ступени алгоритма работы
1) Инициализация библиотеки сокетов
2) Создание сокета
3) Настройка сокета
4) Исходящее соединение или ожидание входящего подключение.
5) Передача данных
6) Закрытие соединения.
7) Прекращение работы с библиотекой сокетов (по желанию)

Каждый из этих этапов имеет определенный набор основных команд, а также их последовательностей.

ИНИЦИАЛИЗАЦИЯ БИБЛИОТЕКИ СОКЕТОВ
Без инициализации невозможно будет работать со всеми функциями, которые напрямую зависят от сети.
Для инициализации достаточно выполнить одну лишь функцию WSAStartup
function WSAStartup(wVersionRequired: word; var WSData: TWSAData): Integer; stdcall;
Первым параметром задается версия библиотеки. Для старых версий задавалось $101. Для новой версии (Winsock2) используется число $202. В дальнейшем будем использовать только Winsock2 т.к. его использовании более актуально для других языков типа С++
Вторым параметром передается структура TWSAData (WSAData) в которую будет записана некоторая информация о библиотеке.
Исходя из этого инициализация библиотеки будет выглядеть как:
Код:

uses winsock2;
var
  ws: TWSAData;
begin
  WSAStartup($202,ws);

При удачной инициализации WSAStartup вернет значение = 0. Мы его проверять не будет, потому как в настоящее время это не требуется, но для надежности можно вставить проверку:
Код:

uses winsock2;
var
  ws: TWSAData;
begin
  if WSAStartup($202,ws)<>0 then
    begin
      вывод сообщения об ошибке
      exit;
    end;

СОЗДАНИЕ СОКЕТА
Для создания сокета следует использовать функцию socket.
function socket( const af, struct, protocol: Integer ): TSocket; stdcall;
Первый параметр – тип адреса, существует множество типов, но т.к. мы работает с TCP/IP то будем использовать AF_INET
Второй параметр – тип создаваемого сокета. Нас будут интересовать 3 типа
SOCK_STREAM – потоковый, с установление соединения. Используется для TCP протокола.
SOCK_DGRAM – дейтаграмный, без установки соединения. Исполь для UDP протокола.
SOCK_RAW- сырой сокет. Без заранее определенного протокола.
Третий параметр – непосредственно протокол который мы будет использовать.
Для нас будут важны следующие 3:
IPPROTO_TCP – TCP соединение
IPPROTO_UDP – UDP соединение
IPPROTO_RAW – для сырого сокета.
В результате выполнения функции мы получит дискриптор сокета, который мы будет использовать в дальнейшем для передачи информации. При ошибке, будет вернут код INVALID_SOCKET.
Код:

var
 TCP_sock:TSocket;
 UDP_sock:TSocket;
 RAW_sock:TSocket;
begin
 TCP_sock:=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // создадим TCP сокет
 if TCP_sock= INVALID_SOCKET then ошибка_создания_сокета
 UDP_sock:=socket(AF_INET, SOCK_ DGRAM, IPPROTO_ UDP); // создадим UDP сокет
 if UDP _sock= INVALID_SOCKET then ошибка_создания_сокета
 RAW_sock:=socket(AF_INET, SOCK_ RAW, IPPROTO_ RAW); // создадим сырой сокет
 if RAW_sock= INVALID_SOCKET then ошибка_создания_сокета

НАСТРОЙКА СОКЕТА
Для того чтобы куда-то подключиться или начать ожидание входящего подключения на определенном порту, необходимо настроить сокет на этот адрес и порт. Для этой цели служит структура sockaddr_in.
Для ожидания сходящего подключения необходимо заполнить следующие поля:
Тип адреса: sin_family:=AF_INET;
Порт на который будем жать подключения sin_port:=htons(SERVER_PORT);
Порт – это число в диапазоне от 0 до 65535. К примеру HTTP по умолчанию имеет порт 80.
Если задать его больше, то значением будет число = остатку от деления указанного порта на 65536. т.е. если задать PORT = 70000, то на самом деле будет = 4464;
htons – функция winsock которая преобразует номер порта в нужный для работы формат и обрезает часть, которая выходит за пределы диапазона 0-65535.
Здание IP адреса интерфейса с которого будут приниматься подключения. Это связанно с тем, что на одном компьютере может существовать несколько сетевых адаптеров и есть необходимость в привязке к какому-нибудь именно одному адресу.
Для преобразования IP адреса в нужный вид применяется функция inet_addr. Она преобразует адрес из сивольного вида в числовой (двоичный)
Для того чтобы принимать подключения со всех адаптером, можно использовать константу INADDR_ANY.
sin_addr.s_addr:=inet_addr(‘192.168.2.1’); // принимать подключения только с адреса 192.167.2.1
sin_addr.s_addr:=INADDR_ANY; // принимать со всех интерфейсов.
!ВАЖНО: следует указывать ТОЛЬКО IP адреса. Доменные имена не принимаются. По этому необходимо через DNS узнать адрес соответствующий данному имения. Об этом я расскажу чуть ниже.

Для подключения к другому компьютеру необходимо заполнить эту структуру следующим образом.
sin_family:=AF_INET; // аналогично входящему подключению
sin_port:=htons(PORT); // аналогично входящему подключению, но тут PORT указывает номер порта на который требуется подключиться.
sin_addr.s_addr:=inet_addr(‘192.168.2.1’); // указываем IP адрес куда нам необходимо подключиться.
При работе с UDP иногда требуется отослать широковещательный пакет. т.е. всем компьютерам сети. Для этого необходимо указать адрес - INADDR_BROADCAST, но для этого требуется еще указать отдельные опции для сокета.
Чаще всего мы не знаем IP адрес сервера, зато мы знаем его доменное имя.
Для поиска IP адреса по его доменному имени существует функция gethostbyname,
которая в качестве параметра принимает доменное имя. Чтобы не замарачиваться с этим делом, можно вынести это всё в отдельную функцию, которая принимает доменное имя и возвращает его IP адрес или пустое значение в случае ошибки или неверного указания доменного имени.
Код:

function GetIPAddress(name: string):string;
var
  p: PHostEnt;
begin
 p:=gethostbyname(pchar(name)); // получим IP
 if p=nil then result:=’’ // если ошибка или нет адреса
  else result:=inet_ntoa(PInAddr(p.h_addr_list^)^); // извлечем и преобразуем адрес.
end;

Также немало важным является настройка некоторых таймаутов. В частности на чтение данных из сокета. Это очень заметно при работе с UDP протоколом, где данные отправляются без установки соединения. А также в других программах, где необходимо ограничить время ожидания приема данных.
Специально для этого создана функция setsockopt. В её возможности входит очень много функций, но нам будет интересна только SOL_SOCKET с параметрами
SO_RCVTIMEO – таймаут для чтения данных
SO_SNDTIMEO – таймаут для посылки данных
Примером работы может быть следующий код
Код:

var
 timeout:TTimeVal;
begin
 timeout.tv_usec:=0;
 timeout.tv_sec:=10000; // время задается в миллисекундах. 10000 = 10 секунд
 // установим для сокета sock время ожидания чтения данных = 10 секунд.
 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, @timeout, sizeof(ttimeval));

Важно отметить что эта опция появилась только в NT'шных ОС начиная с 2000 вроде (точно незнаю). Т.к. в winsock 1.0 не полностью были реализованы BSD сокеты, то там ата функция не поддерживается. т.е. её применение не даст ни какого эффекта.
В большинстве книг по Winsock именно про этот момент многие забывают упомянуть. На практике в многопоточных системах с большим кол-вом клиентов очень часто появляются ошибки связанные с зависшим ожиданием получения данных, что со временем очень сказывается на производительности и занимаемых ресурсах.
Для UDP – это вообще очень серьёзная вещь, потому что из-за проблем с сетью или ошибки в адресах, может всё зависать.

Также для отправки широковещательного UDP пакета, сокету необходимо установить опции SOL_SOCKET с параметром SO_BROADCAST и флагом TRUE.

Код:

var
 Opt:BOOL;
begin
  Opt:=TRUE;
  setsockopt(sock,SOL_SOCKET,SO_BROADCAST, pchar(@Opt),sizeof(Opt)) ;

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

(С) SLESH 2009


P.S. в виду сессии, продолжение выйдет гдето через неделю.

-Hormold- 05.06.2009 00:26

Спасибо, возьму на заметку.
Но мой выбор пал на Indy.
Всё-таки всё уже готово...
Решать кодеру...

[n]-c0der 05.06.2009 00:36

Смотря для какой задачи выбирать, спам боты, мейлеры, на indy писать, это как в хоккей с битой играть.
Кстати slesh, насчет сообщения об ошибке, сделай лучше функцию, которая будет подробно выводить сведения об ошибке(WSAGetLastError к примеру), для многих будет полезно.
Потому что, у многих кто начинает работать с winsock на Delphi, возникнут проблемы, в этом я уверен.

RumShun 05.06.2009 07:53

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

Да, и вот еще вопрос про задание таймаута, он раз и на всегда задается сокету, или только до следуещей отправки/получения данных?

slesh 05.06.2009 10:41

2 Scorpion. В этом мире весь кодинг под Win - боян, потому что первоисточник всего -
MSDN
Да и сокеты MS тоже скопипастила из BSD

2 RumShun Если поставил таймаут то, он действует на этот сокет пока ты его не закроеш

bons 05.06.2009 21:05

gethostbyname устарела, вместо нее рекомендуется юзать getaddrinfo

slesh 05.06.2009 21:25

Ну если дело на то пошло, то для большинства функций необходимо ставить приставку WSA потому как это более новые функции.
А на счет DNS то вобще лучше юзать DnsQuery_A потому что она работает еще быстрее, потому как первооснова DNS.

bons 05.06.2009 21:41

Цитата:

Ну если дело на то пошло, то для большинства функций необходимо ставить приставку WSA потому как это более новые функции.
про WSA-функции вообще отдельный разговор, они существуют только в windows и их полезность еще под вопросом. Но даже если использовать WSA-функции это не отменяет полезность getaddrinfo.
Цитата:

А на счет DNS то вобще лучше юзать DnsQuery_A потому что она работает еще быстрее, потому как первооснова DNS.
Не лучше. Не надо без нужды использовать низкоуровневые функции, ведь придется писать то что уже написано.

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

slesh 05.06.2009 22:45

Ну пока что основной протокол один - DNS.
А вот на счет низкоуровневости, тут смотря какая задача. Когда идет очень много запросов то getaddrinfo будет загивать тебе систему потому что она боле нагромажденная в плане кода чем DnsQuery_A. (про ручную реализацию DNS на сокетах промолчу).
А если требуется с работа с учётом на будущее, на далекое будущее, то getaddrinfo как раз подойдет.
Да и если дело на то пошло то на IPv6 утвердили еще в 95 году, и до сих пор пытаются перейти. Прогнозирую это на 2011-12 года. 16 лет, на переход. Так что когда getaddrinfo будет очень нужна, то это будет еще не скоро )

Цитата:

Не лучше. Не надо без нужды использовать низкоуровневые функции, ведь придется писать то что уже написано.
Ты подумай над своими словами именно в данной теме.
DnsQuery_A - это еще не низкоуровневая функция. А использование её не скажется на размерах твоей программы, потому как кода затратишь одинаково.
А вот при использовании getaddrinfo и gethostbyname ты еще сделает теже операции, но только при этом пройдет с десяток(сотен) лишних операций.
Одно дело когда ты юзаешь напрямую AFD - тут да. кода больше и геморнее.

Так что итог таков - всё зависит от задачи и сферы применения.
И вообще это основы Winsock, а не его хитрости )

bons 05.06.2009 23:32

Цитата:

Да и если дело на то пошло то на IPv6 утвердили еще в 95 году, и до сих пор пытаются перейти. Прогнозирую это на 2011-12 года. 16 лет, на переход. Так что когда getaddrinfo будет очень нужна, то это будет еще не скоро )
вот нескоро это будет как раз из-за того что куча софта написано кое-как.
с DnsQuery потеряешь в поддержке IPv6 и в переносимости кода на другие платформы.
Цитата:

И вообще это основы Winsock, а не его хитрости )
основы они на то и основы что должны быть безукоризнены и без сомнительных оптимизаций в виде этих ваших DnsQuery_A ;)
в конце концов Руссинович рекомендует getaddrinfo

intNet 06.06.2009 22:11

Новичкам будет полезно, +.
Сам буду ждать четвёртую часть статьи, вопрос с соксами не до конца решён.

slesh 14.06.2009 19:06

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

Lamia 14.06.2009 19:13

базываи понятия!Не отражена вся тема!

slesh 14.06.2009 19:31

2 Lamia ты читать умеешь?
Это обучающая статья для новичков, в которой описаны основные необходимые данные о Winsock. Причем расписано всё на примитивном и понятном уровне, чтобы не забивать мозги лишней информацией и левыми данными, которые на реале абсолютно не юзается.
А если тебе нужно чтото помощнее, то учи англ яз и лезь на MSDN где описано всё в мельчайших подробностях.

Flenov 30.12.2009 02:14

Цитата:

Сообщение от slesh
Код:

var
 timeout:TTimeVal;
begin
 timeout.tv_usec:=0;
 timeout.tv_sec:=10000; // время задается в миллисекундах. 10000 = 10 секунд
 // установим для сокета sock время ожидания чтения данных = 10 секунд.
 setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, @timeout, sizeof(ttimeval));


Ты ничего не путаешь?
Помоему, всю жизнь tv_sec - были секундами, а tv_usec - микросекундами?
Кстати, а в чём приемущества setsockopt перед sellect (именно в смысле таймаута)?
Только в том, что в setsockopt не надо добавлять сокет в набор?


Кстати, если не секрет, подскажи по ком (ну или чём) ты учился (я про сокеты)?

zythar 30.12.2009 02:50

почему делфи?

зы я такое писал, давно уже, только. под никсы и с использованием С.
кстати, вроде бы все аналогично, кроме создания сокета. а так, те же структуры, те же функции для того чтобы читать/писать, так?

Flenov 30.12.2009 06:14

А вот ещё вопрос на засыпку:
Какие коды ошибок (кроме -1) могут вернуть Send и Recv?

slesh 30.12.2009 10:35

2 Flenov
Send и Recv -
0 - сокет закрылся по нормальному
-1 - ошибка
>0 данные пришли

setsockopt удобно для того чтобы не мучать select а потом recv всё время, а тут сразу сокет всегда будет давать таймаут на recv. Так что просто меньше команд.

2 zythar под никсы почти также, только там в некоторых случаях ошибка имеет другой код

ntldr 30.12.2009 14:31

Цитата:

Сообщение от Flenov
А вот ещё вопрос на засыпку:
Какие коды ошибок (кроме -1) могут вернуть Send и Recv?

во первых не Send и Recv, а send и recv. Во вторых они не возвращают код ошибки, а возвращают результат выполнения. Код ошибки возвращает WSAGetLastError.

Flenov 30.12.2009 15:56

slesh, а вот шутки ради попробовал сет сок ёпт не с TTimeVal, а с LongWord.
Результат одинаковый, что удивило.
Кстати, если уж на то пошло, получается что сет сок ёпт задаёт чисто время жизни соединения, а sellect только проверяет за установленное время сокет (ы) в наборе на готовность принять или отправить.

Я именно про таймауты.

ErrorNeo 30.12.2009 16:45

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

еще один +1 в пользу необходимости разделения раздела "наши статьи" на тематические подразделы

ps
открыл для себя набор интересного чтива под названием "все темы c slesh" :D :D

=Zeus= 30.12.2009 18:42

Да, для новичков самое то. Сам с удовольствием прочитал, буду ждать 3-4 части.

Flenov 31.12.2009 10:20

Вот, я тут решил немного почудить в стиле PHP.

Короче вот код.
Пусть дети играются.
Код:

unit FSockets; {(c)Flenov forum.antichat}

interface

uses
  SysUtils, WinSock2;

type
  TFSocket = TSocket;


function FSocketsInicialize(Version: Word=$0202): Integer;
function FSockOpen(Host: String; Port: Word): TFSocket;
function FPuts(Sock: TFSocket; Str: String): Integer;
function FGets(Sock: TFSocket; var Size: Integer): String;
function FRedySend(Sock: TFSocket; Timeout: LongWord=10): Boolean;
function FRedyRead(Sock: TFSocket; Timeout: LongWord=10): Boolean;
function FClose(Sock: TFSocket): Integer;
function FSocketsDeinicialize: Integer;

implementation


function FSocketsInicialize(Version: Word=$0202): Integer;
var
  wData: TWSAData;
begin
  Result:=WSAStartup(Version, wData);
end;

function FSockOpen(Host: String; Port: Word): TFSocket;
var
  Sock: TFSocket;
  SockAddr: TSockAddrIn;
  HostEnt: PHostEnt;
  InAddr: TInAddr;
  FDSet: TFDSet;
begin
  Result:=0;
  HostEnt:=GetHostByName(PChar(Host));
  if (HostEnt=nil) then Exit;
  FillChar(InAddr, SizeOf(InAddr), 0);
  With InAddr, HostEnt^ do
                          begin
                            S_un_b.s_b1:=Byte(h_addr^[0]);
                            S_un_b.s_b2:=Byte(h_addr^[1]);
                            S_un_b.s_b3:=Byte(h_addr^[2]);
                            S_un_b.s_b4:=Byte(h_addr^[3]);
                          end;
  SockAddr.sin_family:=AF_INET;
  SockAddr.sin_addr:=InAddr;
  SockAddr.sin_port:=HToNS(Port);
  FillChar(SockAddr.sin_zero, SizeOf(SockAddr.sin_zero), 0);
  Sock:=Socket(AF_INET, SOCK_STREAM, 0);
  if (Sock<>INVALID_SOCKET) then
    if (Connect(Sock, @SockAddr, Sizeof(SockAddr))<>SOCKET_ERROR) then Result:=Sock
                                                                  else CloseSocket(Sock);
end;

function FPuts(Sock: TFSocket; Str: String): Integer;
begin
  Result:=Send(Sock, PChar(Str)^, Length(Str), 0);
end;

function FGets(Sock: TFSocket; var Size: Integer): String;
var
  Buff: PChar;
begin
  Buff:=GetMemory(Size);
  try
    Size:=Recv(Sock, Buff^, Size, 0);
    Result:=Copy(Buff, 1, Size);
  finally
    FreeMemory(Buff);
  end;
end;

function FRedySend(Sock: TFSocket; Timeout: LongWord=10): Boolean;
var
  WFDS: TFDSet;
  tv: TTimeVal;
begin
  Result:=False;
  tv.tv_sec:=Timeout;
  tv.tv_usec:=0;
  FD_ZERO(WFDS);
  FD_SET(Sock, WFDS);
  if FD_ISSET(Sock, WFDS) then
    if (Select(0, nil, @WFDS, nil, @tv)=1) then Result:=True;
end;

function FRedyRead(Sock: TFSocket; Timeout: LongWord=10): Boolean;
var
  RFDS: TFDSet;
  tv: TTimeVal;
begin
  Result:=False;
  tv.tv_sec:=Timeout;
  tv.tv_usec:=0;
  FD_ZERO(RFDS);
  FD_SET(Sock, RFDS);
  if FD_ISSET(Sock, RFDS) then
    if (Select(0, @RFDS, nil, nil, @tv)=1) then Result:=True;
end;

function FClose(Sock: TFSocket): Integer;
begin
  Result:=CloseSocket(Sock);
end;

function FSocketsDeinicialize: Integer;
begin
  Result:=WSACleanup;
end;
end.


Flenov 31.12.2009 10:27

Ссылки на пример использования:
Ссылка 1
Ссылка 2

Библиотека FSockets + WinSock2:
Ссылка 1
Ссылка 2

.Life 31.12.2009 19:22

А вот ещё вопрос на засыпку:
Какие коды ошибок (кроме -1) могут вернуть Send и Recv?

Flenov 01.01.2010 01:28

Да наверно получается, что -1 единственный код ошибки

ntldr 22.02.2010 02:01

На windows 7 существуют варианты использовать сырые сокеты? (без всяких winpcap)

#Specan 06.03.2010 18:23

Уважаемый slesh, я вижу вы неплохой специалист в области сетевого программирования.
Если знаете, подскажите пожалуйста такой вопрос:

В PHP сокеты можно сравнить с файлами и там есть такая замечательная функция feof, которая возвращает true если все данные считаны и соответственно false если нет.
Есть ли аналог подобной функции в Delphi.

Теперь зачем мне это:
Дело в том, что я создаю небольшой TCP клиент, и я хочу не разрывать соединение.
Вот мне как раз и нужно знать получил ли я все данные или нет.
Если тупо использовать recv и ждать, когда она вернёт 0 - это не вариант.
Она просто тупо зависнет если я её вызову после приёма всех данных.
Вот мне и нужнол решение.
Подскажите, если знаете.
Заранее спасибо.

Chrome~ 06.03.2010 19:59

Цитата:

Сообщение от #Specan
Уважаемый slesh, я вижу вы неплохой специалист в области сетевого программирования.
Если знаете, подскажите пожалуйста такой вопрос:

В PHP сокеты можно сравнить с файлами и там есть такая замечательная функция feof, которая возвращает true если все данные считаны и соответственно false если нет.
Есть ли аналог подобной функции в Delphi.

Теперь зачем мне это:
Дело в том, что я создаю небольшой TCP клиент, и я хочу не разрывать соединение.
Вот мне как раз и нужно знать получил ли я все данные или нет.
Если тупо использовать recv и ждать, когда она вернёт 0 - это не вариант.
Она просто тупо зависнет если я её вызову после приёма всех данных.
Вот мне и нужнол решение.
Подскажите, если знаете.
Заранее спасибо.

#Specan, немного неправильно.
Цитата:

Если подключение, открытое при помощи fsockopen() не было закрыто сервером, feof() будет ждать достижения таймаута прежде чем вернуть TRUE. Время таймаута по умолчанию равно 60 секундам. Вы можете использовать stream_set_timeout() для того, чтобы изменить это значение.
То есть сервак должен закрыть соединение, что бы данная функция возвратила True.
Цитата:

Сообщение от #Specan
Дело в том, что я создаю небольшой TCP клиент, и я хочу не разрывать соединение.

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

Хотя я точно не уверен насчет всего этого, поэтому если что, пусть slesh ответит. Он укажет более правильный вариант.

#Specan 06.03.2010 20:19

Спасибо за ответ!
Получается php feof можно сравнить с delphi sellect?

Chrome~ 06.03.2010 20:52

Получается, что в некотором смысле да.


Время: 17:09