做一个项目时遇到的问题,最终解决了,记录下:
1.问题描述
简单理解就是客户端和服务器之间的socket通信。
客户端就是我自己的Windows电脑,使用的是Qt。
服务器使用的是阿里云的服务器,公网ip,Linux系统,编程也是socket c编程。
先是用的TCP通信传业务数据,一切都正常。
再后来想要用udp通信传视频数据,客户端可以向服务器发送数据并且服务器能收到并显示。
但是服务器向客户端发udp数据时,客户端死活收不到。因为服务器端用的是纯c的socket,想着应该不会出问题。
于是在客户端的qt程序了找了半天bug都没找到原因,整个人心情就不好了,于是将怀疑的目光指向服务器。
2.问题定位
但万事都要讲证据,不能说客户端找不到问题就说服务器有问题,所以解决问题的第一步就是问题定位。
对于数据传输出问题,首先要确定是接收方还是发送方。
好在之前一门课学习了网络抓包,赶紧下载了抓包工具。
首先过滤IP地址,只抓取服务器ip发过来的包和客户端发送出去的包。
在一通数据交互后,我们可以看到客户端向服务器发送的udp包
端口号和数据报文都是正确的,服务器也确实是收到了。
问题就是死活都找不到服务器发给客户端的udp包,服务器端已经确认执行了sendto的程序。
说明很大有可能是服务器发送不成功
于是打印出发送的错误:(对,自己之前就是一个憨憨,一个sendto函数直接裸奔,说明错误判断很重要,能避免很多问题,节约很多时间)
//之前是这样写的...裸奔...如果发送没问题还好,有问题就哭死,编程还是不能偷懒,养成好习惯
sendto(sockfd_udp2,udp2_buf,byte_num,0,(struct sockaddr*)&to,len)
//确认是这里出来问题,打印出问题
if(sendto(sockfd_udp2,udp2_buf,byte_num,0,(struct sockaddr*)&to,len) == -1)
{
printf("udp发送错误\n");
perror("sendto");
}
3.sendto:Message too long问题定位
网上一通百度,说是udp的包太大了,要去setsocketopt改缓冲大小,我的包怎么看都只有几个字节呀,怎么会呢,于是用getsocketopt去查udp缓冲区的大小,百度找方法,当时是对照着自己这么写的:
int m;
ret = getsockopt(sockfd_udp2,SOL_SOCKET,SO_SNDBUF,&m,sizeof(m));
if (ret < 0)
{
perror("getsockopt");
}
printf("udp发送缓冲长度: %d\n",m);
大佬们能看出来这样写的问题吗?
打印出来是这样的:
4.getsocket: Bad address问题定位
不知道为什么,当时在百度上面都没找到问题,现在去百度发现有人提了…
当时遇到这个问题是懵逼,于是乎就没管这个问题了,又去把接受的函数错误给打印出来(接受是能收到数据的,就单纯想看看有没有问题)
byte_num = recvfrom(sockfd_udp2,udp2_buf,BUFFER_SIZE,0,(struct sockaddr*)&client_device[i].sockaddr_udp1,len);
if (byte_num < 0)
{
printf("udp接收错误\n");
perror("recvfrom");
}
好家伙。。。虽然能接受到数据,居然也报错了,都是自己偷懒的罪。
5. Bad address的问题定位
两个完全不同的函数,都出现Bad address,那肯定是传入参数的问题。果不其然…
CSDN上的这句话点醒了我:
recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,
__SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len);
sendto (int __fd, const void *__buf, size_t __n,
int __flags, __CONST_SOCKADDR_ARG __addr,
socklen_t __addr_len);
这两个函数长的是真的像,但是有区别。就在最后一个参数里socklen_t *和socklen_t。
原来就是自己传入的参数有问题
6.Message too long和Bad address问题 的 结论
Bad address:函数传入的参数有问题
Message too long: 传入的长度为-1.因为recvfrom接收错误,导致byte_num = -1.
byte_num = recvfrom(sockfd_udp2,udp2_buf,BUFFER_SIZE,0,(struct sockaddr*)&client_device[i].sockaddr_udp1,&len);
if (byte_num < 0)
{
printf("udp接收错误\n");
perror("recvfrom");
}
printf("5G装置udp2:%s端口号:%d\n", udp2_buf,ntohs(client_device[i].sockaddr_udp1.sin_port));
if(sendto(sockfd_udp2,udp2_buf,byte_num,0,(struct sockaddr*)&client_device[i].sockaddr_udp1,len) == -1)
{
printf("udp发送错误\n");
perror("sendto");
}