.NET新手套接字问题

时间:2010-02-21 16:17:11

标签: c# .net sockets

我有一个我用过的C#客户端/服务器网络程序 TCPListener和TCPClient类。服务器正在读取所有内容 从客户端(少量的xml)就好了,直到我尝试发送一个 大文件(100k)回到客户端。

我正在使用流功能 客户端和服务器都具有非阻塞套接字功能。当我做的时候 socket.SendFile(“filename”)回到客户端,文件正在获取 切断 - 我已经将客户端上的接收缓冲区大小设置为过去了 100k,但它仍然被切断约25k和通信 客户端和服务器之间的关系是不可靠的。

我的基本问题 如果数据以某种方式留在管道中会发生什么?那就是 由下一个socket读取。阅读...每个发送呼叫都需要完全正确 一个也只有一个读?也许我没有给客户足够的时间 读取文件,但他们两个在同一台机器上,我试过 在各个地方睡了几秒钟没有成功。

5 个答案:

答案 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)

尝试以块的形式从服务器端发送块。正如对方所说,发布代码对我们有很大的帮助。

相关问题