查看返回接收到UDP数据包的宿地址结构--(适用于LINUX和BSD系统)

/*
* recvfromto Like recvfrom, but also stores the destination
* IP address. Useful on multihomed hosts.
*
* Should work on Linux and BSD.
*
* Copyright (C) 2002 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

/* Remove this when autoconf can detect this. */
#if defined(IP_PKTINFO) && !defined(HAVE_IP_PKTINFO)
#  define HAVE_IP_PKTINFO 1
#elif defined(IP_RECVDSTADDR) && !defined(HAVE_IP_RECVDSTADDR)
#  define HAVE_IP_RECVDSTADDR
#endif

int recvfromto(int s, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen,
struct sockaddr *to, socklen_t *tolen)
{
struct msghdr msgh;
struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[1024];
int opt, err;

/*
* If from or to are set, they must be big enough
* to store a struct sockaddr_in.
*/
if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
    (to   && (!tolen   || *tolen   < sizeof(struct sockaddr_in)))) {
errno = EINVAL;
return -1;
}
if (tolen) *tolen = 0;

#ifdef HAVE_IP_PKTINFO
/*
* IP_PKTINFO doesn't provide sin_port so we have to
* retrieve it using getsockname().
*/
if (to) {
struct sockaddr_in si;
socklen_t l = sizeof(si);

((struct sockaddr_in *)to)->sin_family = AF_INET;
((struct sockaddr_in *)to)->sin_port = 0;
l = sizeof(si);
if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
((struct sockaddr_in *)to)->sin_port = si.sin_port;
((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
}
*tolen = sizeof(struct sockaddr_in);
}
#endif

/* Set MSG_DONTWAIT if O_NONBLOCK was set. */
if (fcntl(s, F_GETFL) & O_NONBLOCK) flags |= MSG_DONTWAIT;

/* Set up iov and msgh structures. */
iov.iov_base = buf;
iov.iov_len  = len;
msgh.msg_control = cbuf;
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = from;
msgh.msg_namelen = fromlen ? *fromlen : 0;
msgh.msg_iov  = &iov;
msgh.msg_iovlen = 1;

#ifdef HAVE_IP_PKTINFO
/* Set the IP_PKTINFO option (Linux). */
opt = 1;
setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
#endif

#ifdef HAVE_IP_RECVDSTADDR
/* Set the IP_RECVDSTADDR option (BSD). */
opt = 1;
setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
#endif

/* Receive one packet. */
err = recvmsg(s, &msgh, flags);
if (fromlen) *fromlen = msgh.msg_namelen;

/* Process auxiliary received data in msgh */
for (cmsg = CMSG_FIRSTHDR(&msgh);
     cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg);
     cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
#ifdef HAVE_IP_PKTINFO
if (cmsg->cmsg_level == SOL_IP
    && cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *i =
(struct in_pktinfo *)CMSG_DATA(cmsg);
if (to) {
((struct sockaddr_in *)to)->sin_addr =
i->ipi_addr;
*tolen = sizeof(struct sockaddr_in);
}
break;
}
#endif

#ifdef HAVE_IP_RECVDSTADDR
if (cmsg->cmsg_level == IPPROTO_IP
    && cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
if (to) {
((struct sockaddr_in *)to)->sin_addr = *i;
*tolen = sizeof(struct sockaddr_in);
}
}
#endif
}

return err;
}

#ifdef STANDALONE
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

/*
* Small test program to test recvfromto
*/
int main(int argc, char **argv)
{
struct sockaddr_in from, to, in;
char buf[1024];
int port = 20000;
int n, s, fl, tl;

if (argc > 1) port = atoi(argv[1]);

s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
in.sin_family = AF_INET;
in.sin_port = htons(port);
in.sin_addr.s_addr = INADDR_ANY;
bind(s, &in, sizeof(in));

while (1) {
fl = tl = sizeof(struct sockaddr_in);
memset(&from, 0, sizeof(from));
memset(&to, 0, sizeof(to));
if ((n = recvfromto(s, buf, sizeof(buf), 0,
    (struct sockaddr *)&from, &fl,
    (struct sockaddr *)&to, &tl)) < 0) {
perror("recvfromto");
break;
}
printf("Received a packet of %d bytes/n", n);
printf("  src ip:port %s:%d/n",
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
printf("  dst ip:port %s:%d/n",
inet_ntoa(to.sin_addr), ntohs(to.sin_port));
}

return 0;
}

#endif /* STANDALONE */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值