Затем сервер отвечает на этот номер порта . Да, это необычно
В этом нет ничего необычного. Вот как большинство серверов UDP должны работать. Они всегда отвечают на порт отправителя. Они не имеют понятия, является ли этот порт фиксированным или эфемерным, и это должен решать отправитель. Если конкретный протокол не предписывает отправлять ответы на другой порт, что не является распространенным явлением.
Все мои поиски в Интернете указывают на использование bind()
Правильно, это то, что вам нужно в этой ситуации.
но этот код обычно находится на стороне сервера.
Ничто не мешает клиенту использовать bind()
.
Я не нашел способа привязки на стороне клиента, используя современный метод getaddrinfo()
.
Это то же самое, что и на стороне сервера, за исключением того, что вам нужно привязать к определенному IP-адресу, вы не можете привязать к 0.0.0.0
или ::0
, как вы можете с помощью сокета сервера.
Я пытался добавить bind()
сразу после установки socket()
, но это не сработало
Да, это так. Проблема в том, что вы используете IP-адрес SAME для привязки и отправки, и это не будет работать. Вам нужно привязаться к IP-адресу *1040* КЛИЕНТА и затем отправить на IP-адрес SERVER.
, поскольку p
- это структура на стороне сервера (полученная из структуры hints
, которая использует IP-адрес сервера)
Вы неправильно используете p
. Вы не можете bind()
клиентский сокет с IP-адресом сервера (для этого вам нужно использовать connect()
). Вам необходимо bind()
клиентский сокет с IP-адресом, локальным для компьютера клиента. Точно так же, как вам нужно bind()
сокет сервера для IP-адреса, который является локальным для сервера сервера.
Помните, что сокет связан с парой IP-адресов. bind()
устанавливает сокет LOCAL IP-адрес. connect()
устанавливает сокет REMOTE IP-адрес.
Я хочу сделать это так, чтобы это работало как для IPv4, так и для IPv6.
Невозможно создать один клиентский сокет для обоих протоколов. Для каждого протокола требуются отдельные сокеты (на стороне сервера вы можете создать один сокет для обоих протоколов, если ваша платформа поддерживает сокеты с двумя стеками).
Я видел другие примеры, когда локальная / исходная структура sockaddr_in
заполняется информацией о клиенте и используется в привязке, но они специфичны для IPv4 или IPv6.
Да, поскольку вы будете отправлять пакет, используя ЛИБО IPv4 ИЛИ IPv6, вы не можете отправлять пакет, используя оба протокола одновременно (сокет с двумя стеками) может получать пакеты от любого протокола, хотя).
Может кто-нибудь показать мне, как правильно обновить код talker.c
на sendto()
и recvfrom()
сервер UDP, используя фиксированный номер порта источника. Предположим, что сервер является неизменным
Попробуйте что-то вроде этого:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdbool.h>
#define LOCALPORT "4950" // the port users will be sending from
#define SERVERPORT "4950" // the port users will be connecting to
#define MAXBUFLEN 65535
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *myinfo, *servinfo, *pserv, *plocal;
int rv;
int numbytes;
char buf[MAXBUFLEN];
char ipstr[INET6_ADDRSTRLEN];
fd_set readfds;
struct timeval tv;
bool stop = false;
if (argc < 3) {
fprintf(stderr, "usage: talker destaddr message [localaddr]\n");
return 1;
}
// get all of the server addresses
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 2;
}
// loop through all the server addresses
for(pserv = servinfo; (pserv != NULL) && (!stop); pserv = pserv->ai_next) {
memset(ipstr, 0, sizeof(ipstr));
switch (pserv->ai_family)
{
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)pserv->ai_addr)->sin_addr), ipstr, INET_ADDRSTRLEN);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)pserv->ai_addr)->sin6_addr), ipstr, INET6_ADDRSTRLEN);
break;
}
printf("talker: trying to send message to %s\n", ipstr);
// get all of the matching local addresses
memset(&hints, 0, sizeof hints);
hints.ai_family = pserv->ai_family;
hints.ai_socktype = pserv->ai_socktype;
hints.ai_protocol = pserv->ai_protocol;
if ((rv = getaddrinfo(argc > 3 ? argv[3] : NULL, LOCALPORT, &hints, &myinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
continue;
}
// loop through all the local addresses, sending the
// message from each one until a reply is received
for(plocal = myinfo; (plocal != NULL) && (!stop); plocal = plocal->ai_next) {
if ((sockfd = socket(plocal->ai_family, plocal->ai_socktype, plocal->ai_protocol)) == -1) {
perror("socket");
continue;
}
memset(ipstr, 0, sizeof(ipstr));
switch (plocal->ai_family)
{
case AF_INET:
inet_ntop(AF_INET, &(((struct sockaddr_in*)plocal->ai_addr)->sin_addr), ipstr, INET_ADDRSTRLEN);
break;
case AF_INET6:
inet_ntop(AF_INET6, &(((struct sockaddr_in6*)plocal->ai_addr)->sin6_addr), ipstr, INET6_ADDRSTRLEN);
break;
}
printf("talker: binding to %s\n", ipstr);
if (bind(sockfd, plocal->ai_addr, plocal->ai_addrlen) == -1) {
perror("bind");
close(sockfd);
continue;
}
// make sure this server address is the only one we talk to
if (connect(sockfd, pserv->ai_addr, pserv->ai_addrlen) == -1) {
perror("connect");
close(sockfd);
continue;
}
if ((numbytes = send(sockfd, argv[2], strlen(argv[2]), 0)) == -1) {
perror("send");
close(sockfd);
continue;
}
printf("talker: sent %d bytes\n", numbytes);
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
tv.tv_sec = 5;
tv.tv_usec = 0;
rv = select(sockfd+1, &readfds, NULL, NULL, &tv);
if (rv == -1)
{
perror("select");
close(sockfd);
continue;
}
if (rv == 0)
{
printf("talker: no reply for 5 seconds\n");
close(sockfd);
continue;
}
if ((numbytes = recv(sockfd, buf, MAXBUFLEN, 0)) == -1)
{
perror("recv");
close(sockfd);
continue;
}
printf("talker: received %d bytes\n", numbytes);
close(sockfd);
stop = true;
break;
}
freeaddrinfo(myinfo);
}
freeaddrinfo(servinfo);
close(sockfd);
if (!stop) {
fprintf(stderr, "talker: failed to communicate with server\n");
return 3;
}
return 0;
}