tryhi 发表于 2011-5-18 14:43:59

TCP连接怎么实现外网连接

本帖最后由 tryhi 于 2011-5-18 23:03 编辑

这里有一个很好的例子,在局域网测试通过,但用来连接外网,好像就不行了
http://www.autoitx.com/forum.php?mod=viewthread&tid=20154&highlight=%2B%CD%E2%CD%F8

帖子是收费贴,我把代码上传了,希望作者不要介意。

主要问题在于TCP怎么用外网连接,只需要服务端的外网IP地址就能连接么?





客户端;~ by yeqing880
TCPStartUp()
AdlibRegister ("Mem",3000)
Global $Socket

conn()


While 1
      
      $sRecv = TCPRecv($Socket, 2048, 1)
      If @error Then
      AdlibUnRegister ("sendx")
      conn()
    EndIf
    If $sRecv <> "" And $sRecv = "shutdown" Then Exit
      
WEnd

Func Sendx()
      TCPSend ($Socket,StringToBinary("online",4))
EndFunc

Func conn()
Do
Sleep (1000)
$Socket = TCPConnect("192.168.1.71",1313)
Until $Socket <> -1
AdlibRegister ("sendx",5000)
EndFunc

Func Mem()
      Local $ai_Handle = DllCall("kernel32.dll", 'int', 'OpenProcess', 'int', 0x1f0fff, _
      'int', False, 'int', @AutoItPID)
      Local $ai_Return = DllCall("psapi.dll", 'int', 'EmptyWorkingSet', 'long', $ai_Handle)
      DllCall("kernel32.dll" , 'int', 'CloseHandle', 'int', $ai_Handle)
EndFunc
服务端;~ by yeqing880
#include "TCP.au3"
#include <date.au3>
#include <Array.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <GuiStatusBar.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>
#Include <GuiListView.au3>

Opt("GUIOnEventMode", 1)
Opt("TrayMenuMode",3)
Opt("TrayOnEventMode",1)

$displayitem    = TrayCreateItem("退出")
TrayItemSetOnEvent($displayitem,"_exit")

Global $chaoshi = "",$szid = 0

Global $chaoshitime = 30 ;根据实际情况设置超时时间

Global $timeset = 1000 ;根据实际情况设置timer调用时间

Dim $lianjie

$hServer = _TCP_Server_Create("1313","192.168.1.71")
_TCP_RegisterEvent($hServer, $TCP_NEWCLIENT, "NewClient")
_TCP_RegisterEvent($hServer, $TCP_DISCONNECT, "Disconnect")
_TCP_RegisterEvent($hServer, $TCP_RECEIVE, "rev")


$Form1 = GUICreate("Form1", 620, 562,150,100)
GUISetOnEvent($GUI_EVENT_CLOSE,"gui")
$ListView1 = GUICtrlCreateListView("Socket|IP地址", 200, 0, 419, 451)
$Menu = GUICtrlCreateContextMenu($ListView1)
$exitall= GUICtrlCreateMenuItem("全部退出",$Menu, 1)
GUICtrlSetOnEvent(-1, "shutdown2")
$Edit1 = GUICtrlCreateEdit("", 0, 0, 198, 451, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_WANTRETURN,$WS_VSCROLL))
$StatusBar1 = _GUICtrlStatusBar_Create($Form1)
Dim $StatusBar1_PartsWidth =
_GUICtrlStatusBar_SetParts($StatusBar1, $StatusBar1_PartsWidth)
_GUICtrlStatusBar_SetText($StatusBar1, "服务器建立成功,监听中....", 0)
_GUICtrlStatusBar_SetText($StatusBar1, "当前在线数0", 1)

$Edit2 = GUICtrlCreateEdit("", 0, 452, 619, 89, BitOR($ES_AUTOVSCROLL,$ES_AUTOHSCROLL,$ES_WANTRETURN,$WS_VSCROLL))
GUISetState(@SW_SHOW)

$Form2 = GUICreate("数组", 411, 303, 154+620,100)
$ListView2 = GUICtrlCreateListView("ID|Socket|IP|TIME", 0, 0, 410, 302)
GUISetState(@SW_SHOW)

$Timer = DllCallbackRegister("check", "int", "hwnd;uint;uint;dword")
$TimerDLL = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", $timeset, "ptr", DllCallbackGetPtr($Timer))
$Timer2 = DllCallbackRegister("check2", "int", "hwnd;uint;uint;dword")
$TimerDLL2 = DllCall("user32.dll", "uint", "SetTimer", "hwnd", 0, "uint", 0, "int", 1000, "ptr", DllCallbackGetPtr($Timer2))


While 1
      Sleep (100000)
WEnd

Func NewClient($hSocket, $iError)
ReDim $lianjie
$lianjie += 1   
$lianjie = $hSocket
$lianjie = _TCP_Server_ClientIP($hSocket)
$lianjie = $chaoshitime
GUICtrlSetData ($Edit1,GUICtrlRead($edit1)&_TCP_Server_ClientIP($hSocket)&"/"&$hSocket&" 连接!"&@CRLF)
GUICtrlCreateListViewItem ($hSocket&"|"&_TCP_Server_ClientIP($hSocket),$listview1)
getonling()
EndFunc

Func rev($hSocket, $sReceived, $iError)
If $sReceived = "online" Then
$lianjie = $chaoshitime
EndIf
EndFunc

Func Disconnect($hSocket, $iError)
If $hSocket = $chaoshiThen
$chaoshi = ""
Return
Else
$s = _ArraySearch ($lianjie,$hSocket, 1, 0, 0, 0, 1)
GUICtrlSetData($Edit1,GUICtrlRead($edit1)&$lianjie[$s]&"/"&$lianjie[$s]&" 离线(退出)!"&@CRLF)
_GUICtrlListView_DeleteItem ($ListView1,_GUICtrlListView_FindInText($ListView1, $lianjie[$s]))               
_ArrayDelete($lianjie,$s)
$lianjie -= 1   
getonling()
EndIf   
EndFunc

Func Shutdown2()
For $s = 1 To UBound($lianjie) - 1
      TCPSend($lianjie[$s],"shutdown")
Next

EndFunc

Func check($hWnd, $uiMsg, $idEvent, $dwTime)
      Local $s = 1
      $begin = TimerInit()
      While $s <=UBound($lianjie) - 1
                If $lianjie[$s] <= 0 Then
                $chaoshi = $lianjie[$s]
                _TCP_Server_DisconnectClient($lianjie[$s])
                GUICtrlSetData($Edit1,GUICtrlRead($edit1)&$lianjie[$s]&"/"&$lianjie[$s]&" 离线(超时)!"&@CRLF)
      _GUICtrlListView_DeleteItem ($ListView1,_GUICtrlListView_FindInText($ListView1, $lianjie[$s]))               
      _ArrayDelete($lianjie,$s)
                $lianjie -= 1            
                getonling()
                GUICtrlSetData($Edit2,"超时检测完成,耗时大约"&Round (TimerDiff($begin),2)&"秒 当前时间:"&_Now()&@CRLF&GUICtrlRead($Edit2))
                Return
            Else
                If $lianjie[$s] <> "del" Then $lianjie[$s] -= 1
            EndIf
      $s += 1
    WEnd
    GUICtrlSetData($Edit2,"超时检测完成,耗时大约"&Round (TimerDiff($begin),2)&"秒 当前时间:"&_Now()&@CRLF&GUICtrlRead($Edit2))

EndFunc

Func check2($hWnd, $uiMsg, $idEvent, $dwTime)

GUICtrlSendMsg($ListView2, $LVM_DELETEALLITEMS, 0, 0)
GUICtrlCreateListViewItem ("0||||",$ListView2)
For $s = 1 To UBound($lianjie) - 1
      
GUICtrlCreateListViewItem($s&"|"&$lianjie[$s]&"|"&$lianjie[$s]&"|"&$lianjie[$s],$ListView2)
      
Next


EndFunc

Func getonling()
_GUICtrlStatusBar_SetText($StatusBar1, "当前在线数"&UBound($lianjie)-1, 1)
EndFunc

Func gui()
      Switch @GUI_CtrlId
                Case $GUI_EVENT_CLOSE
                        _exit()
                        
      EndSwitch
EndFunc

Func _exit()
DllCallbackFree($Timer)
Sleep (1000)
_TCP_Server_Stop()
Exit
EndFunc

需要用到这个UDF




续:回复都很精辟,不知选哪条{:face (396):}

lanfengc 发表于 2011-5-18 14:44:00

这样的连接方式可以采用另外的办法来完成。 只让客户端单方面获取服务器端数据。不要发回数据。 方法是:
1.服务器端创建监听接口。 路由映射出去。
2.客户机启动,连接服务器端口,发送过去“我上线了”
3.服务器端接收到“客户机上线消息”,则根据该客户端标志(这个标志可以在发送我上线了的时候一并发送过来),在本地创建一个以客户机标志命名的文件,然后,本地创建HTTP服务器,可以用傻瓜HTTP、Netbox这样的工具来架设。 然后,路由器再映射80端口到本机80端口。
4.客户机启动发送 我上线了消息后,创建一个定时器,不断的读取服务器80端口,读取发送出去的标志为名字的htm或txt文件。 读取后分析其中内容即可获得服务器对自己做了什么响应。

这样,客户机只需要访问服务器,不需要自身映射就可以完成单方面获取注册信息等数据。 非常方便。

cnmini 发表于 2011-5-18 19:13:29

直接把源码贴出来吧,没钱下载啊

tryhi 发表于 2011-5-18 19:17:00

回复 2# cnmini

感谢你的关注,因为提问区下载源码不要钱的,而且源码有点长,看来我还是贴出来好点

lanfengc 发表于 2011-5-18 19:24:01

你服务器端在外网还是在内网? 需要在路由上映射

tryhi 发表于 2011-5-18 19:30:36

回复 4# lanfengc

服务端在外网,我就是搞不清楚,仅通过一个外网IP是否真的可以连接。

我看里面有一个IP,应该说服务端改成服务端本机的外网IP,客户端里面的IP也改成服务端机子的外网IP应该就能连接得上,我是这样理解的,不知哪里出问题

lanfengc 发表于 2011-5-18 19:42:37

通讯是这样的。AU3创建TCP连接的时候,不可以在同一端口发送和接受数据(这是我的一点看法, 如果不对,请指正),比如你的网络环境是这样的, 服务器端所在的网络为A, 客户机所在的网络为B, 客户机要访问服务器上的数据。 首先要经过网络B的路由器,然后一级一级路由到A网络,经由A网络路由器到服务器。 然后服务器接受到你发送过去的数据,要再开一个端口发送回数据。 此时,服务器和客户机就对调了过来了。服务器变成了客户机,客户机变成了服务器,如果本地客户机没有对外映射接受数据的端口,则服务器发回数据的时候,其实访问的是网络B路由器的端口,如果网络B的路由器对客户机的接受端口没有映射,则数据不可达。 就会造成两个机器无法通讯。明白了吗?

boyhong 发表于 2011-5-18 19:45:38

我个人看法:
因服务端与客户端有交互,走外网的话,那必须双方的身份是公网IP!但实际情况一般是:双方均在各自的局域网中。这样就需要在双方路由器上做是映射。并且在脚本中指定对方公网IP及端口。
似乎这是一个网络通讯的问题,呵呵,不是脚本本身的问题。。。。
希望有牛人多多指教!

tryhi 发表于 2011-5-18 19:50:09

回复 7# boyhong

如果服务端本身没通过局域网而直接通过MODE直接上网呢?

tryhi 发表于 2011-5-18 19:57:37

通讯是这样的。AU3创建TCP连接的时候,不可以在同一端口发送和接受数据(这是我的一点看法, 如果不对,请指 ...
lanfengc 发表于 2011-5-18 19:42 http://www.autoitx.com/images/common/back.gif

那如果服务端运行在一台服务器上,这样也连接不了么?

这样的话,好像普通情况下好像都是无法通讯的

lanfengc 发表于 2011-5-18 20:05:13

不管你运行在什么机器上。通过modem上网的环境可以。 因为你本身就是个公网IP, 但是这个公网IP会变动。 需要希网域名来解析。运行在服务器上,如果是公网IP的服务器,可以。 内网服务器通过映射发布的服务器,还是不行。

tryhi 发表于 2011-5-18 20:53:03

不管你运行在什么机器上。通过modem上网的环境可以。 因为你本身就是个公网IP, 但是这个公网IP会变动。...
lanfengc 发表于 2011-5-18 20:05 http://www.autoitx.com/images/common/back.gif

哇,确实是这样哦,通过MODEM的机子就可以,之前找了几人试,原来都是用路由的

ceoguang 发表于 2011-5-18 21:44:52

此问题论坛一早已讨论过
http://www.autoitx.com/forum.php?mod=viewthread&tid=5455
作为高级会员,请学会搜索

kk_lee69 发表于 2011-5-18 22:18:24

我也覺得是兩方面路由器設定問題吧......內網可以通外網理論上就可以通才對呀

tryhi 发表于 2011-5-18 22:45:34

此问题论坛一早已讨论过

作为高级会员,请学会搜索
ceoguang 发表于 2011-5-18 21:44 http://www.autoitx.com/images/common/back.gif

确实,我也是搜遍了都没找到才来问的。

    不过没关注UDP

你该去研究一下什么叫端口映射,什么叫反向链接.

确实,我该自己研究一下,感谢指点
页: [1] 2 3
查看完整版本: TCP连接怎么实现外网连接