«Что меня всегда раздражало в библиотеке WS2 — так это способы возвращения ошибок, и чтения информации.»
Если вы не знали, хотел бы заметить, что такой способ обработки ошибок — это специфика WinAPI. В нем все (или большинство) функции возвращают коды ошибок, в том числе через HRESULT, 0, -1… Ну а GetLastError — это вовсе как раз системная функция созданная для того, чтобы понять что собственно произошло.
Собственно, переход на обработку ошибок через исключения — это одно из ключевых особенностей платформы .net. В момент выхода этот момент широко рекламировался, позиционировался как решение проблемы разнообразия и бардака с кодами ошибок.
* если вы это знали, то, надеюсь, информация пригодится тем, кто не знал
Действительно, если заниматься тем, что оборачивать всю программу в try catch — это уже не серьёзно. Ещё более несерьёзно — передавать сообщения исключениями (Такое тоже было, некоторые люди так выходили из десятков вложенных циклов.)
А если у нас по исключению рухнул удалённый сервер — можно уже не торопиться. Сервер лежит, скорость не важна 8-)
«Исключения — штука весьма медлительная» — при возникновении с вероятностью 1/100000 это не серьезно, а если у вас исключения 1/2 то значит что код кривой ))) хардом орехи не колим
«В итоге, работа с сокетами превратилась в песню» — буду критичен, как SDM заявляю что код отвратительный.
1. Цикломатичен
2. Смысла в try/catch никакого
3. Дублирование
4. Неверный
Замечания по статье:
.NET Socket в конечном итоге также вызывает функции winsock, с чего вы решили что ваша реализация лучше?
Для pinvoke в .NET есть замечательная функция — Marshal.GetLastWin32Error(), при получении нативной ошибки в pinvoke брать результат нужно именно ей, а не обёрткой над нативной GetLastError() :-)
В атрибуте DllImport есть параметр SetLastError. Пример из MSDN:
[DllImport(«user32.dll», SetLastError=true)]
public static extern int MessageBoxA(int hWnd, String text, String caption, uint type);
После вызова функции маршаллер вызовет winapi-функцию GetLastError и сохранит возвращённое значение. Потом его можно будет получить через Marshal.GetLastWin32Error.
MSDN говорит, что значение по умолчанию — false. Возможно, если оно false, то GetLastWin32Error будет не возвращать сохранённое значение, а вызывать GetLastError непосредственно.
> Ну, можно попытаться ускорить ваше приложение, используя вычисления на очень сложном и быстром Assembler'е.
Только предварительно прикинуть, покроет ли «сложный и быстрый ассемблер» затраты на P/Invoke и не создаст ли это дофига дополнительных проблем с разрядностью native-кода.
Пинвок — это технология межпрограммного взаимодействия. Через что именно она работает в милкоре — это ещё надо поискать 8-). Факт в том, что она там есть, хотя не факт, что на Дллиморте.
ПИнвок предполагает маршаллинг типов в процессе вызова, что в общем и приводит к потере производительности.
А вот при вызове managed -> unmanaged (или наоборот) из, скажем, C++/CLI, маршаллинг не применяется, а просто соблюдается соответствующая конвенция вызова.
Согласен по-поводу 99%. Мне редко вообще приходилось этим пользоваться. Например, необходимо было в OpenFileDalog отображать эскизы страниц, что оказалось не реализуемо средствами.нет, вот пришлось использовать Interop.
тут проблема в основном из-за маршалинга параметров и результата. к примеру строк или байтовых массивов, т.к. данные обычно копируются в/из манадж памяти. если дергаете void Method() то особых тормозов не будет :)
ну тогда пример плохой, так как это можно сделать уже существующими обертками.
Было супер имено посмотреть сравнение насколько быстро был написан код на том или на том. и на сколько быстро работает то или иное. а так статья получается просто про то как вызвать с/c++ библиотеку из .net
А вы молодец, хорошую статью написали. Сам помню так же велосипедили года 2 назад, когда у нас сети были :).
Только я потом узнал, что .Net sockets очень медленные, особенно когда они используются асинхронно. Посему если критична скорость, то лучше написать свою обёртку, только писать её нужно эээ… немножко… подругому ;).
Машинный код .NET и машинный код Native (Не-.NET) приложений это не одно и то же. Соответственно, выходит интересная штука: мы можем взять одно Native приложение, написанное на языке Assembler, и взять другое Native приложение, написанное я языке Pascal, и скрестить их вместе.
Как-то я не понял логический переход:) Можно пояснить?
Вот именно, что не обособленно:) У вас из первого выходит второе. Я сидел некоторое время втыкал, почему из того, что машинные коды не одно и то же следует связка ассемблера с паскалем :D
Что занчит машинный код .Net? IL? Так это не машинный код. Машинным он становится только после jit-компиляции.
Мы долго искали компромиссы с нашим преподом, и в итоге пришли к следующему: Нам позволяют использовать .NET при условии, что WS2 мы будем дёргать через DllImport.
Препод идиот, хотя в этом ничего удивительного. Как по его мнению System.Net.Sockets.Socket работает?
Эх были времена… Помню как мы уламывали препода на лабах, чтоб писать на С++, а не на фортране.
Очень интересная мысль про скрещивание с# и с. Вот предположим, есть железка — трекер, она программируется на с. Интересно, возможно ли написать управляющий код на c#, который компоновал бы инструкции С таким образом, чтобы получалась полноценная, работающая программа на с? Которую, в конечном итоге, можно было бы залить программатором в железку?
В конечном итоге — можно. Это вопрос того, что вы пишете свой компилятор на среде .NET. Фактически, вам надо находить какие-то логики кода и заменять их на последовательности байтов. Это просто делается с помощью конечных автоматов.
.NET Interop на примере работы с сокетами