我有一个我用过的C#客户端/服务器网络程序 TCPListener和TCPClient类。服务器正在读取所有内容 从客户端(少量的xml)就好了,直到我尝试发送一个 大文件(100k)回到客户端。
我正在使用流功能 客户端和服务器都具有非阻塞套接字功能。当我做的时候 socket.SendFile(“filename”)回到客户端,文件正在获取 切断 - 我已经将客户端上的接收缓冲区大小设置为过去了 100k,但它仍然被切断约25k和通信 客户端和服务器之间的关系是不可靠的。
我的基本问题 如果数据以某种方式留在管道中会发生什么?那就是 由下一个socket读取。阅读...每个发送呼叫都需要完全正确 一个也只有一个读?也许我没有给客户足够的时间 读取文件,但他们两个在同一台机器上,我试过 在各个地方睡了几秒钟没有成功。
答案 0 :(得分:2)
您很可能无法通过一次读取呼叫读取整个消息(可能所有数据尚未到达)。在网络编程中,您经常将调用放在一个while循环中,只需读取()直到收到整个预期的消息。
答案 1 :(得分:2)
1发送呼叫可能需要多个读取呼叫才能接收,1个读取呼叫可能会读取多个发送呼叫发送的数据。
TCP只提供一个流,因此您需要定义如何对您发送的数据或消息进行分区。
在这种情况下,您可能只需要循环,执行Read直到流关闭。
答案 2 :(得分: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();
}
}
答案 3 :(得分: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将字节复制到结构中。这样,您始终可以知道要到达的数据以及需要调用Read()的次数。
标题大小字段也有助于对协议进行版本控制(保持结构相同,但为以后的客户端添加它,以便您可以保持向后兼容性)。如果你在C#中定义结构,请务必这样做:
[StructLayout LayoutKind.Sequential]
struct MessageHeader
{
public int HeaderSize;
public int FileNameOffset;
public int FileNameSize;
public int FileDataOffset;
public int FileDataSize;
}
然后Marshal.PtrToStructure
将允许您从套接字读取的byte []中创建此结构的实例
答案 4 :(得分:0)
尝试以块的形式从服务器端发送块。正如对方所说,发布代码对我们有很大的帮助。