开发者

.NET newbie socket problem

I have a C# client/server network program I've written using TCPListener and TCPClient classes. The server is reading everything from the client (small amounts of xml) just fine until I try to send a large file (100k) back to the client.

I'm using stream functions for both client and server with non-blocking socket functions. When I do a socket.SendFile("filename") back to the client, the file is getting cut off - I've set开发者_高级运维 the receive buffer size on the client to well past 100k but it still gets cut off around 25k and the communication between client and server is unreliable afterwords.

My basic question is what happens if data is somehow left in the pipe ? i.e.. will it be read by the next socket.Read... Does every Send call require exactly one and only one Read ? Maybe I'm not giving the client enough time to read the file but their both on the same machine and I've tried sleeping for a few seconds in various places w/o success.


It is very possible that you cannot read the entire message through one Read call (perhaps all data has not arrived yet). In network programming, you would often place the call to Read in a while loop and simply Read() until you have received the entire expected message.


1 Send call might take more than one Read call to receive, and 1 Read call might read the data send by several Send call.

TCP just provides a stream, so it's up to you to define how the data or messages you send are partitioned.

In this case, you probably just need to loop ,doing Read until the stream is closed.


You probably want something like this:

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();
    }
}


What I would usually do is create a header structure that is sent

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]

and then that header is read using either a BinaryReader or copying the bytes to a struct using Marshal. This way you always know what data is arriving and how many times you need to call Read().

The header size field is also helps with versioning the protocol (keep the structure the same but add to it for later clients so you can keep backwards compatibility). If you define the structure in C# be sure to do it like so:

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

Then Marshal.PtrToStructure will allow you do create an instance of this struct right from the byte[] you read from the socket


Try sending the chunk from the server side in chunks. Just as the other said, posting the code would be of great help to us.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜