Отправка данных TCP и UDP
В предыдущих разделах мы объясняли, как обрабатывать входящий поток данных. Можно сказать, что работа шла по входящим данным. Иногда нужно прямо противоположное - выполнять операции в зависимости от отправки данных.
Отправка данных с помощью события on_sock_data_sent
Допустим, в некоторой системе при нажатии кнопки нужно отправить длинную строку данных. Простой код для этого выглядел бы так:
sub on_button_pressed
sock.setdata("This is a long string waiting to be sent. Send me already!")
sock.send
end sub
Этот код будет работать, но только если в момент его выполнения в буфере TX достаточно свободного места (иначе данные будут усечены). Поэтому перед отправкой нужно убедиться, что в буфере TX есть необходимый объём свободного места. Простое решение с опросом выглядело бы так:
sub on_button_pressed
dim s as string
s = "This is a long string waiting to be sent. Send me already!"
while sock.txfree < len(s)
wend
sock.setdata(s)
sock.send
end sub
И это снова не очень хорошо, поскольку блокируются другие обработчики событий. Поэтому вместо такого подхода мы используем код с событием on_sock_data_sent:
dim s as string
s = "This is a long string waiting to be sent. Send me already!"
sub on_button_pressed
sock.notifysent(sock.txbuffsize-len(s)) ' causes the on_sock_data_sent event to fire when the TX buffer has space for our string
end sub
sub on_sock_data_sent
sock.setdata(s) ' put data in TX buffer
sock.send ' start sending it.
end sub
При нажатии кнопки генерируется событие on_button_pressed, и система узнаёт, что нужно отправить строку. С помощью sock.notifysent мы заставляем систему сгенерировать событие on_sock_data_sent, когда освободится необходимый объём места. Это событие сработает только один раз - и сработает сразу же, если места уже достаточно.
В обработчике этого события мы помещаем данные в буфер TX и запускаем отправку.
Объём данных, при котором срабатывает on_sock_data_sent, не включает незафиксированные данные в буфере TX.
Датаграммы UDP формируются по мере их создания
В разделе Использование буферов в режиме UDP мы объясняли, что в UDP вы полностью контролируете, как отправляемые данные делятся на датаграммы UDP. Каждый вызов метода sock.send проводит границу между датаграммами: предыдущая «закрывается», а новая «начинается». Можно даже отправить пустую датаграмму UDP, выполнив sock.send без предварительного sock.setdata.
Правильный ответ отправителю каждой датаграммы UDP
В UDP ваш сокет может принимать датаграммы от нескольких разных хостов. При входе в обработчик события on_sock_data_arrival (см. Приём данных в режиме UDP) следующие свойства автоматически отражают источник текущей датаграммы: sock.remotemac, sock.remoteip и sock.remoteport. Кроме того, свойство sock.bcast показывает, была ли датаграмма обычной или широковещательной (этот материал уже рассматривался в разделе Проверка состояния соединения).
Кроме того, любая датаграмма, созданная и отправленная из обработчика события on_sock_data_arrival, отправляется отправителю той датаграммы, из-за которой был вызван этот обработчик.
Суть в том, что, отправляя датаграмму из обработчика события on_sock_data_arrival, вы автоматически отвечаете отправителю обрабатываемой датаграммы. Следующий пример в ответ на любую датаграмму, принятую сокетом, отправляет обратно строку «GOT DATAGRAM FROM xxx.xxx.xxx.xxx»:
sub on_sock_data_arrival
sock.setdata("GOT DATAGRAM FROM " + sock.remoteip)
sock.send
end sub
В этом примере даже если несколько хостов одновременно отправят датаграммы на сокет, каждый из них получит правильный ответ!
А теперь другой пример: при каждом нажатии кнопки формируется одно и то же сообщение.
sub on_button_pressed
sock.setdata("GOT DATAGRAM FROM " + sock.remoteip)
sock.send
end sub
Разница в том, что при нажатии кнопки датаграмма будет отправлена отправителю самой последней входящей датаграммы UDP, которую сокет принял (и одобрил)!