I've a server running MPD (music player daemon) which communicates via sockets. Now I'm trying to implement the MPD protocol in a Windows store app. Basically I send a command and receive a list which has as the last line "OK". As long as the receving list is smaller than the receive buffer everything is ok. But if I need to load data which is bigger than the buffer the wired stuff starts.
When calling the SendCommand the first time I receive only part of the data, the rest is received when calling SendCommand a second time. When called once more I receive all data as expected. When doing this in a WPF program on the same machine everything is fine.
This is my code:
public async Task<string> SendCommand(MpdProtocol.MpdCommand command)
{
DataWriter writer = new DataWriter(streamSocket.OutputStream);
string res = string.Empty;
writer.WriteString(command.ToString());
await writer.StoreAsync();
res = await ReadResponse();
writer.DetachBuffer();
writer.DetachStream();
return res;
}
private async Task<string> ReadResponse()
{
DataReader reader = new DataReader(streamSocket.InputStream);
reader.InputStreamOptions = InputStreamOptions.Partial;
StringBuilder response = new StringBuilder();
const uint MAX_BUFFER = 8 * 1024;
uint returnBuffer = 0;
do
{
returnBuffer = await reader.LoadAsync(MAX_BUFFER);
response.Append(reader.ReadString(reader.UnconsumedBufferLength));
} while (returnBuffer >= MAX_BUFFER);
reader.DetachBuffer();
reader.DetachStream();
return response.ToString();
}
I've played around with the ReadResponse method but nothing worked.
Can someone point me to the right direction?
Finally, I've found a solution to get the communication working: reading the stream byte by byte with reader.ReadByte() and check each received line (they are separated by "\n") for being "OK".
The problem is reader.ReadString. This methond is good when you know how long the string is you'll try to receive. In my case I have no idea about the size of the string. All I know is that the last line will be an "OK" string.
My error was to belive - as Peter mentioned - that as long as there is data to retrive the recive buffer will be filled completely and only the last call to reader.ReadString will be smaller than the max buffer size. I've also tried to rewrite the ReadResponse function in different ways but nothing worked for reader.ReadString.
Related
I want to create a TCP listener for my .NET Core project. I'm using Kestrel and configured a new ConnectionHandler for this via
kestrelServerOptions.ListenLocalhost(5000, builder =>
{
builder.UseConnectionHandler<MyTCPConnectionHandler>();
});
So what I have so far is
internal class MyTCPConnectionHandler : ConnectionHandler
{
public override async Task OnConnectedAsync(ConnectionContext connection)
{
IDuplexPipe pipe = connection.Transport;
PipeReader pipeReader = pipe.Input;
while (true)
{
ReadResult readResult = await pipeReader.ReadAsync();
ReadOnlySequence<byte> readResultBuffer = readResult.Buffer;
foreach (ReadOnlyMemory<byte> segment in readResultBuffer)
{
// read the current message
string messageSegment = Encoding.UTF8.GetString(segment.Span);
// send back an echo
await pipe.Output.WriteAsync(segment);
}
if (readResult.IsCompleted)
{
break;
}
pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
}
}
}
When sending messages from a TCP client to the server application the code works fine. The line await pipe.Output.WriteAsync(segment); is acting like an echo for now.
Some questions come up
What response should I send back to the client so that it does not run into a timeout?
When should I send back the response? When readResult.IsCompleted returns true?
How should I change the code to fetch the whole message sent by the client? Should I store each messageSegment in a List<string> and join it to a single string when readResult.IsCompleted returns true?
that is entirely protocol dependent; in many cases, you're fine to do nothing; in others, there will be specific "ping"/"pong" frames to send if you just want to say "I'm still here"
the "when" is entirely protocol dependent; waiting for readResult.IsCompleted means that you're waiting for the inbound socket to be marked as closed, which means you won't send anything until the client closes their outbound socket; for single-shot protocols, that might be fine; but in most cases, you'll want to look for a single inbound frame, and reply to that frame (and repeat)
it sounds like you might indeed be writing a one-shot channel, i.e. the client only sends one thing to the server, and after that: the server only sends one thing to the client; in that case, you do something like:
while (true)
{
var readResult = await pipeReader.ReadAsync();
if (readResult.IsCompleted)
{
// TODO: not shown; process readResult.Buffer
// tell the pipe that we consumed everything, and exit
pipeReader.AdvanceTo(readResultBuffer.End, readResultBuffer.End);
break;
}
else
{
// wait for the client to close their outbound; tell
// the pipe that we couldn't consume anything
pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
}
As for:
Should I store each messageSegment in a List<string> and join it to a single string when
The first thing to consider here is that it is not necessarily the case that each buffer segment contains an exact number of characters. Since you are using UTF-8, which is a multi-byte encoding, a segment might contain fractions of characters at the start and end, so: decoding it is a bit more involved than that.
Because of this, it is common to check IsSingleSegment on the buffer; if this is true, you can just use simple code:
if (buffer.IsSingleSegment)
{
string message = Encoding.UTF8.GetString(s.FirstSpan);
DoSomethingWith(message);
}
else
{
// ... more complex
}
The discontiguous buffer case is much harder; basically, you have two choices here:
linearize the segments into a contiguous buffer, probably leasing an oversized buffer from ArrayPool<byte>.Shared, and use UTF8.GetString on the correct portion of the leased buffer
use the GetDecoder() API on the encoding, and use that to populate a new string, which on older frameworks means overwriting a newly allocated string, or in newer frameworks means using the string.Create API
Frankly, "1" is much simpler. For example (untested):
public static string GetString(in this ReadOnlySequence<byte> payload,
Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
return payload.IsSingleSegment ? encoding.GetString(payload.FirstSpan)
: GetStringSlow(payload, encoding);
static string GetStringSlow(in ReadOnlySequence<byte> payload, Encoding encoding)
{
// linearize
int length = checked((int)payload.Length);
var oversized = ArrayPool<byte>.Shared.Rent(length);
try
{
payload.CopyTo(oversized);
return encoding.GetString(oversized, 0, length);
}
finally
{
ArrayPool<byte>.Shared.Return(oversized);
}
}
}
While setting up a TCP server-client connection, I realized that the server receive function hangs if the client does not send an '\n', but the client does not block if the sever doesn't. I tried searching for an explanation without finding a proper answer, so I came here to ask for your help.
I am using the same function to exchange data for both server and client, but I don't know why it works for one and doesn't for the other...
Here is my function in C#:
public bool sendToClient(int i, string msg)
{
try
{
(clientSockets.ElementAt(i)).mSocket.Send(Encoding.ASCII.GetBytes(msg));
}
catch(Exception e)
{
Console.WriteLine(e.Data.ToString());
return false;
}
return true;
}
private string getMessageFromConnection(Socket s)
{
byte[] buff;
string msg = "";
int k;
do
{
buff = new byte[100];
k = s.Receive(buff, 100, SocketFlags.None);
msg += Encoding.ASCII.GetString(buff, 0, k);
} while (k >= 100);
return msg;
}
The sockets are simple SOCK_STREAM ones, and the clientSockets is a list containing Client objects containing each client info including their socket.
I understand that one solution would be to detect a particular character to end the message, but I would like to know the reason behind it because I also had this issue using C.
Thanks in advance.
Your while loop continues only as long as you're reading exactly 100 bytes, and it seems that you intend to use that to detect the end of a message.
This will fail if the message is exactly 100 or any multitude of 100 bytes (in which case it will append a subsequent message to it).
But even worse - there is no guarantee that the socket will return 100 bytes, even if there is data still on its way. Receive does not wait until the underlying buffer has reached 100 bytes, it will return whatever it has available at that point.
You're going to have to either include a header that indicates the message length, or have a terminator character that indicates the end of the message.
I need to transfer a string over TCP connection. For this I serializable my object(over 10000 line list) in one stroke, without Intended. But large string won't transfer(As I understood due to buffer size). So MSDN, on this page (https://learn.microsoft.com/ru-ru/windows/uwp/networking/sockets) say me to use IBuffer for transfer my divided stroke. Here is a code:
// More efficient way to send packets.
// This way enables the system to do batched sends
IList<IBuffer> packetsToSend = PreparePackets();
var outputStream = stream.OutputStream;
int i = 0;
Task[] pendingTasks = new Tast[packetsToSend.Count];
foreach (IBuffer packet in packetsToSend)
{
pendingTasks[i++] = outputStream.WriteAsync(packet).AsTask();
}
// Now, wait for all of the pending writes to complete
await Task.WaitAll(pendingTasks);
What is the method PraparePackets()? How to prepare packets from my stroke?
Edit: I've found solution with DataReader and DataWriter, which has written in Albahari.(End of 16 chapter).
I've found solution with DataReader and DataWriter, which has written in Albahari.(End of 16 chapter).
I have created TCP Server application in Java, and a client application in C#. When i am sending data, the client sometimes receives data out of order, and sometimes parts miss entirely. Basically, the code i use in the server (java) looks like this (stripped):
ServerSocket welcomeSocket = new ServerSocket(port);
Socket connectionSocket = welcomeSocket.accept();
outputStream = new DataOutputStream(socket.getOutputStream()); //Create stream
outputStream.writeBytes(message + "\n");
outputStream.flush();
I use "\n" as a delimiter. On the client side (C#) i use the following code:
private const char Delimiter = '\n';
tcpclnt = new TcpClient();
tcpclnt.NoDelay = true;
tcpclnt.Client.DontFragment = true;
tcpclnt.Connect(ip, port);
//This function is executed in a separate thread
public void Receive()
{
try
{
stream = tcpclnt.GetStream();
streamreader = new StreamReader(stream);
this.Connected = true;
while (Connected)
{
string line = ReadLine(streamreader);
Console.WriteLine("Received data: " + line);
}
}
}
private string ReadLine(StreamReader reader)
{
bool finished = false;
string line = "";
while (finished == false)
{
int asciiNumber = reader.Read();
char character = Convert.ToChar(asciiNumber);
if (!character.Equals(Delimiter))
line += character;
else finished = true;
}
return line;
}
The code is not very complicated. However, the data sent from the server is not always received correctly in the client. As an example, I should receive the following two strings:
"5_8_1" and "6_LEVELDATA"
What i get (sometimes) however, is this: "5_8_61" and "_LEVELDATA"
Another example: "5_4_1" and "6_LEVELDATA" result in one single string: "5_6_LEVELDATA"
This seems like some small problem, but it does in fact pretty much ruin my application. I have read a lot of posts, but the only answers i have read are either "this shouldnt happen with TCP" or "send the length of the tcp message first" which would not help in any way in this case, because the problem isn't the data being split up in multiple packages, it simply isn't arriving in the right order, which is something TCP should do.
I am 100% sure the string is always complete before it is being sent by the Java application.
I really wonder what i'm doing wrong here. Is something messed up bad in my code? I would appreciate any help with this problem. Thanks in advance.
After trying Wireshark, it appears my problem existed in the server. Apparently every TCP-message was sent in a seperate thread. Thank you for all of your comments! My problem is solved now.
I am attempting to create an application that will call a remote modem and do some data transfer (custom data in the form of byte arrays).
I am using JulMar's ITapi3 wrapper and c# 4.0 running on Windows 7 64Bit OS (Compiling as x86).
I have the application making the phone call and disconnecting as I expect but I am having trouble actually sending data across the line. Currently I have the following code in the CallStateChanged event when the call state is connected
var handleArray = callForData.GetID("comm/datamodem");
var byteContents = BitConverter.ToInt64(handleArray, 0);
////temporary Handle array
IntPtr myPointer =new IntPtr(byteContents);
////var pinnedArray = GCHandle.Alloc(handleArray, GCHandleType.Pinned);
////var pointer = pinnedArray.AddrOfPinnedObject();
var commHandle = new SafeFileHandle(myPointer, true);
try
{
//now init filestream
_dataTransferOutFileStream = new FileStream(commHandle, FileAccess.ReadWrite, 2048, true);
//start by writing the login message to the modem
var buffer = CreatePasswordMessage();
IAsyncResult result= _dataTransferOutFileStream.BeginWrite(buffer, 0, buffer.Length,null,null);
while (!result.IsCompleted)
{
//wait for completion of sending login message.
Thread.Sleep(10);
}
//now we are done with sending login message
_dataTransferOutFileStream.EndWrite(result);
//wait 5 seconds
Thread.Sleep(5000);
//do the same type of thing for the read or whether it was sucessful.
var readBuffer = new byte[2048];
IAsyncResult readResult = _dataTransferOutFileStream.BeginRead(readBuffer, 0, 2048,null,null);
while (!readResult.IsCompleted)
{
Thread.Sleep(10);
}
//read is complete.
int readCount = _dataTransferOutFileStream.EndRead(readResult);
Debug.WriteLine("Read Complete Count of Bytes Read: {0} Content of First Byte: {1} ",new object[]{readCount,readBuffer[0]});
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
return false;
}
finally
{
commHandle.Close();
}
return true;
This doesn't seem to be actually sending the data or recieving any valid data from the remote site. Is there something I am missing? Is there a way to check whether the SafeFileHandle being used is actually a reference to the modem's port?
I tried using the builtin SerialPort class for .NET after I am connected but I get an error that the port in question is in use by another process (I am assuming TAPI has a lock on it.)
I am open to all suggestions.
Ok I figured out what the issue was. Apparently I was formatting the data I was sending to the remote modem incorrectly and rather than the remote site telling me that it was simply ignoring my message altogether.
So the code above will work for sending and receiving data asynchronously over a modem line in C#.