TCP外网反连接内网失败,请求指教!

jingmei02 2013-01-13 05:45:51
服务器端:
sockaddr_in acc_addr;
int addSize = sizeof(sockaddr);
SOCKET s = accept(sock, (sockaddr*)&acc_addr, &addSize);
以上是接收客户端发起的连接请求——成功;
然后服务器端又通过以下方式向客户端发起连接请求——失败。

sockaddr_in addr2;
addr2.sin_family = acc_addr.sin_family;
addr2. sin_port = acc_addr.sin_port; //服务器端得到的客户端外网端口
addr2. sin_addr.s_addr = acc_addr.sin_addr; //服务器端得到的客户端外网IP
connect(sock2, (sockaddr *)&addr2, sizeof(sockaddr));

客户端:
int res = ms.Connect(sock, serverIP, port);
sockaddr_in in_addr;
int nAddrLen = sizeof(sockaddr);
int res = getsockname(sock, (sockaddr *)&in_addr, &nAddrLen);
unsigned short port1 = ntohs(in_addr.sin_port);
AfxBeginThread((AFX_THREADPROC)TCP_ThreadListen, (LPVOID)port1);//在客户端监听port1端口,监听成功
...全文
1793 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
虫二中年 2014-09-26
  • 打赏
  • 举报
回复
截铁了?刚看到
jingmei02 2013-03-04
  • 打赏
  • 举报
回复
多谢各位大哥不吝赐教!结贴晚了,抱歉!
  • 打赏
  • 举报
回复
引用 17 楼 jingmei02 的回复:
TO:akirya 如果TCP不能打洞,请问QQ如何实现两个客户端之间的通信?应该不会通过服务器来转发的吧。
1 完全可以用udp通讯啊 2 现在的qq都是用服务器中转的,服务器会记录所有的聊天记录,以供相关部门查询(不然QQ肯定会被和谐掉的) 远程协助 视频通话 都是用的udp打洞,应用层写代码来保证不丢包的。
jingmei02 2013-01-17
  • 打赏
  • 举报
回复
TO:akirya 如果TCP不能打洞,请问QQ如何实现两个客户端之间的通信?应该不会通过服务器来转发的吧。
  • 打赏
  • 举报
回复
引用 10 楼 jingmei02 的回复:
TO:akirya 网上都说外网第一次访问内网时都会被路由器拦截并拒绝,但是只要内网PC先经路由向外发过数据包,外网PC就能够识别并连接到内网PC。
我认识有不少人搞过这个nat打洞,从未听说过tcp能成功打洞,就算是udp成功率也没办法达到100%的能穿透。 nat为了保证安全很多包是直接拒绝掉的,外网的tcp请求是直接过滤掉的。 你应该看看nat原理性的东西
ajax_for_spring 2013-01-17
  • 打赏
  • 举报
回复
其实只要你能欺骗得了路由的NAT 就OK了 让他觉得 你的 SYN是合理的他就会给一个应答 这样就可以 正常通信了 也就是你们所说的 打洞 其实 打洞 可以说是双向的 也可以说是单向的 这要看 两台机器所在的位置了
jingmei02 2013-01-17
  • 打赏
  • 举报
回复
TO:akirya 多谢指教!我改UDP试一下
jingmei02 2013-01-16
  • 打赏
  • 举报
回复
烦请各位大哥帮忙看看,指出错误。谢谢! 服务器端: UINT WINAPI ThreadListen(LPVOID lpParam) { int port = 7000; int res = SOCKET_ERROR; SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建SOCKET int optval = 1; res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(int)); sockaddr_in addr; addr. sin_family = AF_INET; addr. sin_port = htons ( port ); addr. sin_addr.s_addr = htonl( INADDR_ANY ); res = bind( sock, (sockaddr*)&addr, sizeof(sockaddr) ); //绑定SOCKET res = listen(sock, 0); //监听 SOCKET s = new SOCKET(); while ( true ) { sockaddr_in acc_addr; int addSize = sizeof(sockaddr); s = accept(sock, (sockaddr*)&acc_addr, &addSize); //接收客户端发来的连接请求 char *outIp = inet_ntoa(acc_addr.sin_addr); unsigned short outPort = ntohs(acc_addr.sin_port); //以公网IP和端口建立新连接 SOCKET sock2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //新建SOCKET if( sock2 == INVALID_SOCKET ) { printf("new socket failed %d\n", WSAGetLastError()); return INVALID_SOCKET; } res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(int)); sockaddr_in addr2; addr2.sin_family = acc_addr.sin_family; addr2. sin_port = htons(outPort); //公网端口 addr2. sin_addr.s_addr = inet_addr(outIp); //公网IP res = connect(sock2, (sockaddr *)&addr2, sizeof(sockaddr)); //反连客户端,失败 } return 0; } 客户端: int port = 8000; int res = SOCKET_ERROR; SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建SOCKET int optval = 1; res = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(int)); sockaddr_in addr; addr. sin_family = AF_INET; addr. sin_port = htons ( port ); addr. sin_addr.s_addr = htonl( INADDR_ANY ); res = bind( sock, (sockaddr*)&addr, sizeof(sockaddr) ); //绑定SOCKET res = connect(sock, (sockaddr *)&addr, sizeof(sockaddr)); //客户端请求连接服务器端,成功 SOCKET sock2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //创建SOCKET res = setsockopt(sock2, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(int)); sockaddr_in addr2; addr2. sin_family = AF_INET; addr2. sin_port = htons ( port ); addr2. sin_addr.s_addr = htonl( INADDR_ANY ); res = bind( sock2, (sockaddr*)&addr2, sizeof(sockaddr) ); //绑定SOCKET res = listen(sock, 0); //在客户端启动监听 SOCKET s = INVALID_SOCKET; while(true) { sockaddr_in acc_addr; int addSize = sizeof(sockaddr); s = accept(sock, (sockaddr*)&acc_addr, &addSize); //从客户端接收服务器端发来的连接请求 }
jingmei02 2013-01-15
  • 打赏
  • 举报
回复
路由器后面的客户端成功连接到外网的服务器了,服务器端也得到了客户端的信息(路由WAN IP和端口),我现在要新建socket反连客户端,但是不成功。
jingmei02 2013-01-15
  • 打赏
  • 举报
回复
请问大哥你有何办法直接连到路由后面的客户端呢?
爱蹄子的羊头 2013-01-15
  • 打赏
  • 举报
回复
引用 12 楼 jingmei02 的回复:
路由器后面的客户端成功连接到外网的服务器了,服务器端也得到了客户端的信息(路由WAN IP和端口),我现在要新建socket反连客户端,但是不成功。
肯定不成功啊. 你连的不是客户端 你连的是路由器.
dataxdata 2013-01-14
  • 打赏
  • 举报
回复
用于connect的socket不需要bind,只有监听用的socket才需要bind
jingmei02 2013-01-14
  • 打赏
  • 举报
回复
TO:dataxdata 谢谢你,我看过了你给的资料,不过还是没能成功穿透nat。哎,看来我真是太笨了! 我的问题应该是源于地址绑定有误。 在局域网内测试的时候,connect之前先bind了一个本地端口,然而connect成功之后再用getsockname去取socket信息却发现端口号改变了,请问这是为何?如何才能保证bind的端口不会因为connect改变?
My_Love 2013-01-14
  • 打赏
  • 举报
回复
关键是在客户端要bind一个监听端口(在原端口上重复bind)
  • 打赏
  • 举报
回复
引用 2 楼 jingmei02 的回复:
我的打洞方式错了吗?打洞不是从服务器端获取客户端的外网IP和端口号,然后再从服务器端连接客户端吗?
比这个复杂的多了,而且没听说过用tcp打洞的,都是udp。
爱蹄子的羊头 2013-01-14
  • 打赏
  • 举报
回复
TCP 只要你连出去了. 就可以发送数据库u回来了. 打洞是两台都在 路由器后面的机器 用UDP的..
jingmei02 2013-01-14
  • 打赏
  • 举报
回复
TO:akirya 网上都说外网第一次访问内网时都会被路由器拦截并拒绝,但是只要内网PC先经路由向外发过数据包,外网PC就能够识别并连接到内网PC。
  • 打赏
  • 举报
回复
引用 8 楼 jingmei02 的回复:
客户端连服务器端: 192.168.1.2:1234(客户端)——>112.123.101.23:4000(路由)——>61.154.161.178:5000(服务器) 如果要从服务器端反连客户端,是不是该沿着下面的路径返回: 61.154.161.178:5000(服务器)——>112.123.101.23:4000(路由) 如果connect之前不bind……
一般路由会直接拒绝 外部的tcp连接的。
jingmei02 2013-01-14
  • 打赏
  • 举报
回复
客户端连服务器端: 192.168.1.2:1234(客户端)——>112.123.101.23:4000(路由)——>61.154.161.178:5000(服务器) 如果要从服务器端反连客户端,是不是该沿着下面的路径返回: 61.154.161.178:5000(服务器)——>112.123.101.23:4000(路由) 如果connect之前不bind的话,将会从以下路径返回: 61.154.161.178:随机分配的端口(服务器)——>112.123.101.23:4000(路由)【测试时连接不成功】
dataxdata 2013-01-13
  • 打赏
  • 举报
回复
如果客户端是一台直接暴露在外网上的计算机,具有公网IP地址,那么直接连接就可以了;
如果客户端是在躲在路由器之后的内网计算机,IP地址是内网的私有地址,就必须通过路由器的NAT才能连接到外网上,这样外网上的主机如果要访问这台计算机,就要穿透路由器的NAT。
可以参考这几个链接:
http://www.cnblogs.com/epan/articles/98379.html
http://zh.wikipedia.org/wiki/NAT%E7%A9%BF%E9%80%8F
http://michankong.blog.51cto.com/1464983/761270
http://blog.csdn.net/hairetz/article/details/4101918
加载更多回复(2)

1,317

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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