2010-02-21 47 views
3

我有一个C#客户端/服务器网络程序,我用 写的TCPListener和TCPClient类。服务器从客户端阅读所有 (少量的xml)就好了,直到我试图发送一个 大文件(100K)返回给客户端。NET新手套接字问题

我使用流功能为 客户端和服务器与非阻塞套接字函数。当我做一个 socket.SendFile(“文件名”)回到客户端,该文件得到 截断 - 我已经设置客户端接收缓冲区大小以及过去 100k但它仍然被切断约25k并且客户端和服务器之间的通信 是不可靠的后缀。

我的基本问题是 如果数据在某种程度上管离开会发生什么?即......将由下一个socket读取 .Read ...是否每个发送呼叫都需要完全 一个且只有一个Read?也许我没有给客户足够的时间 阅读文件,但他们都在同一台机器上,我试过 在不同的地方没有成功睡几秒钟。

+3

线 - 所读取的字节[]正确的结构创建一个实例中断使文本更易于阅读。 – 2010-02-21 16:18:46

+1

你能提供客户端和服务器的代码吗? – Ikaso 2010-02-21 16:19:15

+1

如果可以,发布一些代码。没有看到你已经实施了什么,很难回答你的问题。 – driis 2010-02-21 16:19:37

回答

0

尝试从块中的服务器端发送块。就像其他人所说的那样,发布代码对我们有很大的帮助。

2

很可能您无法通过一次Read调用读取整个消息(也许所有数据还没有到达)。在网络编程中,您通常会将呼叫置于一个while循环中,然后简单地读取(),直到您收到完整的预期消息。

+2

添加一个指定传入消息长度的标头:'while(readBuffer IAbstract 2010-02-21 16:40:00

+0

或者,如果您只想读取每个连接发送的所有数据,请使用http:// stackoverflow的.com /问题/ 2582947/C-性能的方法 - 的接收数据从 - 一个插槽/ 5504052#5504052 – Pat 2011-03-31 17:46:26

1

你可能想是这样的:

socket.Blocking = false; 


const int ChunkSize = 1492; 
const int ReceiveTimeout = 10000; 
const int SendTimeout = 10000; 

public void Send(Byte[] data) 
{ 
    var sizeBytes = BitConverter.GetBytes(data.Length); 
    SendInternal(sizeBytes); 
    SendInternal(data); 
} 

public Byte[] Receive() 
{ 
    var sizeBytes = ReceiveInternal(4); 
    var size = BitConverter.ToInt32(sizeBytes, 0); 
    var data = ReceiveInternal(size); 
    return data; 
} 

private void SendInternal(Byte[] data) 
{ 
    var error = SocketError.Success; 
    var lastUpdate = Environment.TickCount; 
    var size = data.Length; 
    var count = 0; 
    var sent = 0; 

    while (sent < size) 
    { 
     count = Math.Min(ChunkSize, size - sent); 
     count = socket.Send(data, sent, count, SocketFlags.None, out error); 

     if (count > 0) 
     { 
      sent += count; 
      lastUpdate = Environment.TickCount; 
     } 

     if (error != SocketError.InProgress && error != SocketError.Success && error != SocketError.WouldBlock) 
      throw new SocketException((Int32)error); 
     if (Environment.TickCount - lastUpdate > SendTimeout) 
      throw new TimeoutException("Send operation timed out."); 
     if (count == 0 && !socket.Poll(100, SelectMode.SelectWrite)) 
      throw new SocketException((Int32)SocketError.Shutdown); 
    } 
} 

private Byte[] ReceiveInternal(Int32 size) 
{ 
    var error = SocketError.Success; 
    var lastUpdate = Environment.TickCount; 
    var buffer = new Byte[ChunkSize]; 
    var count = 0; 
    var received = 0; 

    using (var ms = new MemoryStream(size)) 
    { 
     while (received < size) 
     { 
      count = Math.Min(ChunkSize, size - received); 
      count = socket.Receive(buffer, 0, count, SocketFlags.None, out error); 

      if (count > 0) 
      { 
       ms.Write(buffer, 0, count); 
       received += count; 
       lastUpdate = Environment.TickCount; 
      } 

      if (error != SocketError.InProgress && error != SocketError.Success && error != SocketError.WouldBlock) 
       throw new SocketException((Int32)error); 
      if (Environment.TickCount - lastUpdate > ReceiveTimeout) 
       throw new TimeoutException("Receive operation timed out."); 
      if (count == 0 && socket.Poll(100, SelectMode.SelectRead) && socket.Available == 0) 
       throw new SocketException((Int32)SocketError.Shutdown); 
     } 

     return ms.ToArray(); 
    } 
} 
2

1发送呼叫可能需要一个以上的读取调用接收和1个读取调用可能读取的数据由几个发送调用发送。

TCP只是提供一个流,所以由您决定如何分配您发送的数据或消息。

在这种情况下,您可能只需要循环,直到流被关闭。

1

我通常会做的是什么创建发送

Header Size (int, 4 bytes) 
File Name Offset (int, 4 bytes) 
File Name Size (int , 4 bytes) 
File Data Offset (int, 4 bytes) 
File Data Size (int , 4 bytes) 
[ message data here] 

,然后是头部被读取使用一个BinaryReader在或复制字节使用Marshal一个struct头结构。这样你就可以知道数据到达了多少次,你需要调用Read()几次。

头部大小字段也有助于对协议进行版本控制(保持结构相同,但添加到后续客户端以便您可以保持向后兼容性)。如果定义在C#中的结构一定要做到这一点,像这样:

[StructLayout LayoutKind.Sequential] 
struct MessageHeader 
{ 
    public int HeaderSize; 
    public int FileNameOffset; 
    public int FileNameSize; 
    public int FileDataOffset; 
    public int FileDataSize; 
} 

然后Marshal.PtrToStructure将允许你做这从您从插座