![](/img/trans.png)
[英]How to retrieve the source port from a UDP packet while using recvmsg()
[英]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地址的提示結構派生而來socket()
,因此不起作用,並且出現綁定錯誤:
Error 99 (Cannot assign requested address)
添加的代碼:
bind(sockfd, p->ai_addr, p->ai_addrlen)
我想以一種適用於IPv4和IPv6的方式來執行此操作。
我已經看到其它實例中,由此局部/源SOCKADDR_IN結構是與客戶端的信息填寫和在綁定使用的,但這些都是IPv4或IPv6特定。
有人可以告訴我如何使用固定的源端口號正確地將talker.c
代碼更新為sendto()
和recvfrom()
UDP服務器嗎? 假設服務器是不可變的。
然后服務器響應該端口號 。 是的,這很不尋常
沒有什么不尋常的。 這就是大多數UDP服務器的工作方式。 他們總是響應發送者的端口。 他們不知道該端口是固定端口還是臨時端口,由發送方決定。 除非特定協議規定將響應發送到其他端口,否則這種情況並不常見。
我所有的在線搜索都指向使用
bind()
正確,這是您在這種情況下需要的。
但是該代碼通常在服務器端。
沒有什么可以阻止客戶端使用bind()
。
我還沒有找到使用現代的
getaddrinfo()
方法在客戶端進行綁定的方法。
它與服務器端完全相同,只是必須綁定到特定的IP地址,而不能像使用服務器套接字那樣綁定到0.0.0.0
或::0
。
我試圖在
socket()
設置之后立即添加一個bind()
但這不起作用
是的,它確實。 問題是您在綁定和發送時都使用了相同的 IP地址,這將不起作用。 您需要綁定到CLIENT的 IP地址,然后發送到SERVER的 IP地址。
因為
p
是服務器端結構(源自使用服務器IP地址的hints
結構)
您正在濫用p
。 您不能bind()
客戶端套接字bind()
到服務器的IP地址(您需要使用connect()
來代替)。 您需要bind()
客戶端套接字bind()
到客戶端計算機本地的IP地址。 就像您必須bind()
服務器套接字bind()
到服務器計算機本地的IP地址一樣。
請記住,套接字與一對IP地址相關聯。 bind()
建立套接字的本地 IP地址。 connect()
建立套接字的REMOTE IP地址。
我想以一種適用於IPv4和IPv6的方式來執行此操作。
您不能為這兩個協議創建單個客戶端套接字。 每個協議都需要單獨的套接字(在服務器端,如果平台支持雙棧套接字,則可以為兩個協議創建一個套接字)。
我還看到了其他示例,其中在本地/源
sockaddr_in
結構中填充了客戶端的信息,並在綁定中使用了該信息,但這些結構特定於IPv4或IPv6。
是的,因為您將使用EITHER 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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.