Question

I've implemented this C# client which communicate with asynchronous connection to another server I've created in Java. This is the client:

public class NetworkTransaction
{
    private GenericRequest request;
    private const string serverName = "192.168.1.101";
    private const int serverPort = 7777;
    private const int TIMEOUT_MILLISECONDS = 3000;
    private Delegate method;

    public NetworkTransaction(GenericRequest request, Delegate method)
    {
        this.request = request;
        this.method = method;
    }

    public void SendRequest()
    {
        SocketAsyncEventArgs connectionOperation = new SocketAsyncEventArgs();
        DnsEndPoint hostEntry = new DnsEndPoint(serverName, serverPort);
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        connectionOperation.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArg_Completed);
        connectionOperation.RemoteEndPoint = hostEntry;
        connectionOperation.UserToken = socket;

        try
        {
            socket.ConnectAsync(connectionOperation);
        }
        catch (SocketException ex)
        {
            throw new SocketException((int)ex.ErrorCode);
        }
    }

    private void SocketEventArg_Completed(object sender, SocketAsyncEventArgs e)
    {
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Connect:
                ProcessConnectCompleted(e);
                break;
            case SocketAsyncOperation.Receive:
                ProcessReceiveCompleted(e);
                break;
            case SocketAsyncOperation.Send:
                ProcessSendCompleted(e);
                break;
            default:
                throw new Exception("Invalid operation completed");
        }
    }

    private void ProcessConnectCompleted(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(request.ToJson() + "\n\r");
            e.SetBuffer(buffer, 0, buffer.Length);
            Socket sock = e.UserToken as Socket;
            sock.SendAsync(e);
        }
    }

    private void ProcessSendCompleted(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            Socket sock = e.UserToken as Socket;
            sock.ReceiveAsync(e);
        }
    }

    private void ProcessReceiveCompleted(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            string dataFromServer = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
            Socket sock = e.UserToken as Socket;
            sock.Shutdown(SocketShutdown.Send);
            sock.Close();

            method.DynamicInvoke(dataFromServer);
        }
    }
}

What can I do to get long messages from the server? What do I need to change in order to make the client try and read more data to see if I actually read the entire message? Since there might be a problem with the line: string dataFromServer = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred); which receives only a part of the message and not the entire one.

Was it helpful?

Solution

What do i need to change in order to make the client try and read more data to see if i actually read the entire message?

You have to implement a protocol that says "Here come 50 bytes" or "This was all data, goodbye".

ProcessReceiveCompleted() will be called whenever a piece of data is received, this can be anything between one and a lot of bytes. Sockets do not guarantee a "message" to be sent as a whole, since messages don't exist at that level. The amount of data received is dependant on factors like CPU and network utilization.

That's where your protocol comes in. This has to be defined in both server and client. HTTP, for example, uses two CRLF's to let the client say "This was my request, please respond now". The server, on the other hand, (by default) closes the connection when all data is sent, but also sends a Content-length header, so the client can verify it has received all data.

So you'll have to store the data in ProcessReceiveCompleted() and start processing when you have received a whole message.

OTHER TIPS

The answer which was given to me about creating a protocol helped me the most. What i eventually did was attaching a "header" to the message stating it's size. The client then parse the header and just checks if there is still more data to be sent according to message size header and the actual number of bytes received. If there is still more data left to be read then i would just do:

if (messageSizeRemaining > 0)
{
      sock.ReceiveAsync(e);
      return;
}

If you use web services, you could use a Duplex Channel. Where the client registers for a data stream at the server using something like:

[OperationContract(IsOneWay = true)]
void RequestData();

And the server would send the data back calling an OneWay operation to the client, like:

[OperationContract(IsOneWay = true)]
void SendNewData(byte[] data, bool completed)

The client would combine all the data together and when receiving the completed flag it would unregister from the server.

To read more about DuplexChannels look here: Duplex Services.

I've used the following methods synchronously (I know you want async) to acquire data from HTTP

    public static Stream getDataFromHttp(string strUrl)
    {
        Stream strmOutput = null;
        try
        {
            HttpWebRequest request;
            Uri targetUri = new Uri(strUrl);
            request = (HttpWebRequest)HttpWebRequest.Create(targetUri);
            request.Timeout = 5000;
            request.ReadWriteTimeout = 20000;
            request.Method = "Get";


            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)";
            if (request != null)
            {
                request.GetResponse();
                if (request.HaveResponse)
                {
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    strmOutput = response.GetResponseStream();
                }
            }

        }
        catch (WebException wex)
        {

        }
        catch (Exception ex)
        {

        }
        return strmOutput;
    }

Now to convert the stream to text use the following:

    Public Static String StreamToString(Stream stream)
    {
            StreamReader SR = new StreamReader(stream);
            try
            {
               String strOutput = SR.ReadToEnd();
               Return strOutput;
             }
             catch(Exception ex)
             {
                  return ex.message
             }
   }

Hope this works

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top