高分求教:recvfrom函数使用的问题

sdzg_wq 2007-06-26 04:15:29
Linux系统,有多个网卡(即有多个IP地址)基于UDP协议的SOCKET编程中,创建了一个sock=socket(AF_INET,SOCK_DGRAM,0);绑定该sock的端口为5060,不指定该sock的IP地址。然后用recvfrom(sock,buff,500,0,(struct sockaddr *)&source,&i)接收数据。有什么办法可以知道数据是由本机的哪个IP接收呢。我用了getsockname和getpeername函数,返回的结果都不对,两个函数的返回值都是-1。请各位指教,谢谢了!
我的代码如下:
-----------------------
int main()
{
int sock,i;
char buff[100];
struct sockaddr_in address,ad,source;
address.sin_family=AF_INET;
address.sin_port=htons(5678);
address.sin_addr.s_addr=inet_addr("0.0.0.0");
sock=socket(AF_INET,SOCK_DGRAM,0);
bind(sock,(struct sockaddr *)&address,sizeof(address));
source.sin_family=PF_INET;
ad.sin_family=PF_INET;
recvfrom(sock,buff,500,0,(struct sockaddr *)&source,&i);
if(getsockname(sock,(struct sockaddr *)&ad,&i))
{
puts("Error!");
}
close(sock);
--------------
以上代码,在没有recvfrom(sock,buff,500,0,(struct sockaddr *)&source,&i);一行时还可以正常运行,一加上这一句,getsockname函数就返回错误值。
...全文
1444 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
rick0755 2010-11-24
  • 打赏
  • 举报
回复
顶楼上:“但要注意参数6必须初始值为socklen = sizeof(struct sockaddr);”
pc3000c 2010-08-12
  • 打赏
  • 举报
回复
有两种方法可以知道源IP信息:

1) recvfrom 的结构体 参数5 -》 (struct sockaddr *)&svrip , 可存储接收到的IP信息,
采用inet_ntoa(svrip.sin_addr) 反映源IP地址, 但要注意参数6必须初始值为socklen = sizeof(struct sockaddr); 否则,就会出现返回源IP地址 0.0.0.0 的异常现象。 注意,此处为常见错误,且并不是由系统差异或线程安全等概念引起!!!

2) 用RAW SOCK 可以解析整个报文的IP头,此法不仅可以获得往来数据包的(源/目地)IP信息,还可以捕获MAC等二层的部分信息,为分析/定制/处理 往来私有协议和公有协议的通用重要选择。
heimaofj 2010-06-02
  • 打赏
  • 举报
回复
你可以把网卡设置成promisc模式,建立一种raw socket(原始套接字),recvfrom接受数据,分析数据头的目的地址和源地址,就可以达到你的要求。
/*
*this is a program of getting the speed of network
* =================================================
*| | | |
*| 链路层头 | IP报文头 | 传输层报文头 | 应用层数据
*| | | |
* ===================================================
* 14 + 20 + 8 =42
*/


这是一个例子:



#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#define BUFFER_MAX 2048

int main(int argc, char *argv[])
{

int sock, n_read, proto;
char buffer[BUFFER_MAX];
char *ethhead, *iphead, *tcphead,
*udphead, *icmphead, *p;

if((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0)
{
fprintf(stdout, "create socket error\n");
exit(0);
}

while(1)
{
n_read = recvfrom(sock, buffer, 2048, 0, NULL, NULL);
/*
14 6(dest)+6(source)+2(type or length)
+
20 ip header
+
8 icmp,tcp or udp header
= 42
*/
if(n_read < 42)
{
fprintf(stdout, "Incomplete header, packet corrupt\n");
continue;
}

ethhead = buffer;
p = ethhead;
int n = 0XFF;
printf("MAC: %.2X:%02X:%02X:%02X:%02X:%02X==>"
"%.2X:%.2X:%.2X:%.2X:%.2X:%.2X\n",
p[6]&n, p[7]&n, p[8]&n, p[9]&n, p[10]&n, p[11]&n,
p[0]&n, p[1]&n, p[2]&n,p[3]&n, p[4]&n, p[5]&n);

iphead = ethhead + 14;
p = iphead + 12;

printf("IP: %d.%d.%d.%d => %d.%d.%d.%d\n",
p[0]&0XFF, p[1]&0XFF, p[2]&0XFF, p[3]&0XFF,
p[4]&0XFF, p[5]&0XFF, p[6]&0XFF, p[7]&0XFF);
proto = (iphead + 9)[0];
p = iphead + 20;
printf("Protocol: ");
switch(proto)
{
case IPPROTO_ICMP: printf("ICMP\n");break;
case IPPROTO_IGMP: printf("IGMP\n");break;
case IPPROTO_IPIP: printf("IPIP\n");break;
case IPPROTO_TCP :
case IPPROTO_UDP :
printf("%s,", proto == IPPROTO_TCP ? "TCP": "UDP");
printf("source port: %u,",(p[0]<<8)&0XFF00 | p[1]&0XFF);
printf("dest port: %u\n", (p[2]<<8)&0XFF00 | p[3]&0XFF);
break;
case IPPROTO_RAW : printf("RAW\n");break;
default:printf("Unkown, please query in include/linux/in.h\n");
}
}
}
茻鄷 2007-12-29
  • 打赏
  • 举报
回复
学习
wq1982718 2007-06-29
  • 打赏
  • 举报
回复
getsockname只能取得绑定的地址,如果在bind中没有显示给出ip,系统有默认的值,getsockname取得的就是这个,达不到你的目的。可以用revmsg函数取得源地址。
用其中的control字段返回。
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};

可参考http://linux.about.com/od/commands/l/blcmdl7_ip.htm
mymtom 2007-06-26
  • 打赏
  • 举报
回复
recvfrom(sock,buff,500,0,(struct sockaddr *)&source,&i);
if(getsockname(sock,(struct sockaddr *)&ad,&i))

---------------
这两句的每一句之前加上
i = sizeof(struct sockaddr_in);
cceczjxy 2007-06-26
  • 打赏
  • 举报
回复
在linux内核中,一个socket有两个地址,一个本地地址,一个外部俩接地址,

如果使用udp协议,
在调用bind函数时,会把你要绑定的那个地址填到socket的本地地址那块地方.你要设成0.0.0.0它就给你填成0.0.0.0
在udp使用bind函数只是为了不用每此发送数据都填充地址这一参数传递过程而已.
只是在你发送数据时才会根据情况把实际的ip地址填到发送的数据的相应位置.
在内核接收到一个数据包时,会接收到一个外部地址,在你调用读函数时就把此地址读取走了.


如过使用tcp协议时,
在调用bind函数时,会把本地地址填到socket的本地地址那块地方.
在调用connect或则调用accecpt接收到连接时,回把外部地址那块填上.
在读写数据时,只读走数据,不读去地租.


在你这里
主要是 recvfrom(sock,buff,500,0,(struct sockaddr *)&source,&i);
里的i值不确定,也就是说没接收到地址.

sdzg_wq 2007-06-26
  • 打赏
  • 举报
回复
to:jixingzhong(瞌睡虫·星辰)
怎样分析呢?这个ip地址是在ip层的。
jixingzhong 2007-06-26
  • 打赏
  • 举报
回复
分析读取的数据包中的 IP 信息....
jixingzhong 2007-06-26
  • 打赏
  • 举报
回复
http://www.opengroup.org/onlinepubs/007908799/xns/recvfrom.html
isarc 2007-06-26
  • 打赏
  • 举报
回复
sf

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧