tags:

views:

40

answers:

1

I've this nice ASYNC tcp stream class that I'm using to connect to perl tcp server which then every some time sends msg to it. It usually isn't very long from 30 chars to 200-500 maybe. 95% it works fine but i've got a problem with it that it often sends the message cut off (by some chars) and then sends the rest of it in another message which kinda breaks things up. Could anyone suggest how to overcome this issue? I've tried to change some things around but weren't really able to fix it.

public class TcpStream {
    private readonly NetworkStream _stream;
    public event EventHandler<TcpDataEventArgs> DataReceived;
    public event EventHandler<TcpErrorEventArgs> ErrorOccurred;
    public TcpStream(NetworkStream stream) {
        this._stream = stream;
    }
    public void BeginRead() {
        var state = new SocketState {Stream = _stream};
        //var bytes = new byte[1024];

        //IAsyncResult ar = _stream.BeginRead(state.Data, 0, state.Data.Length, HandleRead, state);
        IAsyncResult ar = _stream.BeginRead(state.Data, 0, state.Data.Length, HandleRead, state);


    }
    public void Send(byte[] data, ProtocolConnection protocolConnection) {
        //try {
        _stream.Write(data, 0, data.Length);
        //} catch (Exception ex) {
        //     OnError(new TcpErrorEventArgs(ex, stream, protocolConnection));
        // }
    }
    public void Send(String data, ProtocolConnection protocolConnection) {
        byte[] d = Encoding.Default.GetBytes(data);
        Send(d, protocolConnection);
    }
    public void Close() {
        _stream.Close();
    }
    public void Close(int timeout) {
        _stream.Close(timeout);
    }

    public NetworkStream BaseStream {
        get { return _stream; }
    }

    private void HandleRead(IAsyncResult ar) {
        var state = (SocketState) ar.AsyncState;
        NetworkStream stream = state.Stream;
        int r;
        // check to see if stream has been closed
        if (!stream.CanRead)
            return;
        // try {
        r = stream.EndRead(ar);
        // } catch (Exception ex) {
        //      OnError(new TcpErrorEventArgs(ex, stream, _protocolConnection));
        //      return;
        //  }
        var rdata = new byte[r];
        //var rdata = new byte[1024];
        Buffer.BlockCopy(state.Data, 0, rdata, 0, r);
        OnData(new TcpDataEventArgs(rdata));
        // try {
        BeginRead();
        //  } catch (Exception ex) {
        //      OnError(new TcpErrorEventArgs(ex, stream, _protocolConnection));
        //  }
    }
    private void OnData(TcpDataEventArgs args) {
        EventHandler<TcpDataEventArgs> temp = DataReceived;
        if (temp != null)
            temp(this, args);
    }
    private void OnError(TcpErrorEventArgs args) {
        EventHandler<TcpErrorEventArgs> temp = ErrorOccurred;
        if (temp != null)
            temp(this, args);
    }

    private class SocketState {
        public readonly byte[] Data = new byte[100];
        public NetworkStream Stream;
    }

    public class TcpDataEventArgs : EventArgs {
        public readonly byte[] Data;
        public TcpDataEventArgs(byte[] data) {
            this.Data = data;
        }
    }

    public class TcpErrorEventArgs : EventArgs {
        public readonly Exception Error;
        private NetworkStream Stream;
        public TcpErrorEventArgs(Exception error, NetworkStream stream) {
            this.Error = error;
            this.Stream = stream;
        }
    }
}
+2  A: 

TCP is a byte-stream protocol. There is no direct relation between the transmitted packets and your 'messages'. It is up to you to read the bytes and find the messages.

A very simple example: If the messages are lines (strings ending in LF or CRLF), you could attach a StreamReader to the Networkstream and use ReadLine. Don't under-estimate the convenience of doing this, a UTF-8 encoded character could be split over 2 packets.

Henk Holterman