使用 getaddrinfo 和绑定发送具有固定源端口号的 UDP 数据包
Send UDP packet with fixed source port number using getaddrinfo and bind
使用 BJ 的 talker.c 代码作为模板:
http://beej.us/guide/bgnet/examples/talker.c
#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>
#define SERVERPORT "4950" // the port users will be connecting to
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
socklen_t addr_len;
addr_len = sizeof their_addr;
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to create socket\n");
return 2;
}
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0,
p->ai_addr, p->ai_addrlen)) == -1) {
perror("talker: sendto");
exit(1);
}
freeaddrinfo(servinfo);
printf("talker: sent %d bytes to %s\n", numbytes, argv[1]);
//============== Added Code for recvfrom() (pseudocode-ish) =============
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN , 0, (struct sockaddr *)&their_addr, &addr_len)) == -1)
{
close(sockfd);
perror("talker: recvfrom");
exit(1);
}
close(sockfd);
printf("Got packet\n");
//============== End Added Code for recvfrom() =============
close(sockfd);
return 0;
}
我有一个要求,即与服务器对话的客户端 UDP 进程必须使用固定的、已知的 source
端口号。在这种情况下,假设它是 SERVERPORT
(4950)。然后服务器响应那个端口号。是的,这很不寻常,因为大多数服务器都会响应系统分配给发件人的临时端口号。
使用 sendto()
发送数据包后,我使用 recvfrom()
监听响应。那是我在上面的例子中添加的(伪)代码。
我所有的在线搜索都指向使用 bind()
,但该代码通常在服务器端。我还没有找到使用现代 getaddrinfo()
方法在客户端进行绑定的方法。我试图在 socket()
设置之后添加一个 bind()
但这不起作用,因为 p
是一个服务器端结构(派生自使用服务器 IP 地址的提示结构)我得到一个绑定错误:
Error 99 (Cannot assign requested address)
添加代码:
bind(sockfd, p->ai_addr, p->ai_addrlen)
我想以同时适用于 IPv4 和 IPv6 的方式执行此操作。
我见过其他示例,其中 local/source sockaddr_in 结构填充了客户端信息,并且 that 用于绑定,但是这些是特定于 IPv4 或 IPv6 的。
谁能告诉我如何使用固定的 源 将 talker.c
代码正确更新为 sendto()
和 recvfrom()
UDP 服务器端口号?假设服务器是不可变的。
The server then responds to that port number. Yes, this is unusual
这没什么不寻常的。这就是大多数 UDP 服务器的工作方式。他们总是响应发件人的端口。他们不知道该端口是固定的还是临时的,由发送者决定。除非特定协议规定将响应发送到不同的端口,这并不常见。
All my searches online point to using bind()
正确,这就是您在这种情况下所需要的。
but that code is usually on the server side.
没有什么可以阻止客户使用 bind()
。
I haven't found a way to bind on the client side using the modern getaddrinfo()
method.
它和服务器端完全一样,除了你必须绑定到一个特定的IP地址,你不能像你可以绑定到0.0.0.0
或::0
服务器套接字。
I tried to add a bind()
right after the socket()
setup but that wouldn't work
是的,确实如此。问题是您使用 SAME IP 地址进行绑定和发送,这是行不通的。您需要绑定到 CLIENT 的 IP 地址,然后发送到 SERVER 的 IP 地址。
because p
is a server-side structure (derived from the hints
structure that uses the server IP address)
您在滥用 p
。您不能 bind()
客户端套接字到服务器的 IP 地址(您需要使用 connect()
代替)。您需要 bind()
客户端套接字到客户端机器本地的 IP 地址。就像您必须 bind()
将服务器套接字连接到服务器计算机本地的 IP 地址一样。
请记住,套接字与一对 IP 地址相关联。 bind()
建立套接字的 LOCAL IP 地址。 connect()
建立套接字的 REMOTE IP 地址。
I want to do this in a way that will work for both IPv4 and IPv6.
您不能为这两种协议创建一个客户端套接字。每个协议都需要单独的套接字(在服务器端,如果您的平台支持双栈套接字,则可以为两种协议创建一个套接字)。
I've seen other examples whereby a local/source sockaddr_in
structure is filled out with the client's information and that is used in the bind, but those are IPv4 or IPv6 specific.
是的,因为您将使用 EITHER IPv4 OR IPv6 发送数据包,您不能使用两种协议发送数据包同时(双栈套接字可以接收来自任一协议的数据包)。
Can someone please show me how to properly update the talker.c
code to sendto()
and recvfrom()
a UDP server using a fixed source port number . Assume that the server is immutable
尝试这样的事情:
#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;
}
使用 BJ 的 talker.c 代码作为模板: http://beej.us/guide/bgnet/examples/talker.c
#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>
#define SERVERPORT "4950" // the port users will be connecting to
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
socklen_t addr_len;
addr_len = sizeof their_addr;
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to create socket\n");
return 2;
}
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0,
p->ai_addr, p->ai_addrlen)) == -1) {
perror("talker: sendto");
exit(1);
}
freeaddrinfo(servinfo);
printf("talker: sent %d bytes to %s\n", numbytes, argv[1]);
//============== Added Code for recvfrom() (pseudocode-ish) =============
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN , 0, (struct sockaddr *)&their_addr, &addr_len)) == -1)
{
close(sockfd);
perror("talker: recvfrom");
exit(1);
}
close(sockfd);
printf("Got packet\n");
//============== End Added Code for recvfrom() =============
close(sockfd);
return 0;
}
我有一个要求,即与服务器对话的客户端 UDP 进程必须使用固定的、已知的 source
端口号。在这种情况下,假设它是 SERVERPORT
(4950)。然后服务器响应那个端口号。是的,这很不寻常,因为大多数服务器都会响应系统分配给发件人的临时端口号。
使用 sendto()
发送数据包后,我使用 recvfrom()
监听响应。那是我在上面的例子中添加的(伪)代码。
我所有的在线搜索都指向使用 bind()
,但该代码通常在服务器端。我还没有找到使用现代 getaddrinfo()
方法在客户端进行绑定的方法。我试图在 socket()
设置之后添加一个 bind()
但这不起作用,因为 p
是一个服务器端结构(派生自使用服务器 IP 地址的提示结构)我得到一个绑定错误:
Error 99 (Cannot assign requested address)
添加代码:
bind(sockfd, p->ai_addr, p->ai_addrlen)
我想以同时适用于 IPv4 和 IPv6 的方式执行此操作。
我见过其他示例,其中 local/source sockaddr_in 结构填充了客户端信息,并且 that 用于绑定,但是这些是特定于 IPv4 或 IPv6 的。
谁能告诉我如何使用固定的 源 将 talker.c
代码正确更新为 sendto()
和 recvfrom()
UDP 服务器端口号?假设服务器是不可变的。
The server then responds to that port number. Yes, this is unusual
这没什么不寻常的。这就是大多数 UDP 服务器的工作方式。他们总是响应发件人的端口。他们不知道该端口是固定的还是临时的,由发送者决定。除非特定协议规定将响应发送到不同的端口,这并不常见。
All my searches online point to using
bind()
正确,这就是您在这种情况下所需要的。
but that code is usually on the server side.
没有什么可以阻止客户使用 bind()
。
I haven't found a way to bind on the client side using the modern
getaddrinfo()
method.
它和服务器端完全一样,除了你必须绑定到一个特定的IP地址,你不能像你可以绑定到0.0.0.0
或::0
服务器套接字。
I tried to add a
bind()
right after thesocket()
setup but that wouldn't work
是的,确实如此。问题是您使用 SAME IP 地址进行绑定和发送,这是行不通的。您需要绑定到 CLIENT 的 IP 地址,然后发送到 SERVER 的 IP 地址。
because
p
is a server-side structure (derived from thehints
structure that uses the server IP address)
您在滥用 p
。您不能 bind()
客户端套接字到服务器的 IP 地址(您需要使用 connect()
代替)。您需要 bind()
客户端套接字到客户端机器本地的 IP 地址。就像您必须 bind()
将服务器套接字连接到服务器计算机本地的 IP 地址一样。
请记住,套接字与一对 IP 地址相关联。 bind()
建立套接字的 LOCAL IP 地址。 connect()
建立套接字的 REMOTE IP 地址。
I want to do this in a way that will work for both IPv4 and IPv6.
您不能为这两种协议创建一个客户端套接字。每个协议都需要单独的套接字(在服务器端,如果您的平台支持双栈套接字,则可以为两种协议创建一个套接字)。
I've seen other examples whereby a local/source
sockaddr_in
structure is filled out with the client's information and that is used in the bind, but those are IPv4 or IPv6 specific.
是的,因为您将使用 EITHER IPv4 OR IPv6 发送数据包,您不能使用两种协议发送数据包同时(双栈套接字可以接收来自任一协议的数据包)。
Can someone please show me how to properly update the
talker.c
code tosendto()
andrecvfrom()
a UDP server using a fixed source port number . Assume that the server is immutable
尝试这样的事情:
#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;
}