simple Socket Programming question.

F

Francois Malgreve

Hi,

I am a bit new in socket programming and I have a question about what's the
best way to implemnt a TCPClient that can receive long messages?

The example I found in MSDN works for me but it downloads only the first
chunck of data sent by the server I connect to. How can i make the code wait
until the end of the message is received from the server?

Changing the buffer size won't change anything as i guess the stream.Read()
method returns after the first bunch of data is received from the server. I
don't really know how many packets is that neither why it returns before
everything is read but i guess that i need to add in some infrastructure
that will do some waiting and / or looping and which will detect when all
the data is read and that i can exit.

I have done some research on the internet and the example i saw often did
not care for this kind of longer message or were too complex or not working
if not communicating with their own server implementation. I have no access
on the server I connect to then I cannot change anything on that side.

Here is the code i use from MSDN doc (TCPClient class):
static void Connect(String server, String message)
{
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
Int32 port = 13000;
TcpClient client = new TcpClient(server, port);

// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);

// Get a client stream for reading and writing.
// Stream stream = client.GetStream();

NetworkStream stream = client.GetStream();

// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);

Console.WriteLine("Sent: {0}", message);

// Receive the TcpServer.response.

// Buffer to store the response bytes.
data = new Byte[256];

// String to store the response ASCII representation.
String responseData = String.Empty;

// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);

// Close everything.
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("\n Press Enter to continue...");
Console.Read();
}
Anyone can help me or point me to an adequate resource?Thanks a lot,Francois
Malgreve
 
B

Barry Kelly

Francois said:
I am a bit new in socket programming and I have a question about what's the
best way to implemnt a TCPClient that can receive long messages?

With blocking reads (i.e. you're not using Begin* and End*), you run in
a loop reading from the stream / socket until either (1) something
you've read tells you that the stream has ended, or (2) the read returns
0, zero bytes read.

For an example of (1), HTTP 1.1 with keep-alive requires a header in the
response stream to tell the client how much data to expect. It looks
like "Content-Length: <some number>\r\n". So, to read this kind of data,
you'd read in the headers and parse them until the blank line in the
HTTP response, then read that exact number of bytes in by looping around
calling Socket.Read() or NetworkStream.Read().

For an example of (2), see the code as I've modified it below:
// Receive the TcpServer.response.

// Buffer to store the response bytes.
data = new Byte[256];

// String to store the response ASCII representation.
String responseData = String.Empty;

MemoryStream buffer = new MemoryStream();
for (;;)
{
int read = stream.Read(data, 0, data.Length);
if (read == 0) // read of 0 bytes indicates end of stream.
break;
buffer.Write(data, 0, read);
}

// Now, buffer has all the data. You can convert it into an array
// with ToArray() if desired.
// Close everything.
stream.Close();
client.Close();
}

You should use 'using' to dispose of both the client and the stream. If
an exception occurs in your existing code before these lines, the stream
/ client will never be closed.

For actual tactics that will work for long-running downloads of large
amounts of data, you could look into (1) streaming the download to a
file on disk, and (2) getting a handle on how asynchronous sockets work
to avoid lots of threads if you've got lots of concurrent downloads.

-- Barry
 
F

Francois Malgreve

Dear Barry,

Thanks a lot for the quick answer, I found myself finally doing nearly the
same thing.

int numberOfBytesRead = stream.Read(data, 0, data.Length);
string returndata = System.Text.Encoding.ASCII.GetString(data, 0,
numberOfBytesRead);

while (numberOfBytesRead > 0)
{
numberOfBytesRead = stream.Read(data, 0, data.Length);
returndata += System.Text.Encoding.UTF8.GetString(data, 0,
numberOfBytesRead);
}

And you are right about the "using", I will implement it right away.

I don't have more than 1 concurrent download at a type then i don't need a
asynchronous system. Nevertheless, my file downloaded can be pretty large
and I would be really glad if you could point me to some information
concerning the streaming strategy. Right now i put everything in a String as
you can see in my code snippet and then write the string in a text file.

Thanks again,

Francois Malgreve.

You are ight
Barry Kelly said:
Francois said:
I am a bit new in socket programming and I have a question about what's
the
best way to implemnt a TCPClient that can receive long messages?

With blocking reads (i.e. you're not using Begin* and End*), you run in
a loop reading from the stream / socket until either (1) something
you've read tells you that the stream has ended, or (2) the read returns
0, zero bytes read.

For an example of (1), HTTP 1.1 with keep-alive requires a header in the
response stream to tell the client how much data to expect. It looks
like "Content-Length: <some number>\r\n". So, to read this kind of data,
you'd read in the headers and parse them until the blank line in the
HTTP response, then read that exact number of bytes in by looping around
calling Socket.Read() or NetworkStream.Read().

For an example of (2), see the code as I've modified it below:
// Receive the TcpServer.response.

// Buffer to store the response bytes.
data = new Byte[256];

// String to store the response ASCII representation.
String responseData = String.Empty;

MemoryStream buffer = new MemoryStream();
for (;;)
{
int read = stream.Read(data, 0, data.Length);
if (read == 0) // read of 0 bytes indicates end of stream.
break;
buffer.Write(data, 0, read);
}

// Now, buffer has all the data. You can convert it into an array
// with ToArray() if desired.
// Close everything.
stream.Close();
client.Close();
}

You should use 'using' to dispose of both the client and the stream. If
an exception occurs in your existing code before these lines, the stream
/ client will never be closed.

For actual tactics that will work for long-running downloads of large
amounts of data, you could look into (1) streaming the download to a
file on disk, and (2) getting a handle on how asynchronous sockets work
to avoid lots of threads if you've got lots of concurrent downloads.

-- Barry
 
B

Barry Kelly

Francois said:
I don't have more than 1 concurrent download at a type then i don't need a
asynchronous system. Nevertheless, my file downloaded can be pretty large
and I would be really glad if you could point me to some information
concerning the streaming strategy. Right now i put everything in a String as
you can see in my code snippet and then write the string in a text file.

As you saw in the sample I wrote, I used a MemoryStream; you could
consider using a FileStream instead.

BTW, if the data is text, then you don't need to pass it through
Encoding.ASCII - leave it alone. Any bytes with a value > 127 get
converted to character value 63 ('?') by Encoding.ASCII.

-- Barry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top