| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Сети > TSocketServer\TSocketClient и интернет |
| Автор: OXOTHUK 7.6.2007, 20:29 |
| Как подсоединить сервера и клиента через интернет(У сервера фиксированный IP). У мня чтобы я ни писал в поле Adress, какой бы IP не вводил, всё равно не хочет коннектится. ЗЫ если есть более удобные/легко осваиваемые способы прередачи record'а через интернет, то скажите, желательно с примером. |
| Автор: Snowy 7.6.2007, 20:36 |
| Отладь сначала на одной машине. Если там будет работать, значит проблемы на другом уровне. |
| Автор: OXOTHUK 7.6.2007, 20:58 |
| на одной машине работает, даже если вводить бурду в адрес. А какие могут быть проблемы? |
| Автор: Snowy 7.6.2007, 21:20 |
| Значит ты что-то напутал с адресом. Не может запрос пойти на "бурду". Добавлено через 24 секунды Обычные. Файрволы, закрытые порты... |
| Автор: Snowy 7.6.2007, 22:58 | ||
Если ты прописал asdfga, а законнетилось на локальный, значит нифига ты не прописал. Значит где-то ошибка, и твоё asdfga не играет никакой роли. Это не сокет, а код ошибки. Вбей его в гугль - сразу найдёшь, что она значит. |
| Автор: OXOTHUK 7.6.2007, 23:30 | ||||
Ну что тут может быть не так? |
| Автор: misha_mike 8.6.2007, 16:06 | ||||
| Во-первых я что-то не вижу чтобы ты порт указал. А во-вторых используй свойство Host. А вот это излишество:
Достаточно только FormDestroy. И тут поосторожней:
Никто не гарантирует что на приемной стороне твоя структура прийдет целиком а не парой-тройкой кусочков последовательно (и чем больше твоя структура и чем медленнее канал тем больше вероятность ее разрыва, а начиная с нескольких килобайт размера разрываться она будет гарантированно). Также никто не гарантирует что два (или больше) последовательных SendBuf(...) небольшого размера приведут к генерации соответствующего количества событий OnRead на приемной стороне, а не слипнуться в один кусок. |
| Автор: OXOTHUK 8.6.2007, 16:07 |
| misha_mike, порт указан в изначальных настройках, которые на обжект инспекторе. АПД: Сам допёр, спосебо всем. |
| Автор: OXOTHUK 10.6.2007, 09:53 | ||
Нозникла новая проблема: как передать такую струкруру:
Через SendBuf естесна не передаётся, но я попробывал сохранить в файл, а потом SendStream. Но получать приходится через RecieveBuf. И опять не доходит или что ещё. Есть какие-нибудь способы? ЗЫ на одной машине всё нормально передаётся |
| Автор: misha_mike 10.6.2007, 16:21 | ||
| Как не передается? Какую ошибку возвращает? Вообще для передачи больших блоков (более 2 килобайт) надо их нарезать на кусочки по одному-два килобайта, и передвать по кусочку пока не возникнет ошибка WSAEWOULDBLOCK. Потом ждать события OnWrite и повторять с места остановки до следующей ошибки. И так далее до конца буфера. Приходить она тоже будет кусками, причем совсем не обязательно такими, которыми ты ее нарезал при передаче, поэтому о сборе в единое целое тоже нужно позаботиться на приемной стороне. P.S. А связь с localhost всегда более дубовая чем с удаленной машиной, много подобных ньюансов не вылазят потому что при связи с самим собой стек TCP/IP может работать далеко не на всю свою глубину.... P.P.S. А вообще такие вещи по сети лучше не гонять, и вместо крайне избыточной структуры с COUNT-ом перегони сначала COUNT, а потом соотвествующее количество неизбыточных структур такого вида (и обрати внимание на слово packed):
А не то тебя юзера проклянут. |
| Автор: OXOTHUK 10.6.2007, 18:44 | ||||
никакую, там просто не возникает события OnRead. а чем отличается packed record от record? :lamo: Но вроде работает. АПД только не всегда доходит. вот серверная:
вот клиентская:
или можно как-то лучше реализовать? :lamo: я с сетями никогда не работал |
| Автор: misha_mike 10.6.2007, 20:14 | ||||||||||||||||
И OnError не возникает? Не может этого быть...
Тем что выравнивания нет и структура имеет именно такой размер, который имеют в сумме ее члены. В прочем об этом написано в любом учебнике и к сетям отношения не имеет.
Что такое АПД?
Не передавай без крайней необходимости числа в виде текста, это кроме того что избыточно, в случае вещественных чисел обязательно приведет к проблемам. Пиши Socket.Send(count_msg, SizeOf(count_msg)), а на приемной стороне соответственно Socket.ReceiveBuf(count_msg, SizeOf(count_msg)), но ты про это пока забудь и читай дальше.
Я же предупреждал, что так делать нельзя никогда. Никто не гарантирует что все что ты передал с одной стороны прийдет на другую именно так как ты ожидаешь. Во-первых рано или поздно случится так что в твой ClientRead прийдет count и три с половиной msg, а оставшиеся пять с половиной -- в следующем ClientRead, который совершенно не готов к такому повороту (не разобравшись в том что пришло, сразу читает Count и что-то там принимает). Во вторых очень бысто случится так, что в одном ClientRead прийдет то что ты передал в два или три захода! В первом случае ты получишь ошибку, а во втором просто проворонишь один или несколько пакетов. Еще раз поясняю, прочитай очень внимательно. Со стопроцентной вероятностью ты столкнешся с такими ситуациями: 1. Разрезание. ПЕРЕДАТЧИК: Вызываешь Socket.SendBuf(Data, 6000); ПРИЕМНИК: Возникает событие ClientRead, в котором Socket.ReceiveLength = 4096, и через некоторое время опять возникает событие ClientRead, в котором Socket.ReceiveLength = 1904. РЕЗЮМЕ: То что ты передал одним куском, пришло несколькими. 2. Склеивание. ПЕРЕДАТЧИК: Вызываешь Socket.SendText('текст'), и через некоторое время Socket.SendBuf(Data, 8). ПРИЕМНИК: Возникает одно единственное событие ClientRead, в котором Socket.ReceiveLength = 13 (сумма длин текста и данных). РЕЗЮМЕ: То что ты передал несколькими Send-ами пришло одним куском. 3. Смешивание. ПЕРЕДАТЧИК: Вызываешь Socket.SendText('текст'), и через некоторое время Socket.SendBuf(Data, 6000). ПРИЕМНИК: Возникает событие ClientRead, в котором Socket.ReceiveLength = 4096 и в этот размер входит переданный первым Send-ом 'текст' и первые 4091 байт из буфера Data, переданного вторым Send-ом, и через некоторое время опять возникает событие ClientRead с оставшимися 1909 байтами из буфера Data, переданного вторым Send-ом. РЕЗЮМЕ: То, как ты передаешь данные никак не связанно с тем, как они прийдут. Сохраняется только последовательность, но никак не блочность. 4. Переполнение буфера передачи WinSock. ПЕРЕДАТЧИК: Вызываешь Socket.SendBuf(Data, 100000), тут же возникает OnError, в котором ErrorEvent = eeSend. ПРИЕМНИК: Ничего не приходит. РЕЗЮМЕ: Передавать в один присет более нескольких килобайт нельзя, нужно разрезать большой пакет на тесколько маленьких (не более пары килобайт) и передвать их до тех пор пока не возникнет OnError (при этом ErrorEvent должен быть равен eeSend, иначе прерываем передачу и генерируем ошибку разрыва канала связи). После этого нужно остановиться, дождаться события OnWrite и только тогда продолжить до следующего OnError. И так пока не закончатся данные для передачи.
Первый вариант -- реализовываешь свой протокол верхнего уровня, в котором на предающей стороне перед полезными данными помещается некоторая структура-заголовок, включающая размер передаваемого блока, а потом все это будет передаваться по кускам согласно п. 4. На приемной стороне эта структура анализируется и производится накопление пришедших данных до тех пор пока их объем не достигнет указанной в заголовке длины. Также если объем принятых данных превышает размер остатка текущего блока, нужно инициализировать начало приема следующего блока. Второй вариант -- воспользоваться оним из существующих протоколов верхнего уровня, например IRC. Благо соответствующих компонент достаточно. |
| Автор: OXOTHUK 11.6.2007, 12:41 | ||||
сам вызов функции не проверял, но видимых ошибок нет никаких. UPD - updated, т.е. исправил пост и дописал.
тут мня возникает несколько вопросов: 1) чем принимать, т.е. каким типом? array of byte? 2) как склеивать\разрезать получившиеся части?
TInIRCServer/TInIRC подойдёт с вкладки Indy? Или есть что-то лучше? А есть какой-нибудь мануал как или пользоваться, а то они не похожи на сокеты. Желательно с примерами. ЗЫ. Дельфийская справка у меня не работает. |
| Автор: misha_mike 11.6.2007, 13:12 | ||||||||||
Блин, это же основы работы с "сырыми" данными. Без этого ничего сложнее калькулятора не написать, а ты сразу игрушки с мультиплеером ваять собрался... Принимать надо указателем на динамический блок, передавать им же. Динамические массивы конечно тоже сгодятся, но ты не на джаве и не в дотнете пишешь чтобы на них выезжать в любой ситуации. Как производится работа с динамической памятью написано в ЛЮБОМ учебнике по Паскалю. Указательную арифметику для нарезки/склейки пакетов имеет смысл показывать только поле того как подобные отквоченному вопросы возникать перестанут в принципе. А спрашивать об тривиальных операциях по извлечению подмассива и склеиванию нескольких динамических массивов вовсе должно быть стыдно!
Там примитив полнейший, да и не единственные это компоненты (и не единственный подходящий протокол).
Это не оправдание, либо чини либо вызывай ее руками. А основы языка (изложенные в любой книжке) рассказывать думаю тут желающих не много наберется. |
| Автор: OXOTHUK 11.6.2007, 14:03 | ||||||||
На с++ я себе это более менее представляю, но в дельфи у меня с этим труднее. Да и не игрушку я пишу, это для информационной панели, просто нужно чтобы данные отображались у всех.
Это невозможно, у мня Виста, а она категорически не поддерживает старые справки.
Какую лучше выбрать структуру? если она будет большая, как я понял, не факт что она вся придёт, а если 1-2 символа\числа, то может встретится и не структура.
Какие ещё, к примеру, есть протоколы высокого уровня? чтобы был выбор. |
| Автор: misha_mike 11.6.2007, 14:25 | ||||||||||||||
Тут точно так же, только кастовать указатель надо так:
Где
Кто ж заставлял? Да и в новых версиях Delphi идет новая справка, попробуй извлечь от-туда.
Правильно понял, поэтому по приходу данных проверяй размер, и если он меньше структуры -- то придерживай их в каком-нибудь буфере до следующего прихода и разбирайся только когда размер будет больше или равен размеру структуры. Но не забывай что после заголовка пойдут твои полезные данные и в них ничего такого уже искать не нужно пока не будет принята вся длина, указанная в заголовке.
Ну хоть HTTP, через прокси можно гонять |
| Автор: OXOTHUK 12.6.2007, 15:25 | ||||
| я вроде почти сделал, всё работало, но когда я добавил поддержку обратной связи через структуру TCMessage, но почему-то перестала работать, передаются данные только при первом OnКоннект. Потом ни байта, проверял через файрвол. Ни ошибки, ничего. Просто не возникает OnRead ни где. А вот если взять более старую версию, где обраная связь по средствам передачи одной сторки маленькой, то там всё ок. Вот прога: Серверная:
Клиентская:
ЗЫ не ругайте сильное, если написано абы как, я не гнался за производительностью и экономией памяти. |
| Автор: misha_mike 12.6.2007, 16:38 | ||||||||||||||||||||||||||||||||||||||
Значит так, я уже дважды писал про передачу данных, но ты не внял ;) В таком случае приведу цитату из хелпа, может тогда пронкнешся:
Внимательно причитай выделенное, а особенно -- подчеркнутое (OnError действительно не возникает, возвращается ошибка). Что касается последнего выделенного фрагмента, то я бы не советовал так делать (хотя уверен что и так проканает), и ждать не "a bit", а вполне конкретного события -- OnWrite/OnClientWrite и только тогда продолжать. Я бы делал так:
Не ручаюсь за полную синтаксическую правильность, но идея именно такая. Теперь вызывая SafeSend можно не опасаться что что-то не уйдет (но сильно все равно не налягать потому как память не резиновая и буфер передачи ее сожрет очень быстро). Теперь про твои художества:
А не проще ли вместо SendText('HEADER') делать SendText('HEADER'#0) и всю эту байду заменить на:
Да и вообще с такими сигнатурами надо поосторожнее, а ну как встретится это слово среди данных? Хотя дело хозяйское.
Логин должен быть нечувствителен к регистру, заменяем на:
Есть две замечательные функции: ReallocMem (изменяет размер выделенного блока с сохранением данных и по возможности без новых гетмемов) и MoveMemory (тоже что и Move, но можно не беспокоиться что буферы источника и приемника пересекаются в памяти).
Достаточно один раз (и из инспектора объектов, а не в коде):
или установи Form1.Constraints
Гораздо лучше так:
Но не забывай что если клиент отвалится, то соответствующий ему элемент твоего массива станет неактуальным и лишним. Поэтому обрабатывай OnClientDisconnect. P.S. Чего маешся вместо того чтобы IRC взять? |
| Автор: OXOTHUK 12.6.2007, 18:03 | ||||||||||||
Понял, но не думал что при предаче 1 кб могут быть такие проблемы.
У меня левую рамку можно двигать.
я их кол-во там уменьшаю. Он же сами сдвигаются? В общем, я что-то переделал. Сервер стал посылать, но клиент всё равно не хочет, он посылает, но у сервера не возникает OnRead. То, что получилось: Клиент
Сервер:
нормальный так и не нашёл ЗЫ на всякий случай http://ifolder.ru/2324208 http://ifolder.ru/2324222 |
| Автор: misha_mike 13.6.2007, 00:49 |
| Высылаю полностью рабочий семпл, написанный в согласии с тем что я тут говорил про передачу. Разбирайся и ищи свои ошибки сам, пригодится |
| Автор: OXOTHUK 13.6.2007, 12:31 | ||
| Огромный спасиб! Ошибка была в этом:
В закоментеном тексте, почему-то если его закоментить, на сервере перстаёт возникать онрид, хотя на сервере выполняются аналогичные действия и всё работает. |
| Автор: OXOTHUK 15.6.2007, 12:08 | ||
| Я отправляю информацию, но как только буфер winsock'а переполняется, т.к. я отправлю свои 8кб, то вся моя информация складируется в буфер и отправляется потом через OnWrite. Но на приёмной стороне, пока он не переполнился - всё хорошо, после 8кб, начинает приходить бурда. Посмотрите, пожалуйста, код сервера, я никак не могу найти ошибку. ЗЫ точно ошибка в сервере.
|
| Автор: misha_mike 15.6.2007, 13:53 | ||||
| Вот что бывает когда бездумно копируешь чужой код. Я допустил помарку, а кто-то ее слепо скопировал. Вместо:
Нужно:
И на обеих сторонах! P.S. В написанных мной тестовых приложениях этой ошибки, кстати, нет, так что внимательнее. |
| Автор: OXOTHUK 15.6.2007, 14:33 | ||
я сверялся, но такой маленький символ трудно заметить. А можно ещё вопрос? Что произойдёт если связь с клиентов оборвётся, т.е. не будет OnDisconnect. Вылезет ошибка, а что будет с массивом Server.Socket.Connections? |
| Автор: misha_mike 15.6.2007, 15:40 | ||||||||
Дело не в значительности/заметности символа, а в том что он делает. Даже при беглом изучании этой строчки становится ясно что что-то не так. Без этого символа на передачу уходило не содержимое буфера, а значение указателя и следом за ним еще (Size-4) байт из сегмента данных (другие переменные или просто мусор). В одном случае это вызывает просто появление непонятных данных, в ряде других ведет к Access Violation.
Это смотря как оборвется. Если упадет приложение, то OnDosconnect на другой стороне возникнет. Если отвалится сетевой интерфейс -- аналогично (но только на том хосте, где эта неприятность и случилась). Не произойдет события если связь разорвется не непосредственно на сетевом интерфейсе хоста, а где-нибудь по пути. Например если разрыв по причине длительного бездействия произведет один из шлюзов на пути канала.
Я когда столкнулся с проблемой "тихих" разрывов, просто реализовал на каждой стороне передачу раз в минуту специального короткого пакета, который на принимающей стороне просто игнорируется. Во-певых это не дает всяким шлюзам убить линк по тайм-ауту. Во-вторых, если "тихий" разрыв все-таки произошел -- при передаче такого idle-пакета немедленно возникает ошибка и сокет отключается. В случае серверного сокета это обозначает и удаление ячейки из Socket.Connections. Вот только возникает ли OnDisconnect, я не помню, надо пробовать. |
| Автор: OXOTHUK 15.6.2007, 15:48 |
просто у клиента отключится интернет, или если например убить клиент в процессах. Тогда приложение закрывается, а OnDestroy формы не вызывается, т.е. Client.Active:=false не происходит. |
| Автор: misha_mike 15.6.2007, 16:04 | ||
Перечитай мой последний пост еще раз. Не важно произойдет ли дестрой формы, при завершении приложения (в т.ч. и аварийном) система сама подчищает все занятые ресурсы и посылает уведомление другой стороне что канал закрыт. Если у клиета спонтанно отвалится интернет, то спасет только периодическое тормошение канала, как я и сказал. |