EndReceive in Async Socket without strings - c#

I'm following this example about the creation of an async tcp listener in C#.
MSDN Example
I see that all data is encoded as string to check for message completeness. More precisely, every message sent is already a string, which we append the 'EOF' char to for string termination.
The server side part i'm talking about is in this snippet:
public static void ReadCallback(IAsyncResult ar) {
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject) ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0) {
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1) {
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, content);
} else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
Is there a way, as i usually do with TcpListener/TcpClient classes, to check if received bytes are available on the socket?
I mean something like this:
private void HandleClientConnection(TcpClient client)
{
NetworkStream clientStream = client.GetStream();
MemoryStream memoryStream = new MemoryStream();
while (true)
{
int read = clientStream.ReadByte();
if (read != -1)
{
memoryStream.WriteByte((byte)read);
}
else
{
break;
}
}
}
I'm aware that i probably misunderstood this example, or at least the Begin/End part and the "legacy" async pattern. But this is my goal, do you know some way to get it working without involving strings?

You said : "Is there a way to check if received bytes are available on the socket?"
In general 'EndReceive' will block the thread until data is available. So you don't need to do anything because 'EndReceive' is doing all the job for you.
'bytesRead' is an int that shows you how much data you have received.
a quote from docs.microsoft
The EndReceive method will block until data is available.1
But if you are using a SYNC socket (which you are not) then it's another topic.

Related

How to transfer a file by socket PHP to C# [duplicate]

This question already has answers here:
How to receive a file over TCP which was sent using socket.FileSend Method
(2 answers)
Closed 7 years ago.
Hi after a lot of research i didn't found a solution to my case
I have a C# socket server what's is running on my server and i need to transfer files to it by Socket from my website that's in PHP.
What i have now ?
Php part:
$file = array('file' => Input::file('file'));
$fp = stream_socket_client("tcp://localhost:9192", $error_number, $error_string);
if ( !$fp ) {
echo "$error_number ($error_string)\n";
} else {
fwrite($fp, $file['file']);
}
fclose($fp);
C# Socket Server:
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
// Echo the data back to the client.
Send(handler, content);
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
When i try to make the connection the C# instantly refuses it what i'm doing wrong ?
I'm using Laravel 5 on PHP part.
I found a solution in this question:
How to receive a file over TCP which was sent using socket.FileSend Method
Sorry for waste your time :)

C# Tcp Async socket data read logic

I'm working on a C# Tcp Async socket server program which will simply read socket data and put it into a file. Now the issue is before sending actual file content I'm sending client name and file name. Please note that I'm sending these data from C++ MFC application with simple send() API in below manner.
CString csInitData = "Client_Name | File_Name <EOF>";
send(hSocket, (LPCTSTR)csInitData, csInitData.GetLength(), 0);
Now how do I receive this data inside below async callback function? Cannot figure out the byte break up logic.
public void OnDataReceived(IAsyncResult ar)
{
StateObject state = (StateObject)ar.AsyncState;
Socket clientSocket = state.m_currentSocket;
int bytesRead = clientSocket.EndReceive(ar);
if (bytesRead > 0)
{
//HERE I WANT TO RECEIVE THOSE DATA AND CREATE FOLDER AND FILE WITH THOSE NAMES RECEIVED FROM CLIENT
//Process 'state.dataBuffer'//DATA INSIDE THIS BYTE BUFFER
string fileName = ""; //FILE NAME SHOULD GO HERE
string folderName = ""; //CLIENT NAME SHOULD GO HERE
BinaryWriter writer;
if (!File.Exists(folderName+fileName))
{
Directory.CreateDirectory(folderName);
writer = new BinaryWriter(File.Open(folderName+fileName, FileMode.Create));
}
else
{
writer = new BinaryWriter(File.Open(folderName+fileName, FileMode.Append));
}
writer.Write(state.dataBuffer, 0, bytesRead);
writer.Flush();
writer.Close();
// Recursively receive the rest file.
try
{
clientSocket.BeginReceive(state.dataBuffer, 0, StateObject.BufferSize, 0, new AsyncCallback(OnDataReceived), state);
}
catch
{
if (!clientSocket.Connected)
{
MessageBox.Show("Catched from OnDataReceived!");
}
}
}
else
{
// Signal if all the file received.
}
}
For the buffering, it sounds like you want MemoryStream:
// Somewhere to put the data:
// for simplicity, I'll assume an instance field is fine; could
// also be tracked via async-state object, if preferred
private MemoryStream backlog = new MemoryStream();
if (bytesRead > 0)
{
// we want to append, so move to the end
backlog.Position = backlog.Length;
// copy new data into the buffer
backlog.Write(state.dataBuffer, 0, bytesRead);
...
}
General feedback:
don't use BinaryWriter here; just Stream is fine
use using on the file-stream
have a cached delegate instance in a field so you don't need to new a delegate each time (this can even be static if you pass the target instance as state)
use Path.Combine, preferably once ahead of time, to avoid lots of string concatenations
check for sync-completion; in the callback, check CompletedSynchronously and exit immediately if true; when starting work, capture the IAsyncResult, check CompletedSynchronously, and process right away on the same thread if true
consider moving to the newer async API - ReceiveAsync (this is quite a significant change, though)
don't MessageBox from an IO method

Async socket - Duplex communication with permanent sockets in c#

I'm trying to do asynchronous socket communication, and want the server to keep a list of all connected sockets, so he can broadcast messages to them.
First I took the examples from msdn Asynchronous Socket Server and altered them a little so they don't close the sockets. (just removed the .shutdown and .Close commands)
But doing so, seems to cause the client to hang in the "receiving part".
Here are the changes I made to the msdn examples:
Client:
only changed ReceiveCallback() so it stays in an endless receiving loop:
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
Console.WriteLine(state.sb.ToString());
}
// Signal that all bytes have been received.
receiveDone.Set();
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
The Server:
just commented out the lines that close the socket:
//handler.Shutdown(SocketShutdown.Both);
//handler.Close();
Later i planed to keep a List of Sockets so i can send Messages to them, but it already fails here.
I'm greatfull for any hints, also I would like to hear your opinion on using this tenique for a server that has to serve at max 100 clients, which could send a request any 2 seconds. (The communication should be both ways, so that the client and the Server can send a Message at any time without waiting for a Message to respond to).
Thank you and good evening
Martin
EndReceive only returns 0 when the socket is closed. Your clients will never set the receiveDone handle because the server never closes the socket. The callback is called when data is received OR when the connection is terminated.
You need to detect the end of message (like the example you linked too).
eg. (Modified version of the code you linked)
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1) {
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content );
// Echo the data back to the client.
Send(handler, content);
{ // NEW
// Reset your state (persist any data that was from the next message?)
// Wait for the next message.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
} else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}

How to read all requested data using NetworkStream.BeginRead?

There is a code of async server. Client sends Header - size of Data Block + Data Block.
Server reads asynchronously first Header and then Data Block.
I need, after I read Data Block run the BeginRead for Header reading part, to make threads async.
PROBLEM:
When I got DataCallBack, in line:
int bytesRead = ns.EndRead(result);
I get not all buffer i asked to read in
mc.Client.GetStream().BeginRead(mc.DataBuffer, 0, size, new AsyncCallback(DataCallBack), mc);
If client send 1MB of Data I can get different number of "bytesRead".
QUESTION:
How to force "BeginRead" to read all data from connection. It should cause the new loop of Header - Data.
MyClient - simply wrapper over TcpClient;
CODE:
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
TcpListener listener = (TcpListener)ar.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(ar);
client.NoDelay = false;
// client.ReceiveBufferSize = 1024*1024;
listener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), listener);
MyClient mc = new MyClient(client);
ContinueRead(0,mc);
}
public void ContinueRead(int size, MyClient mc)
{
if (size != 0)
{
mc.DataBuffer = new byte[size];
mc.Client.GetStream().BeginRead(mc.DataBuffer, 0, size, new AsyncCallback(DataCallBack), mc);
}
mc.Client.GetStream().BeginRead(mc.HeaderBuffer, 0, 4, new AsyncCallback(HeaderCallBack), mc);
}
private void HeaderCallBack(IAsyncResult result)
{
MyClient mc = (MyClient)result.AsyncState;
NetworkStream ns = mc.Stream;
int bytesRead = ns.EndRead(result);
if (bytesRead == 0)
throw new Exception();
mc.TotalLengs = BitConverter.ToInt32(mc.HeaderBuffer, 0);
ContinueRead(mc.TotalLengs, mc);
}
private void DataCallBack(IAsyncResult result)
{
MyClient mc = (MyClient)result.AsyncState;
NetworkStream ns = mc.Stream;
int bytesRead = ns.EndRead(result);
if (bytesRead == 0)
throw new Exception();
BAD CODE - MAKES ASYNC READING - SYNC
while (bytesRead < mc.TotalLengs)
{
bytesRead += ns.Read(mc.DataBuffer, bytesRead, mc.TotalLengs - bytesRead);
}
END BAD CODE
ContinueRead(0, mc);
ProcessPacket(mc.DataBuffer, mc.IP);
}
"If client send 1MB of Data I can get different number of "bytesRead"."
Yes...this is simply how TCP works under the hood. You can't change this. TCP guarantees the order of packets, not how they are grouped. The hardware and traffic conditions along the route the packets travel determine how that data is grouped (or un-grouped).
"How to force "BeginRead" to read all data from connection."
TCP has no idea how much data is being sent. As far as it is concerned, the connection is simply an endless stream of bytes; therefore it cannot read "all data" since there is no end to the data (from its perspective). TCP also has no notion of what a "complete message" is with respect to your application. It is up to you, the programmer, to develop a protocol that allows your application to know when all data has been sent.
If you are expecting a certain number of bytes, then keep a running sum of the values returned by EndRead() and stop when that magic number is hit.

How to determine the end of a file transfer (data) from the client to the server (Socket)

Simple situation:
The client on a Socket sends a pieces(e.g., 256 byte) of the file (data) in the format byte []to the server. The server receives the data asynchronously.
How to determine when a file (data) is transmitted completely? (Server-side)
Here's the code on server-side responsible for receiving data
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
BinaryWriter writer = new BinaryWriter(File.Open(#"D:\test.png", FileMode.Append));
writer.Write(state.buffer, 0, bytesRead);
writer.Close();
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket!",
bytesRead);
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
Is there a method that allows to make the some following?
if (bytesRead > 0)
{
....
if(state.buffer!=end of receive)
{
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
Or, I may to try add some information to the this byte[] object (e.g., some string with <EOF> tag)
but I must to analyse this info on each step.
May I do this check more simple and how? Or use another way ...
The only way to do it is to sent a header (of specific size) in front of every message. So, the each message should consist of header and body. The data stream should look like this:
[HEADER][BODY][HEADER][SOME BIGGER BODY][HEADER][SOME EXTRA BIG BODY]
As I said, the header should be of specific size and should contain some custom service fields inlcuding the size of the message's body in bytes. In your case the header could contain only body size, i.e. int value (4 bytes). The receive process should look like this:
Receive 4 bytes (header);
Retrieve body size from header (just convert header to int);
Receive the number of bytes specified in header (i.e. receive message body);
Handle message body;
Go to 1.
I know, it may seem complicated for you, but it is the common way to do it. But you can simplify the code by using Rx library. After implementing some extensions methods for socket (WhenReadExact, the implementation could easily be found over Internet, for example here), the code will look like this:
var whenFileReceived = from header in socket.WhenReadExact(4)
let bodySize = BitConverter.ToInt32(header)
from body in socket.WhenReadExact(bodySize)
select body;
whenFileReceived.Subscribe(
file =>
{
// Handle file here
});

Categories