![]() |
|
Модераторы: Snowy, Poseidon, MetalFan |
![]()
|
|
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Привет всем!
Ребят нужна оперативная помощь. Я пишу многопоточный сервер/клиент на TServerSocket и TClientSocket Делаю так: У меня есть запись(record) определенной структуры в ней есть переменная fCommand: Word; Я заполняю эту структуру данными и передаю таким же образом эти данные принимаются затем структура заполняется даннымии распознается проблема в том что в локальной сети всё без ошибок, а вот по инету помимо моих пакетов приходят еще какие то данные которые не распознаются (т.е. например вместо команды fCommand содержит 0), что это за данные, чем отличается передача данных в локальной сети от передачи через инет? |
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
как принимаешь и передаешь?
Что-то мне подсказывает (стандартная ошибка), что не учитываются результаты Socket.ReceiveBuf и Socket.SendBuf Добавлено через 7 минут и 21 секунду
Ой-е, вопрос отменяется. Не работаю и не буду работать в многопоточном режиме, не знаю, как там всё устроено. |
|||
|
||||
| Antony41 |
|
||||||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
передаю так
FillTData это
//принимаю так. это соответственно происходит в потоке который постоянно проверяет на входящие данные
|
||||||||
|
|||||||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Смотрите получается так:
сервак шлет данные и сразу получает какие то пустые пакеты, хотя клиент не отправлял данные я имею ввиду что команды или пакеты я не посылал в ответ серверу. Предполагаю что когда сервер шлет какой либо пакет, то стандартно приходят какие то данные о подтверждении, что пакет этот доставлен, но так как у меня эти данные преобразутся в мою структуру, то я вижу пустой пакет(record) возможен ли такой вариант? |
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
мне бы такую уверенность, что пришло данных ровно на 1 структуру, ни байтом больше или меньше. Даже в локалке вероятность такого (+-) достаточно велика. Не говоря уже про глобальную сеть. Ввести проверки, сколько пришло. Если меньше - ждать следующей порции. Если больше - выбирать SizeOf данных, а хвост - запоминать для стыковки со следующим пакетом. Добавлено через 4 минуты и 8 секунд Поэтому нужно анализировать количество реально прочитанного Вот еще что непонятно - сокет создан в основном потоке, так (лежит-то он на форме)? Почему же обращение по выборке данных идет из дополнительного? Это несколько не-потокобезопасно. |
|||
|
||||
| Antony41 |
|
||||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Это ведь не Визуальный компонент почему не потокобезопасно?
тоесть так?
Это сообщение отредактировал(а) Antony41 - 11.2.2013, 20:43 |
||||||
|
|||||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
не совсем. Под анализом я имел ввиду проверку i Вот так делается у меня:
Давайте возьмем за аксиому, что переключение между потоками происходит в любой момент. Этот момент определяется виндой и повлиять на него возможно только специальными методами, например - использованием объектов синхронизации (критические секции, мьютексы, евенты, сообщения). Допустим, что Винда решила переключиться в основной поток после выполнения (а может, и во время выполнения) этой строчки: И в основном потоке сокет закрывается (ну, прервалось соединение - бывает и такое). И обратно переключается уже после того, как сокет закрыт. Соответственно, всякие обращения типа Read, дальшейшее чтение ReceiveLength и т.п. будут как минимум невалидными, а как максимум - приведут к ошибке работы с памятью. Добавлено через 3 минуты и 22 секунды И то - далеко не факт, что произойдет моментальное переключение на нужный поток. Например, в случае использования критических секций это гарантировать нельзя. Некоторую гарантию дает только SendMessage, которая, в частности, используется (-лась, по крайней мере в D7) в Synchronize |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
тоесть Вы хотите сказать, что лучше бы было использование компонента сокета внутри потока?
хотя эти действия у меня заключены в try except и finally за примерчик спасибо отдельное |
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
забыл сказать - этот код из события OnRead компонента. Заводить поток, чтобы без перерыва читать оттуда данные, а отправлять другие данные из основного потока - нельзя. Добавлено через 4 минуты и 29 секунд
Как минимум - создание и активация сокета в OnExecute потока. Но в этом случае для правильной работы сокета в потоке нужно организовать цикл выборки сообщений. В том случае, если тип у сокета (свойство ClientType)- ctNonBlocking. Если выставлен ctBlocking - ничем помочь не могу, не работал с этим режимом. А чем вызвано желание сетевой работы именно в доп.потоке? |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
||||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
Это не примерчик, вот примерчик Реально использующийся уже несколько лет код, не претерпевший почти никаких изменений с тех пор UPD: поменял ссылку на более поздний пост. Это сообщение отредактировал(а) kami - 11.2.2013, 21:15 |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Я так понимаю это всё в не блокирующем режиме? потому что в блокирующем у меня данные вообще не приходят в событии OnRead
Это сообщение отредактировал(а) Antony41 - 11.2.2013, 21:32 |
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
в блокирующем несколько другая система - там (вроде, могу ошибаться) на каждое соединение заводится отдельный поток, в котором всё и вертится. Но - он мне изначально не понравился, посему - даже не разбирался с ним. Это не значит, что блокирующий режим - плохой! Просто я не умею его готовить. Кстати, там в примере - большой недостаток. Во всех событиях OnRead последним оператором должно быть Data.Free, иначе пойдут утечки. Это сообщение отредактировал(а) kami - 11.2.2013, 21:41 |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Про сервер это так, но я имею ввиду клиента. т.е. сервер у меня в поточном режиме(blocking) а клиент впринципе тоже был в (blocking).
И вот теперь я сижу и думаю. Я понимаю что блочный режим для сервера нужен для того что бы поддерживать соединения в отдельном потоке и это нужно для того что бы сервак не зависал, НО зачем клиенту блочный режим ведь у него только одно соединение, или это нужно для поддержки соединений с несколькими серверами так что ли? В данном случае у меня один сервер и значит что мне не нужно использовать поточный режим для клиента? |
|||
|
||||
| kami |
|
|||
|
Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1806 Регистрация: 25.8.2007 Где: Санкт-Петербург Репутация: 6 Всего: 72 |
можно поспорить. Я не думаю, что у Вас в коде производится настолько ресурсоемкая работа с принимаемыми/отправляемыми данными (или их настолько много принимается/отправляется за единицу времени), что это замедлит работу основного потока. Чаще всего, "заморозка" происходит из-за неправильных алгоритмов обработки данных в неблокирующем режиме.
Один ClientSocket - одно соединение. А сделано, я думаю, чтобы использовать один и тот же подход к обработке данных. Согласитесь, нелогично было бы на клиенте ориентироваться на OnRead|OnWrite и т.п., а на сервере - использовать потоки. "Всё должно быть единообразно - подстрижено, покрашено, посыпано песком" (с) армейское выражение. Добавлено через 2 минуты и 41 секунду Аргумент в пользу неблокирующего режима - создание на каждое соединение дополнительного потока считаю расточительством. Что будет, если количество живых подключений к серверу будет в районе 1000? Вы представляете себе, как винде будет тяжко разрулить такое количество потоков? |
|||
|
||||
| Antony41 |
|
||||||||||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
впринципе переписал немного, получается что сервер работает в режиме blocking а клиент nonblocking, короче
проблемка такова: есть структура Record, и объект этой структуры GlobalData указатель на эту структуру fData: PData
теперь заполняю структуру
и отправлять её приходится непонятным пока образом. Вот так всё нормально принимается клинтом:
если вместо этого написать
то отчет клиента такой:
мне вот непонятно может быть эта строка всё таки верна?
Это сообщение отредактировал(а) Antony41 - 13.2.2013, 17:29 |
||||||||||||
|
|||||||||||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
fData указатель. SizeOf = 4(8)
Добавлено @ 17:40 Пкажи fRSDataTWinSocketStream.Write Добавлено через 11 минут и 29 секунд
Это сообщение отредактировал(а) Чучмек - 13.2.2013, 17:41 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Передаешь только первые 4 байта. Твоя структура принимается частями. Каждую часть, при приеме, ты считаеш новой структурой. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
угу вот я тоже уже понял что это размер в памяти не структуры а указателя. Вот нифига не пойму
Структуры у меня одинаковы что в клиенте что на сервере размер фиксированный у обоих как их не заполняй. вот она структура собственно
Смотрите пакет мне приходит рубленый значит потоки байт данных передаются последовательно один за другим это так? а значит допустим событие OnRead в клиенте может выполнится несколько раз... А это значит мне нужно создать например MemoryStream, как глобальную переменную а при выполнении onRead писать всё в MemoryStream последовательно. Я в правильном направлении? Как тогда мне определить начало и конец нового пакета? для того чтобы обработать принятый пакет очистить MemoryStream и начать принимать следующий пакет поток данных нового пакета? Добавлено @ 18:10 С этим я согласен тупанул малёха, теперь делаю так
но тут возникает вопрос чем отличается от того что выше вот это fRSData.WriteBuffer(fData^, SizeOf(fData^)); Добавлено @ 18:15 А что показывать стандартный класс, стандартная функция))) (Delphi XE) Это сообщение отредактировал(а) Antony41 - 13.2.2013, 18:18 |
||||
|
|||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Да. Я уже нашел. Какой протокол? TCP? -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
может вобще стоит перевести ClientSocket в режим блокинга
Добавлено через 6 минут и 52 секунды ну наверно))) ведь TServerSocket/TClientSocket использует его вроде как Добавлено через 11 минут и 54 секунды И еще вот такой вопрос например если у меня клиент будет работать в режиме nonblocking я так понимаю это в основном потоке будут приниматься данные... Предположим что пакет принимался принимался а пользователь затем открыл какое нибудь окно ShowModal, прием данных приостановится? если я размещу на форме компонент TidAntifreeze который собственно не относится к TClientSocket, будет ли пролжаться приём данных в таком случае? |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
По размеру передаваемой структуры. Делаешь глобальными (или свойство) буфер (размер твоей структуры) Buff и размер данных записанных в буфер. L В onRead, 0. offset:=0; 1. записываешь в буфер, с позиции L,принятые данные c позиции offset размером count:=min(BuffSize-L,ReadLength-offset) ; 2. L=L+count; 3. If L=BuffSize -> Передаешь записанный буфер на обработку ; L:=0; offset:=offset+count; if offset< ReadLength then goto 1 Это сообщение отредактировал(а) Чучмек - 13.2.2013, 18:39 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Жесть что попроще ни как ? может всё таки перевести клиента в режим блокинга и работать уже в потоках?
|
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
А какая разница?
Неблокирующий: пришел пакет -> событие Блокирующий режим: пришел пакет ->поток вышел из Write. Хоть так хоть эдак, сколько пакетов, столько фрагментов. Добавлено через 3 минуты и 58 секунд
Есть системный буфер. После выхода из Modal прочитаешь несколько пакетов сразу. Это сообщение отредактировал(а) Чучмек - 13.2.2013, 18:51 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
||||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
-------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ну вот смотрите вот как я сейчас всё переписал приблизительно...
Подопните плиз в нужную сторону если что то не так. Processing(@GlobalData); //там я уже работаю с пакетом SIZE_OF_STRUCT это постоянный размер моей структуры ReadedLen это кол-во прочитанных байт (глобальная Integer) MS это TMemoryStream
Это сообщение отредактировал(а) Antony41 - 13.2.2013, 19:39 |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Antony41, извини сразу не ответил, был занят.
Я с TClientSocket не работал. Работал с сокетами только через API Но чтение я бы сделал так.
Это сообщение отредактировал(а) Чучмек - 14.2.2013, 02:45 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
впринципе похоже, только вот как быть если данных принято больше чем сама структура. тоесть если пришли данные еще и из слудующего пакета, надо вырезать нужное кол-во байт, а к оставшимся начинать дописывать следующий пакет. Или пакеты не мугут быть склеины? |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Вызовется несколько раз proc. Остаток (меньший, чем размер TData) ,будет хранится в выделенной памяти. Адрес выделенной памяти хранится в связанном с сокетом указателе Data. На каждый сокет свой указатель, своя память. Это гарантирует, что при получении, данные разных соединений не будут смешиваться. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
тоесть если я правильно понимаю, например допустим мне осталось принять 10 байт до полного пакета данных, а пришло 1000 байт (10 байт склееный со след. пакетом), и я сделаю вот так обработчик OnRead...
затем отправлю пакет на обработку, то при следующем вызове обработчика в Socket.ReceiveLength будут оставшиеся байты (990 байт) ? Добавлено через 2 минуты и 26 секунд тоесть если пришло 1000 байт а я буду читать по 1 байту Socket.ReceiveBuf(ReadBuf, 1); то обработчик будет вызываться 1000 раз? |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Скажем так.
Твоя структура 200 байт Пришло 199 байт. Событие OnRead. Выделили память 4+200 В выделенную память записали 199 принятых байт и Integer=199 Пришло еще 399 байт Событие OnRead. В выделенную память дописали 1 байт Вызвали proc, передали принятую структуру. В выделенную память записали 200 байт Вызвали proc, передали принятую структуру. В выделенную память записали 198 байт и Integer=198 ждем еще. Добавлено через 5 минут и 53 секунды Че гадать, когда легко можно проверить.
-------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Спасибо Вам огромное!
|
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Кроме того. Если ты не используешь устойчивое соединение, можешь прочитать сразу все данные в OnDisconnect.
-------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Ребят вот еще какой вопрос. Сервак запущен в блочном режиме, и создаются отдельные потоки, в самом потоке прописана вот эта строка
И эта строка грузит мой цп на 25% при 1 подключенном клиенте, почему так как быть? |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
потому что процессор четырёхядерный
-------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ну 4 и что, его хватит только на 4 потока... и потом цп будет загружен на 100%. как быть то чтоб не такой прожерливый был? Может быстро обрабатывать данные и выходить из потока, отключать клиента, а клиент будет слать серваку каждые например 30-40 сек команду что он на связи... серв же посчитает его отключенным только после 5 попыток поставит ему статус офлайн. А вот если выполняешь какой нибудь запрос от клиента то отсылать сразу его и если нужно соединиться например для передачи какого нибудь файла, стрима, затем снова отрубить.
|
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Ну самое простое, но неправильное, вставить в цикл sleep(100)
-------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ну а правильное ?
|
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Ну вот посмотри.
Скомпилируй запусти, и введи в браузере http://localhost:3600/ -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Antony41, я заврался. Так тоже не годится.
Оно конечно работает, но через костыль. Это сообщение отредактировал(а) Чучмек - 18.2.2013, 11:39 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Чучмек |
|
||||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Antony41, советую разобраться с сокетами (API)
Вот как выглядит сервер на WinSock API. (без проверки ошибок)
p.s. T(Clent)ServerSocket всего лишь ООП обертка для WinSock API. Вот тоже на TServerSocket
p.p.s При закрытии, оба варианта будут ждать, пока не разорвется соединение. Только тогда recv/ClientSocket.ReceiveBuf вернет управление. Это сообщение отредактировал(а) Чучмек - 18.2.2013, 16:24 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
||||
|
|||||
| Чучмек |
|
||||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Ответ один - внутри цикла нет, или не правильно используется функция ожидания. Например. TWinSocketStream.WaitForData возвращает управление если в сокете остались непрочитанные данные. TWinSocketStream - редкое г. Лучше не используй его. На мой взгляд - бесполезный компонент. В TWinSocketStream используются только read/write/WaitForData Если при остановке сервера необходимо сразу остановить все потоки (ClientSocket.Connected возвращает false)- вместо TWinSocketStream.WaitForData используй можно использовать
WaitForData(ClientSocket,timeout) -> true, если есть данные для чтения и false, если timeout Это сообщение отредактировал(а) Чучмек - 18.2.2013, 16:26 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
||||
|
|||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
||||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Да нет, должно работать. Возвращает true, если есть данные для чтения и false, если таймаут или ошибка. Однако если прочитано меньше чем пришло - всегда true и без ожидания. Один байт не прочитал - и будет твой поток грузится на 100%. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
кстати мне sleep(10) очень помогло впринципе засыпание на 10 мс незаметно, это ни как не влияет на приём данных... а вот в цикле с GetticCount самое оно. Загрузка ЦП при 20 подключ. клиентов составила 0-1% при опросе клиентов каждые 3 сек. Провёл тестирование подключил 20 клиентов, и они стабильно обменивались данными с сервером 7-8 часов при закрытии серва все 20 клиентов были на связи. Пока ошибок не выявил.
Это сообщение отредактировал(а) Antony41 - 18.2.2013, 16:39 |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Короче тут такая ситуация вышла... Я запарился с этими vcl (TClientSocket) и решил с чистого листа на Socket API впринципе пока все прекрасно...
Вопросик образовался: Я Выполняю прием данных в основном потоке тоесть
пока выполняется эта функция, блокируется главный поток на время приёма данных. (ну я имею ввиду зависание интерфейса). никакие тут ProcessMessages не помогают, Что будет если я работу и прием данных напишу в отдельном потоке? ни каких конфликтов не возникнет при переключении потоков системой? или что посоветуете. |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Antony41, есть неблокирующий режим, есть select. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ок спс... щас поюзаем
Добавлено через 10 минут и 26 секунд блин... чето я запутался немного. неблокирующий сокет создает отдельный поток на каждое подключение, так не будет ди тоже самое что я буду использовать блокирующий сокет в отдельном потоке, просто создам поток и суну туда все функции работы с сокетом.? |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Ребят помогите чёто не пойму как надо. Вот процедура приема данных у клиента
проблема в том что иногда структура не верно заполняется, а иногда верно. Если проверять когда данные от сервера ушли уже точно, то пакет приходит нормально, а вот если не ушли или ушли не полностью то иногда пакет поступает в обработку (processing()) и обрабатывается не верно. Короче серв шлет тестовый пакет каждые 5 сек, если я буду проверять пришел пакет или нет каждые 5 сек, то ошибки обработки (тут не понятно данные вроде по размеру заполнятся полностью, может там какие нибудь спец симполы в начале или конце пакета добавляются?) А если я буду делать проверку ReceiveNBytes каждые напр. 6-7 сек то всё ОК.
|
|||
|
||||
| Чучмек |
|
||||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Antony41, не выдумывай.
Добавлено через 13 минут и 52 секунды Нажми несколько раз подряд на Button1. Antony41,неблокирующий режим для того и существует, чтобы обойтись без дополнительных потоков. Есть как минимум один способ на блокирующем режиме - проверять состояние сокета через select (посмотри выше, я давал пример где проверялось подключение), и есть несколько способов на неблокирующих. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
||||
|
|||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Просто терпения нет хочется быстрее выучить и написать уже рабочую нормальную прогу...))) Пошел снова читать)))
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
завтра если будет время перепишу всё под неблокирующий. посмотрим что из этого выйдет.
тут я так понимаю select timeout для recv задавать не нужно, так как прием данных будет возникать по событию FD_READ и вечного ожидания данных не будет? |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Да. WSAAsyncSelect "привязывает" оконное сообщение к сокету(или, точнее, события сокета к оконному сообщению) и переводит сокет в асинхронный(неблокирующий) режим. Можно привязать одно или несколько событий FD_xxx. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
||||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
чот то не выходит,
Чучмек,
Почему так то разве если пакет придут по частям то ведь эта функция сработает не корректно, ведь у нас buff:array[word] of ansichar; определяется каждый раз заново короче я не пойму я уже всё сделал в одном основном потоке, сделал привязал событие FD_READ к сообщению всё ок при отправке данных, событие возникает нормально при чём сообщение которое отправляется обрабатывается несколько раз, и приходит нужное число байт, но буфер пустой(((( как правильно принимать рубленные пакеты в асинхронном режиме а именно буфер каких нибудь байт.... блин честное слово запарился, проект пользуется спросом а я не могу тут передачу данных наладить(((Что ж всё так печально то, уже и на стандартные компоненты забил засунулся в API а толку... Добавлено через 4 минуты и 4 секунды код не могу предоставить на работу нужно вечером помучаюсь еще на так я и не понял... блин Хелпинг... |
||||
|
|||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Ты все правильно понимаешь. Пример на неблокирующий режим и только. Конечно, буфер для сборки пакета должен быть глобальным. Как глобально Memo1. Кроме того, если возможно несколько соединений одновременно - под каждый сокет должен быть свой буфер. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
блин приходит пустой пакет(((( как правильно принять данные в ассинхронном режиме не блокирующем?
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
если поставить например вместо if iStillToReceive = 100 then, iStillToReceive = SizeOf(Buff) или 200 то данных в структуре нет, мало того еще и ошибка, а вот если сто +- то читается нормально и там те данные которые я передал |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
тоесть не не так, получается если я ставлю размер принимаемого буфера Buff: array [0..500] of Char; то всё пока нормально приходит
а если поставить уже Buff: array [0..1000] of Char, то уже пустой буфер. как так
|
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Ты ведь пакет собираешь.
Пришел первый кусок (меньше чем указываешь в recv) -сумма меньше. Пришел второй кусок -сумма уже больше. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
какого типа должен быть Buff
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
не работает то принимает то нет, если размер структуры больше 1000 уже не принимает
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
разобрался немного еще отпишусь
|
|||
|
||||
| Чучмек |
|
||||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
-------------------- умную мысль держи при себе, а дурной - поделись с другими |
||||
|
|||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
какой сервер лучше? многопоточный? синхронный асинхронный? синхронный и асинхронный это однопоточные? Могу ли я мспользовать WSAAsyncSelect в многопоточном сервере?
Добавлено через 12 минут и 1 секунду система асинхронных сокетов прикольная и удобная. вобще мне нужно разработать так, чтобы сервер максимально быстро реагировал на входящие события сокетов и так же быстро рассылал им ответное сообщение, на данный момент, могу использовать 8 ядерный Core i7 или серверный комп |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Сколько одновременных подключений ожидается? Сколько времени будет занимать обработка данных от клиента + подготовка ответа? Все что ниже - лишь мое мнение. Если несколько подключений - однозначно многопоточный в блокирующем режиме (простота,распределение нагрузки по ядрам процессора, быстрота реакции) Если большое количество подключений - асинхронный. Если важна максимально быстрая реакция сервера на запросы клиента или процессорозатраты на обработку всех соединений не влезают в один поток (в производительность одного ядра), то необходим многопоточный (число потоков равно числу ядер) асинхронный. На каждый поток должно быть свое окно со своей очередью сообщений. Соединения должны распределятся между потокам. Окна придется создавать на WinAPI. Или используй другой способ работы с сокетами в неблокирующем режиме. Это сообщение отредактировал(а) Чучмек - 17.4.2013, 23:09 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
сейчас пытаюсь сделать так
создал класс Tserver=class (TWinControl) //окно? окно! в нем создал слушающий serversocket, и событие WSAAsyncSelect, передающее FD_Accept в это же окно TServer при обработке сообщения внутри тела TServer, создается дополнительный класс TConnection = class(TWinControl), и в неё заполняются все данные о подключенном сокете, далее указатель на PConnection добавляется в список TList и связывает WSAAsyncSelect с окном TConnection как такой подход?
Добавлено через 1 минуту и 15 секунд получается типа многопоточности? Добавлено через 3 минуты и 51 секунду если учитывать что AsyncSelect переводит сокет в отдельный поток получается при каждом подключении создается сокет и заполняется в класс TWinControl и переводится в отдельный поток, получая в свою очередь события в виде сообщений Добавлено через 4 минуты и 36 секунд или это не так |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
А где сами потоки? Нет? Какая тогда многопоточность? В Delphi отдельную очередь сообщений для окон с объектной оберткой (TWinControl) можно сделать только через Ж., и то, не уверен. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ну сообщения приходят нормально и программа работает без ошибок
Добавлено через 44 секунды былы конечно проблемы с CreateWnd, но они решились перекрытием метода |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
вроде работает хорошо вот полный код
unit ServerSock; interface uses Dialogs, Classes, WinSock, Errunit, Controls, Forms, Messages, Windows, SysUtils; const WM_ACCEPTMESSAGE = WM_USER + 1; WM_SOCKETMESSAGE = WM_USER + 2; type TServer = class; TWMSocketMessage = packed record Msg: Cardinal; Socket: TSocket; SockEvent: Word; SockError: Word; end; TTransportPhase = (tpReceiveLength, tpReceivestring, tpSendstring); PConnection = ^TConnection; TConnection = class(TWinControl) private Owner: TServer; procedure WMSocketMessage(var Msg: TWMSocketMessage); message WM_SOCKETMESSAGE; public ClientSocket: TSocket; ClientAddr: AnsiString; MsgSize: Integer; Msg: AnsiString; Phase: TTransportPhase; Offset: Integer; BytesLeft: Integer; SendRead: Boolean; constructor Create(AOwner: TServer); destructor Destroy; override; end; TServer = class(TWinControl) private vWSAData: TWSAData; FHost: AnsiString; procedure Startup; //разрешить сокетное использование function Cleaned: Boolean; //запретить сокетн. использование procedure ClearConnections; procedure WMAcceptMessage(var Msg: TWMSocketMessage); message WM_ACCEPTMESSAGE; public FPort: Word; //только чтение Connections: TList; vServerSocket: TSocket;//серверный сокет который принимает входящие подключения vServerAddr: TSockAddr; // Адрес, к которому привязывается слушающий сокет function Start_Server(Port: Word): Boolean; constructor Create; destructor Destroy; override; // Метод GetConnectionBySocket находит в списке FConnections // запсись, соответствующую данному сокету function GetConnectionBySocket(S: TSocket): PConnection; end; function GetErrorstring(Error: Integer = 0): string; implementation uses Mainunit; { TServer } function GetErrorstring(Error: Integer = 0): string; var Buffer: array[0..2047] of AnsiChar; begin FormatMessage(forMAT_MESSAGE_FROM_SYSTEM, nil, Error, $400, @Buffer, SizeOf(Buffer), nil); Result := Buffer; end; procedure TServer.ClearConnections; var I: Integer; begin for I := 0 to Connections.Count - 1 do begin closesocket(PConnection(Connections[I])^.ClientSocket); PConnection(Connections[I])^.Destroy; end; Connections.Clear; end; constructor TServer.Create; var Params:TCreateParams; begin inherited Create(Application.MainForm); Parent := Application.MainForm; HandleNeeded; CreateParentedControl(Application.MainFormHandle); Connections := TList.Create; Startup; end; destructor TServer.Destroy; var Res: Integer; begin if Assigned(Connections) then begin closesocket(vServerSocket); ClearConnections; Cleaned; Connections.Free; Connections := nil; end; DestroyHandle; inherited; end; function TServer.GetConnectionBySocket(S: TSocket): PConnection; var I: Integer; begin for I := 0 to Connections.Count - 1 do if PConnection(Connections[I]).ClientSocket = S then begin Result := Connections[I]; Exit; end; Result := nil; end; function TServer.Cleaned: Boolean; begin Result := False; if WSACleanup = SOCKET_ERROR then //Говорим что сокеты больше не будут использоваться begin Err_(WSAGetLastError); Exit; end; Logging('Сервер остановлен'); Result := True; end; procedure TServer.Startup; var Res: Integer; begin Res := WSAStartup($101, vWSAData); //Разрешаем использ. сокетов if Res <> 0 then //если ошибка передадим её в обработку ошибок Err_(Res); end; function TServer.Start_Server(Port: Word): Boolean; begin Result := False; FPort := 0; // Формируем адрес для привязки. FillChar(vServerAddr.sin_zero, SizeOf(vServerAddr.sin_zero), 0); vServerAddr.sin_family := AF_INET; vServerAddr.sin_addr.S_addr := INADDR_ANY; try vServerAddr.sin_port := htons(Port); if vServerAddr.sin_port = 0 then begin Logging('Номер порта должен лежать в диапазоне 1-65535'); //MessageDlg('Номер порта должен лежать в диапазоне 1-65535', mtError, [mbOK], 0); Exit; end; // Создание сокета vServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if vServerSocket = INVALID_SOCKET then begin Err_(WSAGetLastError); //MessageDlg(''#13#10 +InttoStr(WSAGetLastError), mtError, [mbOK], 0); Exit; end; // Привязка сокета к адресу if bind(vServerSocket, vServerAddr, SizeOf(vServerAddr)) = SOCKET_ERROR then begin Err_(WSAGetLastError); //MessageDlg('Ошибка при привязке сокета к адресу:'#13#10 + GetErrorstring, mtError, [mbOK], 0); closesocket(vServerSocket); Exit; end; // Перевод сокета в режим прослушивания if listen(vServerSocket, SOMAXCONN) = SOCKET_ERROR then begin Err_(WSAGetLastError); //MessageDlg('Ошибка при переводе сокета в режим прослушивания:'#13#10 + GetErrorstring, mtError, [mbOK], 0); closesocket(vServerSocket); Exit; end; // Связь слушающего сокета с событием FD_ACCEPT if WSAAsyncSelect(vServerSocket, Handle, WM_ACCEPTMESSAGE, FD_ACCEPT) = SOCKET_ERROR then begin Err_(WSAGetLastError); //MessageDlg('Ошибка при установке асинхронного режима слушающего сокета:'#13#10 + GetErrorstring, mtError, [mbOK], 0); closesocket(vServerSocket); Exit; end; // Перевод элементов управления в состояние "Сервер работает" Logging('Сервер работает'); FPort := Port; Result := True; except on EConvertError do // Это исключение может возникнуть только в одном месте - // при вызове StrToInt(EditPortNumber.Text) begin Logging('"' + InttoStr(Port) + '" не является целым числом'); //MessageDlg('"' + InttoStr(Port) + '" не является целым числом', mtError, [mbOK], 0); end; on ERangeError do begin // Это исключение может возникнуть только в одном месте - // при присваивании значения номеру порта Logging('Номер порта должен лежать в диапазоне 1-65535'); MessageDlg('Номер порта должен лежать в диапазоне 1-65535', mtError, [mbOK], 0); end; end; end; procedure TServer.WMAcceptMessage(var Msg: TWMSocketMessage); var NewConnection: PConnection; // Сокет, который создаётся для вновь подключившегося клиента ClientSocket: TSocket; ClientAddr: TSockAddr; // Адрес подключившегося клиента AddrLen: Integer; // Длина адреса begin // Стр###мся от "тупой" ошибки if Msg.Socket <> vServerSocket then begin Logging('Внутренняя ошибка сервера - неверный серверный сокет'); Exit; end; // Обрабатываем ошибку на сокете, если она есть. if Msg.SockError <> 0 then begin Logging('Ошибка при подключении клиента. Код:' + IntToStr(Msg.SockError) + ' Сервер будет остановлен.'); ClearConnections; closesocket(vServerSocket); on StopServer; Exit; end; // Стр###мся от ещё одной "тупой" ошибки if Msg.SockEvent <> FD_ACCEPT then begin Logging('Внутренняя ошибка сервера - неверное событие на сокете'); Exit; end; AddrLen := SizeOf(TSockAddr); ClientSocket := accept(vServerSocket, @ClientAddr, @AddrLen); if ClientSocket = INVALID_SOCKET then begin // Если произошедшая ошибка - WSAEWOULDBLOCK, это просто означает, // что на данный момент подключений нет, а вообще всё в порядке, // поэтому ошибку WSAEWOULDBLOCK мы просто игнорируем. Прочие же // ошибки могут произойти только в случае серьёзных проблем, // которые требуют остановки сервера. if WSAGetLastError <> WSAEWOULDBLOCK then begin Logging('Ошибка при подключении клиента. Сервер будет остановлен'); ClearConnections; closesocket(vServerSocket); on StopServer; end; end else begin // Создаём запись для нового подключения и заполняем её New(NewConnection); NewConnection^ := TConnection.Create(Self); NewConnection.ClientSocket := ClientSocket; NewConnection.ClientAddr := format('%u.%u.%u.%u:%u', [ Ord(ClientAddr.sin_addr.S_un_b.s_b1), Ord(ClientAddr.sin_addr.S_un_b.s_b2), Ord(ClientAddr.sin_addr.S_un_b.s_b3), Ord(ClientAddr.sin_addr.S_un_b.s_b4), ntohs(ClientAddr.sin_port)]); NewConnection.Phase := tpReceiveLength; NewConnection.Offset := 0; NewConnection.BytesLeft := SizeOf(Integer); NewConnection.SendRead := False; // Связываем сообщение с новым сокетом if WSAAsyncSelect(ClientSocket, NewConnection.Handle, WM_SOCKETMESSAGE, FD_READ or FD_WRITE or FD_CLOSE) = SOCKET_ERROR then begin Logging('Ошибка при установке асинхронного режима подключившегося сокета.'); closesocket(ClientSocket); Exit; end; // Добавляем запись нового соединения в список Connections.Add(NewConnection); Logging('Зафиксировано подключение с адреса ' + NewConnection.ClientAddr); end; end; { TConnection } constructor TConnection.Create(AOwner: TServer); begin inherited Create(Application.MainForm); Parent := Application.MainForm; HandleNeeded; Owner := AOwner; end; destructor TConnection.Destroy; begin DestroyHandle; inherited; end; procedure TConnection.WMSocketMessage(var Msg: TWMSocketMessage); var Connection: PConnection; Res: Integer; // Вспомогательная процедура, освобождающая ресурсы, связанные // с клиентом и удаляющая запись подключения из списка procedure RemoveConnection; begin closesocket(Connection.ClientSocket); Owner.Connections.Remove(Connection); Connection^.Destroy; //Dispose(Connection); end; begin // Ищем соединение по сокету Connection := Owner.GetConnectionBySocket(Msg.Socket); if Connection = nil then begin Logging('Внутренняя ошибка сервера - не найдено соединение для сокета'); Exit; end; // Проверяем, были ли ошибки при взаимодействии if Msg.SockError = 10050 then begin ShowMessage('1'); Logging('Ошибка при взаимодейсвии с клиентом ' + Connection.ClientAddr + ': ' + Inttostr(Msg.SockError)); RemoveConnection; Exit; end; // Анализируем, какое событие произошло case Msg.SockEvent of FD_READ: begin // Проверяем, на каком этапе находится взаимодействие с клиентом. if Connection.Phase = tpReceiveLength then begin // Этап получения от клиента длины строки. При выполнении этого // этапа сервер получает от клиента длину строки и размещает её // в поле Connection.MsgSize. Здесь приходится учитывать, что // теоретически даже такая маленькая (4 байта) посылка может // быть разбита на несколько пакетов, поэтому за один раз этот // этап не будет завершён, и второй раз его придётся продолжать, // загружая оставшиеся байты. Connection.Offset - количество // уже прочитанных на данном этапе байт - одновременно является // смещением, начиная с которого заполняется буфер. Res := recv(Connection.ClientSocket, (PAnsiChar(@Connection.MsgSize) + Connection.Offset)^, Connection.BytesLeft, 0); if Res > 0 then begin Logging('Клиент шлет данные...'); // Если Res > 0, это означает, что получено Res байт. // Соответственно, увеличиваем на Res количество прочитанных // на данном этапе байт и на такую же величину уменьшаем // количество оставшихся. Inc(Connection.Offset, Res); Dec(Connection.BytesLeft, Res); // Если количество оставшихся байт равно нулю, можно переходить // к следующему этапу. if Connection.BytesLeft = 0 then begin // Проверяем корректность принятой длины строки if Connection.MsgSize <= 0 then begin Logging('Неверная длина строки от клиента ' + Connection.ClientAddr + ': ' + IntToStr(Connection.MsgSize)); RemoveConnection; Exit; end; // Следующий этап - это чтение самой строки Connection.Phase := tpReceivestring; // Пока на этом этапе не прочитано ни одного байта Connection.Offset := 0; // Осталось прочитать Connection.MsgSize байт Connection.BytesLeft := Connection.MsgSize; // Сразу выделяем память под строку SetLength(Connection.Msg, Connection.MsgSize); end; end else if Res = 0 then begin logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение'); RemoveConnection; Exit; end else // Ошибку WSAEWOULDBLOCK игнорируем, т.к. она говорит // только о том, что входной буфер сокета пуст, но в целом // всё в порядке - такое вполне возможно при ложных // срабатываниях сообщения if WSAGetLastError <> WSAEWOULDBLOCK then begin logging('Ошибка при получении данных от клиента ' + Connection.ClientAddr + ': ' + GetErrorstring); RemoveConnection; Exit; end; end else if Connection.Phase = tpReceivestring then begin // Следующий этап - чтение строки. Он практически не отличается // по реализации от этапа чтения длины строки, за исключением // того, что теперь буфером, куда помещаются полученные от клиента // данные, служит не Connection.MsgSize, а Connection.Msg. Res := recv(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1], Connection.BytesLeft, 0); if Res > 0 then begin Inc(Connection.Offset, Res); Dec(Connection.BytesLeft, Res); // Если количество оставшихся байт равно нулю, можно переходить // к следующему этапу. if Connection.BytesLeft = 0 then begin Logging('От клиента ' + Connection.ClientAddr + ' получена строка: ' + Connection.Msg); // Преобразуем строку. В отличие от предыдущих примеров, здесь // мы явно добавляем к строке #0. Это связано с тем, что при // отправке, которая тоже может быть выполнена не за один раз, // мы указываем индекс того символа строки, начиная с которого // нужно отправлять данные. И (хотя теоретически вероятность // этого очень мала) может возникнуть ситуация, когда за // один раз будут отправлены все символы строки, кроме // завершающего #0, и тогда при следующей отправке начинать // придётся с него. Если мы будем использовать тот #0, который // добавляется к концу строки автоматически, то в этом случае // индекс выйдет за пределы диапазона. Поэтому мы вручную // добавляем ещё один #0 к строке, чтобы он стал законной // её частью. Connection.Msg := AnsiUpperCase(stringReplace(Connection.Msg, #0, '#0', [rfReplaceAll])) + ' (AsyncSelect server)'#0; // Следующий этап - отправка строки клиенту Connection.Phase := tpSendstring; // Отправлено на этом этапе 0 байт Connection.Offset := 0; // Осталось отправить Length(Connection.Msg) байт. // Единицу к длине строки, в отличие от предыдущих примеров, // не добавляем, т.к. там эта единица нужна была для того, // чтобы учесть добавляемый к строке автоматически символ #0. // Здесь мы ещё один #0 добавили к строке явно, поэтому // он уже учтён в функции Length. Connection.BytesLeft := Length(Connection.Msg); // Ставим в очередь сообщение с событием FW_WRITE. // Его получение заставит сервер отправить данные PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_WRITE); end; end else if Res = 0 then begin Logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение'); RemoveConnection; Exit; end else // Как обычно, "ошибку" WSAEWOULDBLOCK просто игнорируем if WSAGetLastError <> WSAEWOULDBLOCK then begin Logging('Ошибка при получении данных от клиента ' + Connection.ClientAddr + ': ' + GetErrorstring); RemoveConnection; Exit; end; end else if Connection.Phase = tpSendstring then // Если сервер находится на этапе отправки данных, // а событие FD_READ всё же произошло, отмечаем это Connection.SendRead := True; end; FD_WRITE: begin if Connection.Phase = tpSendstring then begin // При наступлении события FD_WRITE проверяем, находится ли // сервер на этапе отправки данных, и если да, отправляем их Res := send(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1], Connection.BytesLeft, 0); if Res > 0 then begin Inc(Connection.Offset, Res); Dec(Connection.BytesLeft, Res); // Если Connections.BytesLeft = 0, значит, строка отправлена // полностью. if Connection.BytesLeft = 0 then begin Logging('Клиенту ' + Connection.ClientAddr + ' отправлена строка: ' + Connection.Msg); // Очищаем строку просто чтобы сэкономить память Connection.Msg := ''; // Следующий этап - снова получение длины строки от клиента Connection.Phase := tpReceiveLength; // Получено - 0 байт Connection.Offset := 0; // Осталось прочитать столько, сколько занимает целое число Connection.BytesLeft := SizeOf(Integer); // Если были промежуточные события FD_READ, вызываем их // снова искусственно if Connection.SendRead then begin PostMessage(Handle, WM_SOCKETMESSAGE, Msg.Socket, FD_READ); Connection.SendRead := False; end; end; end else if WSAGetLastError <> WSAEWOULDBLOCK then begin Logging('Ошибка при отправке данных клиенту ' + Connection.ClientAddr + ': ' + GetErrorstring); RemoveConnection; Exit; end; end; end; FD_CLOSE: begin // Клиент вызвал функцию shutdown. Закрываем соединение. Logging('Клиент ' + Connection.ClientAddr + ' закрыл соединение'); shutdown(Connection.ClientSocket, SD_BOTH); RemoveConnection; end; else begin Logging('Неверное событие при обмене с клиентом ' + Connection.ClientAddr); RemoveConnection; end; end; end; end. Это сообщение отредактировал(а) Antony41 - 18.4.2013, 17:12 |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Antony41, это однопоточный. Задержка в обработке одного соединения приведет к временной блокировке всех остальных.
Сколько??? -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
думаю одновременно 3-4 подключения (пока) обработка данных будет занимать минимум времени, короче клиент выполняет свою задачу локально, а подключается к серверу "лицензий" для того чтобы посмотреть в каком состоянии его лицензия
по сути сервер будет отсылать всего один пакет, который скажет что всё ОК или нет. |
|||
|
||||
| Чучмек |
|
|||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Я в какой то момент подумал, что счет пойдет на тысячи постоянных соединений. Создавать отдельное окно на каждое соединение? Все можно обработать одним. -------------------- умную мысль держи при себе, а дурной - поделись с другими |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ок с этим разобрались код который выше я привел, впринципе работает нормально, но там происходит при нескольких подключениях OutOfMemory, немогу отловить... еще выдается исключение InvalidPointer при уничтожении подкласса Connection точнее тут
тоже не могу понять, если убрать Connection^.Destroy; то всё ОК, остается только OutOfMemory Добавлено через 24 секунды помогите найти пож. время поджимает |
|||
|
||||
| Чучмек |
|
||||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Ищи где утечка памяти. Где память выделяется но не освобождается, объект создается но не уничтожается.
Какой длинны передаешь строки?
Это лишнее. Объект и так указатель. Достаточно
Это сообщение отредактировал(а) Чучмек - 19.4.2013, 15:41 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
||||
|
|||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
||||
|
||||
| Чучмек |
|
||||
|
НЭТ БИЛЭТ ![]() ![]() Профиль Группа: Участник Сообщений: 841 Регистрация: 11.5.2008 Где: СССР Репутация: 4 Всего: 41 |
Подожди, запустил сервер, открыл браузер, ввел адрес сервера и нажал enter? А сервер первым делом ждет длину строки? Браузер передает
G.E.T."пробел" 0x47,0x45,0x54,0x20 Или 0x20544547 Или 542393671 Пол га для ansistring Четыре запроса положат сервер. Это сообщение отредактировал(а) Чучмек - 19.4.2013, 16:51 -------------------- умную мысль держи при себе, а дурной - поделись с другими |
||||
|
|||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
щас перепишу клиента чтоб все верно передавал, да и еще какой максимальный размер пакета может быть для передачи/получения
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
не могу понять где ошибка 3 пакета уходит потом вылетает ошибка по адресу...
вот так клиент отправляет данные
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
разобрался но так и не понял
ошибка возникала из за того что я пытаюсь показать сколько сейчас соединений в TList при событии FD_CLOSE, вот так вроде гуд... но стоит поставить после RemoveConnection и ошибка по адресу...
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
как можно проверить состояние асинхронного сокета например если был сбой, и событие об отключении не было, т. е сокет потерян, как проверить?
|
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
Ладно ребят спасибо всем огромное при огромное... Вроде пока всё корректно работает.
Еще подскажите пожалуйста. Если у клиента прописан прокси, от куда будут браться настройки с IE? и как назначить connect + PROXY вручную |
|||
|
||||
| Antony41 |
|
|||
|
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 332 Регистрация: 27.12.2008 Репутация: нет Всего: 1 |
ответит кто или нет?
|
|||
|
||||
| MetalFan |
|
|||
![]() Аццкий Сотона ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 3815 Регистрация: 2.10.2006 Где: Moscow Репутация: 14 Всего: 128 |
работу с проксей, афаик, если юзаешь сокеты, придется вручную реализовывать...
возможно ошибаюсь. -------------------- There are always someone smarter than you... |
|||
|
||||
![]()
|
| Правила форума "Delphi: Сети" | |
|
|
Запрещено: 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делится вскрытыми компонентами
Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Snowy, Poseidon, MetalFan. |
| 0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) | |
| 0 Пользователей: | |
| « Предыдущая тема | Delphi: Сети | Следующая тема » |
|
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |