如何使用TcpListener / Client通过tcp发送文件? SocketException问题

时间:2009-06-18 15:18:08

标签: c# .net file tcp

我正在开发一个使用TCPListener和TCPClient类通过TCP发送文件的简单应用程序。这是发送文件的代码。

Stop是一个易失性布尔值,可以随时停止进程,WRITE_BUFFER_SIZE可能会在运行时更改(另一个易失性)

while (remaining > 0 && !stop)
{
  DateTime current = DateTime.Now;
  int bufferSize = WRITTE_BUFFER_SIZE;
  buffer = new byte[bufferSize];
  int readed = fileStream.Read(buffer, 0, bufferSize);
  stream.Write(buffer, 0, readed);
  stream.Flush();
  remaining -= readed;
  // Wait in order to guarantee send speed
  TimeSpan difference = DateTime.Now.Subtract(current);
  double seconds = (bufferSize / Speed);
  int wait = (int)Math.Floor(seconds * 1000);
  wait -= difference.Milliseconds;
  if (wait > 10)
    Thread.Sleep(wait);
}                
stream.Close();

这是处理接收方的代码:

do
{
  readed = stream.Read(buffer, 0, READ_BUFFER_SIZE);
  // write to .part file and flush to disk
  outputStream.Write(buffer, 0, readed);
  outputStream.Flush();
  offset += readed;
} while (!stop && readed > 0);

现在,当速度很低(大约5KBps)时,一切正常,但是,当我提高速度时,接收器大小在从流中读取时更容易引发SocketException。我猜测它与所有数据都可以读取之前关闭的远程套接字有关,但是这样做的正确方法是什么?我该什么时候关闭发送客户端?

我没有在谷歌上找到任何关于文件传输的好例子,我发现的那些与我正在做的事情有类似的实现,所以我想我错过了什么。

编辑:我收到此错误“无法从传输连接中读取数据”。这是一个IOException,其内部异常是SocketException。

我在发送者函数中添加了这个,但是我得到了相同的错误,代码永远不会到达stream.close(),当然tcpclient永远不会真正关闭...所以我现在完全迷失了。

buffer = new byte[1];
client.Client.Receive(buffer);
stream.Close();

1 个答案:

答案 0 :(得分:1)

通常,您希望在套接字上设置LINGER选项。在C ++下,这将是SO_LINGER,但在Windows下,这实际上并不像预期的那样工作。你真的想这样做:

  • 完成发送数据。
  • 使用how参数设置为1来调用shutdown()。
  • 循环使用recv()直到它返回0.
  • 调用closesocket()。

取自:http://tangentsoft.net/wskfaq/newbie.html#howclose

C#sharp可能已在其库中对此进行了更正,但我对此表示怀疑,因为它们是基于winsock API构建的。

修改

更详细地查看您的代码。我发现你根本没有发送任何标题,所以在接收方你不知道你实际应该读多少字节。知道要读取套接字的字节数使得调试更加容易。请记住,如果不正确关闭套接字,关闭套接字仍然可以剪断最后一位数据。

另外,让缓冲区大小变得不稳定并不是线程安全的,并且真的不会给你带来什么。使用stop作为volatile是安全的,但不要指望它是即时的。换句话说,循环可以在获得更新的stop值之前运行几次。在多处理器机器上尤其如此。

<强> Edit_02:

对于TCPClientClass,您希望执行以下操作(据我所知,目前无法访问C#)。

// write all the bytes
// Then do the following

client.client.Shutdown(Shutdown.Send) // This assumes you have access to this protected member
while (stream.read(buffer, 0, READ_BUFFER_SIZE) != 0);
client.close()