找回密码
 加入
搜索
查看: 9513|回复: 22

TCP同时发送文件和信息只用一个socket的讨论

 火.. [复制链接]
发表于 2010-1-24 18:59:40 | 显示全部楼层 |阅读模式
本帖最后由 itljl 于 2010-1-24 19:29 编辑

论坛上讨论TCP的少,我也来发一个吧。

重点是,用同一个端口,同一个SOCKET连接,既发送文字信息又发送二进制文件信息,也就是一程序是侦听端,一个程序是连接端,但两个程序都要能给对方发送文字信息与二进制文件信息,并都能接收文字信息与二进制文件信息。

最下面附有,用AU3,TCP发送和接收文件的示例。今天讨论的内容是,在同一个SOCKET中如果既发送信息,又发送文件。当然论坛上有朋友做了新的UDF,还有详尽的示例来演示。但是,他们都有一个地方没有处理好。

问题出在,接收文件的循环。

在文件接收循环,在论坛已有的例子中,有两种情况认为文件传送完成

第一种:
局域网通信核心程序(聊天、群发、传文件、维护通道等)元旦修正版
http://www.autoitx.com/forum.php ... p;extra=&page=1


$rData = TCPRecv($rAccept, 1024 * 1024, 1);开始接收文件
If Not @error Then

注意这里,如果没有错误。也就是如果有错误就退出文件接收这个循环,怎样会有错误?那就是对方断开这个socket连接。如果我们即要发送文字信息,又要发送文件的二进制信息。这里就不能断开连接。
(注意,如果对方没发送信息了,socket是正常连接着的,这里的tcprecv是不会出错的,只会返回空字符。)


另外一种
If $ri >= $rDays[3] Then ExitLoop
当接收到的二进制总字节数大于文件的信息时,退出接收文件循环
这也是行不通的,为什么?因为TCPRecv并不是按序列一条一条接收信息。而是从SOCKET中按设定好的大小 1024 * 1024 来接收。
所以,当对方发送文件完成后,并立即发送文件信息时,这个文件信息还是会被写入到文件中。

目前马马虎虎的方法就是发送文件用一个新的SOCKET,发送完成就断开。在  TCPRecv 下面检测出错就退出文件接收循环,并关闭连接,关闭文件。

但这样做的不好就是要新开一个SOCKET,那就必须接收一方要在开始侦听端口,这样将导致发送端与接收端都要同时侦听端口。

一是应用在外网上面,无论正向还是反向连接,路由器上必须开端口印射
二是,不能在同一台机子上开服务端与客户端。(因为同一个端口只能被一个程序侦听。)

谁有更好的办法来判断文件循环发送完成呢?欢迎讨论。



-----------------------------------------
接收

$R_ip = @IPAddress1
$R_File = @ScriptDir & "\R.jpg"
$R_Prot = 65432

$var = _recvFiles($R_ip, $R_File, $R_Prot)
MsgBox(0, "接收端", $var)

Func _recvFiles($R_ip, $R_File, $R_Prot);接收文件
  Local $i
  TCPStartup(); 开始 TCP 服务
  $MainSocket = TCPListen($R_ip, $R_Prot, 100); 创建监听套接字(SOCKET)
  If $MainSocket = -1 Then Return "1,创建监听套接字失败"
  ; 查看客户端连接
  While 1
    TrayTip("", "等待连接", 3)
    $ConnectedSocket = TCPAccept($MainSocket)
    If $ConnectedSocket >= 0 Then
      $file = FileOpen($R_File, 2 + 8 + 16);打开文件准备接收
      If $file = -1 Then
        Return "2,无法打开文件"
        Exit
      EndIf

      While 1;开始接收文件
        $i += 1
        TrayTip($i, "开始接收文件", 3)
        $sBuff = TCPRecv($ConnectedSocket, 1024 * 1000, 1)
        If @error Then
          FileClose($R_File)
          Return "3,文件接收完成"
        EndIf
        ;写文件
        FileWrite($R_File, $sBuff)
      WEnd

    EndIf
  WEnd
  Return 1
  TCPCloseSocket($ConnectedSocket);关闭套接字,停止TCP服务
  TCPShutdown()
EndFunc ;==>_recvFiles

发送

$S_ip = InputBox("please input ipaddress", "please input ipaddress",@IPAddress1)
$S_File = @ScriptDir & "\S.jpg"
$S_Prot = 65432

$var = _SendFile($S_ip, $S_File, $S_Prot)
MsgBox(0, "", $var)

Func _SendFile($S_ip, $S_File, $S_Prot)
  Local $i
  ;开始tcp服务
  TCPStartup()
  ;创建一个套接字(socket)连接到已经存在的服务器
  TrayTip("", "1 连接到服务器", 3)
  $socket = TCPConnect($S_ip, $S_Prot)
  If $socket = -1 Then
    Return "连接到服务器出错: " & @error
    Exit
  EndIf

  $File = FileOpen($S_File, 16)
  if $File = -1 Then return "2 文件打开失败"
  ;打开读取文件
  While 1
    $i += 1
    TrayTip($i, "3 发送文件", 3)
    $fdata = FileRead($File,1024 * 1000)
    If @error = -1 Then ExitLoop
    TCPSend($socket, $fdata)
  WEnd
  TCPCloseSocket($socket)
  TCPShutdown()
  Return 1
EndFunc ;==>_SendFile
 楼主| 发表于 2010-1-24 19:35:02 | 显示全部楼层
另外。AU3的TCPSend函数没有发现可以更改同步异步的参数。
而据我观察,应该是异步的。
发表于 2010-1-24 20:39:55 | 显示全部楼层
直接调用Ws2_32.dll中以WSA开头的函数吧,那个支持同步异步。
 楼主| 发表于 2010-1-24 23:26:22 | 显示全部楼层
在研究TCP方面的兄弟一起上来讨论啊。
发表于 2010-1-25 09:03:03 | 显示全部楼层
其实这个问题很好,但是就是有点大,抓紧时间说两句。

用同一个socket,就需要两端都知道IP地址和端口。但是,如内网客户端,其对外的端口往往是防火墙或者出口路由器分配的,本地如何得知公网开的IP映射端口呢?更不用提防火墙上的安全策略问题。
发表于 2010-1-25 10:56:50 | 显示全部楼层
这个问题确实存在 继续关注研究中...
 楼主| 发表于 2010-1-25 13:40:31 | 显示全部楼层
回复 3# pusofalse

谢谢提醒 ,我打算先用逻辑方面的办法来用AU3原的函数处理这个问题。
 楼主| 发表于 2010-1-25 13:42:03 | 显示全部楼层
回复 5# remme

以前我试过用函数来检测接收到的内容。如果内容有标记为"eof"就退出接收文件的循环。
但这样效率太低了。为什么?
因为我们每次接收的数据有 1024*1024那么多,而每一个循环都要将收到的二进制转成字符串,再来检测这个字符串是否有结束标记。效率太低了!
 楼主| 发表于 2010-1-25 13:42:17 | 显示全部楼层
回复 6# crwmart

兄弟,一起想想啊,你这方面最行了。
 楼主| 发表于 2010-1-25 13:45:45 | 显示全部楼层
回复 5# remme
"用同一个socket,就需要两端都知道IP地址和端口。但是,如内网客户端,其对外的端口往往是防火墙或者出口路由器分配的,本地如何得知公网开的IP映射端口呢?更不用提防火墙上的安全策略问题。"

关于你上面说这个,只有用同一个SOCKET才是最佳的解决办法。因为只要不用同一个SOCKET都会经过一次以上的连接,特别是外网。
LAN - INTELNET - ADSL
LAN - INTELNET - LAN
ADSL - INTELNET - LAN
三种情况,全要在路由上映射端口。
特别是
LAN - INTELNET - LAN
你根本没有办法去修改对方路由器的端口映射。
发表于 2010-1-25 17:04:13 | 显示全部楼层
我很囧的发现,原来au3的socket的概念和tcp里的不同。c语言就能实现一个socket双向通讯。
 楼主| 发表于 2010-1-25 17:06:26 | 显示全部楼层
回复 11# remme


AU3也是双向的。同一个SOCKET即可以接收也可以发送。
发表于 2010-1-25 17:33:26 | 显示全部楼层
how?我试过了不行啊。
 楼主| 发表于 2010-1-25 17:43:23 | 显示全部楼层
比如,接收端你先侦听端口。
$MainSocket = TCPListen($R_ip, $R_Prot, 100); 创建监听套接字(SOCKET)

成功后,就可以直接用
tcpsend($MainSocket,"发送文字了")

在发送端,你在循环里用。

TCPRecv从 TCPConnect 连接成功的socket里接收就可以了。
发表于 2010-2-8 15:35:58 | 显示全部楼层
测试了n种方法,也没有弄出个所以然来,要在文件发送过程中分检出文件数据包和信息数据包确实很难
您需要登录后才可以回帖 登录 | 加入

本版积分规则

QQ|手机版|小黑屋|AUTOIT CN ( 鲁ICP备19019924号-1 )谷歌 百度

GMT+8, 2024-12-26 09:55 , Processed in 0.083734 second(s), 23 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表