Комментарии 18
Однако Windows не работает с сокетами так же, как с файлами
Конечно же работает. В Windows по-умлочанию для сокета возвращается точно такой же дескриптор ядра.
Надо понимать две вещи:
в Linux вызов write это системный вызов. В Windows системным вызовом будет ZwWriteFile, на худой конец его win32 обёртка WrteFile.
в Windiws существует концепция Layered Socket Providers (LSP). Объединяющиеся в цепочки, через которые последовательно проходят вызовы сетевого API. Базовый провайдер возвращает описатели. Но вполне возможна ситуация, когда сторонний провайдер в цепочке решит вернуть псевдо-описатель, вместо настоящего, ядерного. И будет перекодировать их "на лету". Разумеется, с таким псевдо-описателем работать напрямую уже не получится, только проходя через всю цепочку. Выходом может быть просмотр цепочки и работа с нижним уровнем, минуя все установленные фильтры.
Далее, select это тормозно. Для производительных приложений надо использовать epoll в ,Linux и completion ports в windiows.
Если вы хотите загружать код без использования динамического связывания,
Очень плохой совет. Внезапное появление executable памяти в процессе это красная тряпка для средств защиты. Кроме того, приложению может быть вообще запрещено самому выделять исполняемую память (и/или модифицировать атрибуты страниц). Например SELinux отлично различает кейсы по загрузке динамических библиотек, выделению исполняемой памяти, изменению атрибутов страниц, причем для стека отдельно.
Вот, что ещё нужно знать о сокетах:
Если recv() не возвращает ошибку, и соединение выглядит как живое, это ещё не значит, что оно живое. Ни на какие системные таймауты полагаться нельзя. Нужно самостоятельно слать "пинги" и считать соединение потерянным, если данные не приходят дольше определённого порога времени.
Если одна сторона закрывает соединение, это не значит, что другая сторона об этом узнает. То есть реально может произойти так, что одна сторона корректно полностью завершила соединение, закрыла сокет, всё что нужно, а другая сторона при этом считает, что соединение ещё живо, сколь угодно долгое время. Опять же, тут надежда только на "пинги".
Если вы посылаете набор байтов, то на другую сторону они придут не одним куском, а иногда будут разбиты на куски произвольного размера. Всегда стоит рассчитывать на получение только части данных за один recv().
Ну, системные таймауты (по крайней мере в Linux) - вещь вполне себе рабочая и полагаться на них вполне можно. Надо только понимать что информация об ошибке к вам спокойно может прийти только через 5 минут или даже более, это - вполне нормальные величины по умолчанию, и такие оповещения - не самые приоритетные. Если для вашего применения это неприемлемо но автоматический контроль целостности установленного соединения важен - тогда да, действительно надо это все иметь в самом вашем протоколе (том что будет поверх TCP) и учитывать. В большинстве же простых случаев это не обязательно.
Еще стоит упопянуть особенность TCP которая не очевидна новичкам. Количество вызовов send() и recv() могут не совпадать.
Два коротких send() можно вычитать одним recv(), и наоборот большой send() придется вычитывать несколькими recv().
ошибочно посчитал, что, например, send() блокируется и возвращает успешное выполнение только после успешного получения сообщения на стороне recv()
Зависит от типа сокета - синхронный или асинхронный.
Синхронный вполне себе блокирует.
Уже начинают напрягать такие статьи. То, что send() не гарантирует recv() так же очевидно, как и то, что если я отправлю посылку почтой России, то не факт, что на том конце её примут. Ну то есть тут не просто не нужно быть "опытным сетевым программистом", а достаточно всего лишь дружить с логикой. Если это неочевидно, то, возможно, стоит рассмотреть другие области программирования.
Зашёл почитать про LGA 1700
:(
Не совсем понятно почему приложение не знает когда ему прочитать из сокета? Можно просто в бекгрунд очереди запустить цикл, который ждёт чтение из сокета, вот простейший класс с этой идеей, который читает с клавиатуры, отправляет на сокет, читает из сокета - выводит в терминал.
public struct ChatClientString {
public init() {}
public func work() {
print("Представьтесь:")
let message = readLine()
let name = message ?? "User"
do {
let dog = try Leash()
let queue = DispatchQueue(label: "keyboard listen")
queue.async {
keyinput: while true {
let message = readLine()
guard let line = message else { continue keyinput }
do { try dog.clientSocket.write(from: name + ": " + line) }
catch let error { print(error) }
}
}
while true {
let incom = try dog.clientSocket.readString()
print("from sever: \(incom ?? "")")
}
} catch let error {
print(error)
}
}
}
Фундаментальная задача, которую я пытался решить, — это чистая обработка нарушения связности сети, то есть когда машина А и машина Б оказываются полностью разъединены.
Для решения такой задачи лучше использовать UDP. И ещё в ARP может понадобиться немножко залезть.
Такую
send()
можно написать, и она будет гарантировать на уровне приложения, что сообщения точно попадут к получающему приложению, и что они считаются получающим приложением. Однако скорость взаимодействия приложения существенно упадёт, потому что каждый вызовsend()
будет заставлять приложение ожидать подтверждения того, что другое приложение получило сообщение.
Этого недостаточно, потому что потеряться может само подтверждение. Тут нужен двухфазный коммит.
Однако Windows не работает с сокетами так же, как с файлами. Если вы хотите использовать нативные Windows API, то нужно применять специализированные функции сокетов: send(), recv(), closesocket() и так далее.
Странные какие-то вещи вы пишете. Потому что все поставляемые MS провайдеры базовых сервисов (TCP, UDP, RAW для IP и IPv6) используют в качестве описателя (descriptor) сокета Handle файловой системы (IFS), то есть для системы эти сокеты — файлы, и с ними можно работать как с файлами, используя системные функции типа ReadFile/WriteFile. Это легко видеть, если посмотреть в их записях каталога (командой netsh Winsock show catalog) содержимое поля Служебные флаги: оно содержит флаг XP1_IFS_HANDLES (0x00020000)
а в винде есть какая нибудь функция, которая ждёт что в файл что нибудь запишут, чтобы тут же прочитать, по типу let i = readLine() с клавиатуры, только из файла?
Сетевое программирование - это целая область, со своими "механиками". Новичкам может показаться, что мол "я сейчас возьму либы и примеры и начну слать байты по сети, а обо всем остальном позаботятся высокоуровневые интерфейсы, OSI и т.д.". Но суровая действительность такова, что Pipe - это не просто абстрактное название. Это почти буквально труба(трубопровод) и никто не знает, что там с этим трубопроводом происходит на пути TCP/UDB пакета. Он может оборваться и ни клиент ни сервер об этом не узнают "автоматически". Поэтому иногда к сетевому программированию (особенно для подвижных средств связи с изменяемой пропускной способностью канала) я подхожу как к программированию UART/RS232/485.
Чего вам не говорили про сокеты