项目中的一个c/s应用,获得客户机的公网ip地址。使用的udp通讯,现在在大多数网络情况下都能正常通讯,但是有些nat下的客户机不能收到包!代码:
客户端:发送一个登陆包,然后阻塞着等服务器的应答sendto(m_hUSock, (const char*)&H2SLogPct, sizeof(H2SLogPct), 0, (sockaddr*)&m_ServerAddr, sizeof(sockaddr_in));int nRecv = recvfrom(m_hUSock, (char *)&S2HRpyLogonPct, sizeof(S2HRpyLogonPct), 0, (LPSOCKADDR)&m_RecvAddr, &m_nAddrLen);
服务器端:
int nRecv = recvfrom(m_hUSock, (char *)zRcvBuf, sizeof(zRcvBuf), 0, (LPSOCKADDR)&addrhost, &nAddrLen);CRpyNorPct RpyNorPct;sendto(m_hSock, (char*)&RpyNorPct, sizeof(RpyNorPct), 0, (sockaddr*)&addrhost, sizeof(sockaddr_in));
服务器将按照接收的地址发送回去,部分路由后的客户端不能接收到主机的回复包,这是为什么?
客户端:发送一个登陆包,然后阻塞着等服务器的应答sendto(m_hUSock, (const char*)&H2SLogPct, sizeof(H2SLogPct), 0, (sockaddr*)&m_ServerAddr, sizeof(sockaddr_in));int nRecv = recvfrom(m_hUSock, (char *)&S2HRpyLogonPct, sizeof(S2HRpyLogonPct), 0, (LPSOCKADDR)&m_RecvAddr, &m_nAddrLen);
服务器端:
int nRecv = recvfrom(m_hUSock, (char *)zRcvBuf, sizeof(zRcvBuf), 0, (LPSOCKADDR)&addrhost, &nAddrLen);CRpyNorPct RpyNorPct;sendto(m_hSock, (char*)&RpyNorPct, sizeof(RpyNorPct), 0, (sockaddr*)&addrhost, sizeof(sockaddr_in));
服务器将按照接收的地址发送回去,部分路由后的客户端不能接收到主机的回复包,这是为什么?
我现在实际就是这个example:
内网机器A,外网机器B,外网机器绑定端口3000,
1.A向B的3000端口发送消息注册
2.B通过recvfrom得到ip,port
3.B向得到的ip和port发送数据
4.A能接收到B发送的数据
而我在别人的帖子中看到的方式有:
内网机器A,外网机器B,内网机器绑定端口2000,外网机器绑定端口3000,
1.A向B的3000端口发送消息注册
2.B通过recvfrom得到ip,port
3.B向得到的ip和port发送数据
4.A在2000端口上能接收到B发送的数据
唯一区别就是我的例子没有在内网绑定端口,难道问题就是出在这里?
很多NAT设备是基于五元组的,例如: A--NAT---Server(固定IP).经过NAT之后是这样的:(IP1:Port1 --- IP2:8080)
IP1是NAT之后的IP、Port1是NAT之后的Port。IP2、8080是你固定IP的端口和IP。 如果你新开一个socket给A回应,IP是这样的:
IP2:PORT2----IP1:Port1 IP2是系统分配的一个端口,或者你bind的端口。但是,很多NAT设备发现PORT2不是由A发出去的端口,因此是不让进入,这种属于一种严格的地址转换方式,现在大部分NAT设备都是这样的。 这个五元组就类似一个“洞”,不是很多NAT穿越也叫在路由器上“打洞”就是这个道理。
是这样的,严格来说,LAN--LAN方式,如果两个LAN都处于私网,如果没有一个中介转发的话,成功率会很低。因此,你看QQ,很多时候都是处于一种“服务器转发模式”,这种模式其实就是两个网吧之间的PC通信。类似你程序设计的方式,我觉得应该对下面的线程再聚合一下,意思就是每个线程处理完了一个,把发送报文交给一个独立的线程或者socket接收的那个线程做发送处理。让你的socket工作于“全双工模式”。
的确处理完后聚合,使用另一个线程来发送,socket采用“全双工模式”是个很好的办法!
我下周去客户那儿做这个测试,谢谢!