Эти две системные функции предназначены для общения (обмена данными) через сокеты. Если вам нужны несоединенные сокеты датаграм, то нужно использовать: sendto() и recvfrom().
send()иrecv()являются блокирующими функциями, пока не пройздет определенное конечное событие, например: получение данных, запись данных. Это значит, что программа будет "спать" и дальше этого вызова не двинется. Как это обработать, будет описано в дальнейшем.
Сигнатура:
int send(int sockfd, const void* msg, int len, int flags);Параметры:
-
int sockfd- дескриптор сокета, которому отправляем. -
const void* msg- указатель на данные, которые вы хотите отправить. -
int len- длина этих данных в байтах. -
int flags- дополнительные флаги для работы функции, можно оставить0. (смотрите manpages про функциюsend(), чтобы узнать больше про доступные флаги).
Функция возвращает количество отправленных байтов. Это количество может быть меньше, чем вы намеривались отправить!
Пример кода:
char* msg = "intfloabool был здесь!";
int len = sizeof(msg);
int bytes_sent = send(sockfd, msg, len, 0)Если количество отправленных байтов int bytes_sent меньше чем int len, то в вашей ответственности заняться переотправкой оставшихся байтов.
Если пакет данных размером меньше 1 тысячи байт, то скорее всего он будет отправлен целиком
В случае ошибки возвращается -1 и выставляется errno, и можно вывести ее с помощью вызова
perror().
Сигнатура:
int recv(int sockfd, void* buf, int len, int flags);Параметры:
-
int sockfd- дескриптор сокета, из которого получаем данные. -
void* buf- буфер, куда данные записываются. -
int len- максимальная длина буфера. -
int flags- дополнительные флаги для работы функции, можно оставить0. (смотрите manpages про функциюrecv(), чтобы узнать больше про доступные флаги).
recv() возвращает количество прочитанных байтов или -1 в случае ошибки (соответственно, выставляя errrno).
Еще recv() может вернуть 0, что означает - удаленная сторона закрыла с вами соединение.
Поскольку сокеты датаграмм не подключенны к удаленному хосту, то им нужно передать адрес назначения.
Сигнатура функции:
int sendto(int sockfd, const void* msg, int len, unsigned int flags, const struct sockaddr* to, socklen_t tolen);Как можно увидеть, параметры очень похожи на функцию send(), но имеют два дополнительных:
struct sockaddr* to- содержит адрес назначения и порт.socklen_t tolen- длина адреса в байтах. Можно передатьsizeof(struct sockaddr_storage)
Также как и send(), функция sendto() возвращает количество отправленных байтов (что также может быть меньше чем len !), и -1 в случае ошибки.
Сигнатура функции:
int recvfrom(int sockfd, void* buf, int len, unsigned int flags, struct sockaddr* from, int* fromlen);Она также очень похожа на recv(), но имеет два дополнительных параметра:
-
struct sockaddr* from- заполненный адрес и порт устройства, с которого получаем данные. -
int* fromlen- указатель на локальныйint, должен иметь значениеsizeof(*from)илиsizeof(struct sockaddr_storage)
По окончанию работы функции, fromlen будет содержать длину адреса from.
recvfrom() возвращает количество полученных байтов или -1 при возникновению ошибки (и errrno с указанием ошибки).
Мы используем struct sockaddr_storage во всех вычислениях адреса, вместо struct sockaddr_in и struct sockaddr_in6, чтобы не зависеть от конкретной версии IP протокола - IPv4 и IPv6. struct sockaddr_storage достаточно вместительный для обеих версий протокола.
Запомните, если вы используйте connect() для сокета датаграмм, вы можете продолжать использовать recv() и send() для своих транзакций. Сокет сам по себе остается UDP, а внутреннее устройство доставки пакетов автоматически подстроится под этот протокол.
В конце работы нужно закрывать файловый дескриптор сокета, через системный вызов close():
int close(int socketfd);Закрытие дескриптора, предостережет дальнейшие чтение и запись к сокету, выдавая ошибку.
Для большего контроля, можно использовать функцию shutdown():
int shutdown(int sockfd, int how);-
int howуказывает поведение:0- останавливает дальнейшее получение данных.1- останавливает дальнейшую отправку данных.2- останавливает всё, подобноclose().
shutdown() как и close() возвращает 0 при успехе и -1 в случае ошибки (и записывает errno).
shutdown()не закрывает дескриптор сокета, а меняет его поведение. Для закрытия все-равно нужно использоватьclose()в конце использования.