I am working on socket C#. I've implemented a client server application using socket, but the problem is that the client doesn't receive all data sent by the server.
Here is the client application code. What should I do so that it would receive all data sent by the server?
strRecieved = "";
Socket soc = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9001);
soc.Connect(endPoint);
byte[] msgBuffer = Encoding.Default.GetBytes(path);
soc.Send(msgBuffer, 0, msgBuffer.Length, 0);
byte[] buffer = new byte[2000];
int rec = soc.Receive(buffer);
strRecieved = String.Format(Encoding.Default.GetString(buffer));
First of all. If you're implementing some kind of streaming feature ( tcp/udp/file ) you should consider using some kind of protocol.
What is a protocol? It's just a scheme to use when streaming data. Example:
[4Bytes - length][lengthBytes - message][1Byte - termination indicator]
Knowing the protocol you can read all of the incoming bytes simply as such :
byte[] buffer = new byte[4];
stream.ReadBytes(buffer, 0, 4); // cast that to int and read the rest
int packetLen = BitConverter.ToInt32(buffer, 0);
buffer = new byte[packetLen];
stream.ReadBytes(buffer, 0, buffer.Length); // all bytes that was sent
Remember that you have to subtract thease 4 bytes in the length before sending the message.
EDIT:
Simple example on how to send and receive data using shared protocol.
// sender.cs
string _stringToSend = "some fancy string";
byte[] encodedString = Encoding.UTF8.GetBytes(_stringToSend);
List<byte> buffer = new List<byte>();
buffer.AddRange(BitConverter.GetBytes(encodedString.Length));
buffer.AddRange(encodedString);
netStream.WriteBytes(buffer.ToArray(), 0, buffer.Count);
// netStream sent message in protocol [#LEN - 4Bytes][#MSG - #LENBytes]
// simply speaking something like: 5ABCDE
// receiver.cs
byte[] buffer = new byte[sizeof(int)];
netStream.ReadBytes(buffer, 0, buffer.Length);
// receiver got the length of the message eg. 5
int dataLen = BitConverter.ToInt32(buffer, 0);
buffer = new byte[dataLen];
// now we can read an actual message because we know it's length
netStream.ReadBytes(buffer, 0, buffer.Length);
string receivedString = Encoding.UTF8.GetString(buffer);
// received string is equal to "some fancy string"
Making it simpler
This technique forces you to use desired protocol which in this example will be :
First 4 bytes sizeof(int) are indicating the length of the incoming packet
Every byte further is your packet until the end.
So right now you should make ProtocolHelper object:
public static class ProtocolHelper
{
public byte[] PackIntoProtocol(string message)
{
List<byte> result = new List<byte>();
byte[] messageBuffer = Encoding.UTF8.GetBytes(message);
result.AddRange(BitConverter.GetBytes(messageBuffer.Length), 0); // this is the first part of the protocol ( length of the message )
result.AddRange(messageBuffer); // this is actual message
return result.ToArray();
}
public string UnpackProtocol(byte[] buffer)
{
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
}
}
Now ( depending on method you've chosen to read from network ) you have to send and receive your message.
// sender.cs
string meMessage = "network message 1";
byte[] buffer = ProtocolHelper.PackIntoProtocol(meMessage);
socket.Send(buffer, 0, buffer.Length, 0);
// receiver.cs
string message = string.Empty;
byte[] buffer = new byte[sizeof(int)]; // or simply new byte[4];
int received = socket.Receive(buffer);
if(received == sizeof(int))
{
int packetLen = BitConverter.ToInt32(buffer);// size of our message
buffer = new byte[packetLen];
received = socket.Receive(buffer);
if( packetLen == received ) // we have full buffer
{
message = PacketHelper.UnpackProtocol(buffer);
}
}
Console.WriteLine(message); // output: "network message 1"
You're limiting the size of received messages by 2KB as you're using new byte[2000].
I think you could either:
Size up you buffer to meet you message's size needs; and/or
Split you message into more than one socket messages.
Given that 4-8K is a good size for buffering socket messages and assuming RAM size is not a issue I would start with that, say, new byte[8000].
Also, you can send socket messages splitted in chunks. Maybe this is a good idea for the case. For example, if you have msg as the message (or object) you want to send:
private static async Task SendAnswer(Message msg, WebSocket socket)
{
var answer = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(msg).ToCharArray());
var bufferSize = 8000;
var endOfMessage = false;
for (var offset = 0; offset < answer.Length; offset += bufferSize)
{
if (offset + bufferSize >= answer.Length)
{
bufferSize = answer.Length - offset;
endOfMessage = true;
}
await socket.SendAsync(new ArraySegment<byte>(answer, offset, bufferSize),
WebSocketMessageType.Text, endOfMessage, CancellationToken.None);
}
}
And when receiving, you can also split the reception in chunks, so you can control you buffer (and therefore you memory consumption). After hanlding the whole message, you should wait for another message from the client to do more stuff. Source
private async Task ReceiveMessage(WebSocket webSocket)
{
var buffer = new byte[8000];
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
while (!result.CloseStatus.HasValue)
{
string msg = Encoding.UTF8.GetString(new ArraySegment<byte>(buffer, 0, result.Count).ToArray());
while (!result.EndOfMessage)
{
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
msg += Encoding.UTF8.GetString(new ArraySegment<byte>(buffer, 0, result.Count).ToArray());
}
//At this point, `msg` has the whole message you sent, you can do whatever you want with it.
// [...]
//After you handle the message, wait for another message from the client
result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
}
await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
}
Related
I have a TCPClient and it should listen to incoming messages from it's c&c. The messages structure should be: buffer (byte array), type(int/string), id(int) and arguments(string array).
I found this code for listening to incoming messages:
Byte[] bytes = new Byte[1024];
while (true)
{
// Get a stream object for reading
using (NetworkStream streamFromServer = client.GetStream())
{
int length;
// Read incomming stream into byte arrary.
while ((length = streamFromServer.Read(bytes, 0, bytes.Length)) != 0)
{
var incomingData = new byte[length];
Array.Copy(bytes, 0, incomingData, 0, length);
// Convert byte array to string message.
string serverMessage = Encoding.ASCII.GetString(incomingData);
}
}
}
How I use the buffer to read the message? Also, How to convert the incomingData to my message structure?
I wrote this method using UDP socket with ReceiveTimeout = 1000. And sometimes it loses second packet:
private static byte[] ReceivePlainData(Socket socket) {
var recievedData = new List<byte>();
var buffer = new byte[1024];
do {
int recievedAmount;
while (true) {
try {
recievedAmount = socket.Receive(buffer);
}
catch (SocketException) {
continue;
}
break;
}
recievedData.AddRange(buffer.Take(recievedAmount));
} while (socket.Available > 0);
return recievedData.ToArray();
}
Socket initialization:
using (var socket = new Socket(SocketType.Dgram, ProtocolType.Udp) {
ReceiveTimeout = 1000
})
What do i need to change to make this method work correctly?
Socket.Available only tells you if data has been received and available for immediate reading. It doesn't tell you if the stream is complete or not. The sender may still be in the process of generating or transmitting more data. If you intend to consume the entire response and are relying on the socket to be closed by the sender when there is no more data, continue reading from the socket until the number of bytes returned is 0 (indicating the socket was closed normally) or an exception occurs (indicating abnormal termination).
MemoryStream stream = new MemoryStream();
int received;
var buffer = new byte[8096];
do {
received = socket.Receive(buffer);
stream.Write(buffer, 0, received);
} while (received > 0);
// Use the stream
// byte[] bytes = stream.ToArray();
Set your socket's ReceiveTimeout to the maximum amount of time you're willing to wait between bytes before aborting.
This is how I send a file using a NetworkStream.
private void go()
{
byte[] send = File.ReadAllBytes("example.txt");
ns.Write(send, 0, send.Length);
}
ns is a NetworkStream of course.
Now I would like to know how I could receive and read an incoming NetworkStream?
I know that I need to specify a buffer to read from like this,
ns.Read(buffer,0,buffer.length).
but which buffer should be there?
TCP is a stream based protocol, which means that there is no notation of application messages like in UDP. Thus you cannot really detect by TCP itself where an application message ends.
Therefore you need to introduce some kind of detection. Typically you add a suffix (new line, semicolon or whatever) or a length header.
In this case it's easier to add a length header since the chosen suffix could be found in the file data.
So sending the file would look like this:
private void SendFile(string fileName, NetworkStream ns)
{
var bytesToSend = File.ReadAllBytes(fileName);
var header = BitConverter.GetBytes(bytesToSend.Length);
ns.Write(header, 0, header.Length);
ns.Write(bytesToSend, 0, bytesToSend.Length);
}
On the receiver side it's important that you check the return value from Read as contents can come in chunks:
public byte[] ReadFile(NetworkStream ns)
{
var header = new byte[4];
var bytesLeft = 4;
var offset = 0;
// have to repeat as messages can come in chunks
while (bytesLeft > 0)
{
var bytesRead = ns.Read(header, offset, bytesLeft);
offset += bytesRead;
bytesLeft -= bytesRead;
}
bytesLeft = BitConverter.ToInt32(header, 0);
offset = 0;
var fileContents = new byte[bytesLeft];
// have to repeat as messages can come in chunks
while (bytesLeft > 0)
{
var bytesRead = ns.Read(fileContents, offset, bytesLeft);
offset += bytesRead;
bytesLeft -= bytesRead;
}
return fileContents;
}
I am trying to write a simple client/server application in C#. The following is an example server reply sent to my client:
reply {20}<entry name="test"/>
where {20} indicates number of chars that full reply contains.
In the code I wrote below how can I use this number to loop and read ALL chars?
TcpClient tcpClient = new TcpClient(host, port);
NetworkStream networkStream = tcpClient.GetStream();
...
// Server Reply
if (networkStream.CanRead)
{
// Buffer to store the response bytes.
byte[] readBuffer = new byte[tcpClient.ReceiveBufferSize];
// String that will contain full server reply
StringBuilder fullServerReply = new StringBuilder();
int numberOfBytesRead = 0;
do
{
numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length);
fullServerReply.AppendFormat("{0}", Encoding.UTF8.GetString(readBuffer, 0, tcpClient.ReceiveBufferSize));
} while (networkStream.DataAvailable);
}
You're not using numberOfBytesRead. It is fascinating to me that every 2nd TCP question has this same issue as its answer.
Apart from that, you cannot split UTF-8 encoded string at arbitrary boundaries. Encoding.UTF8.GetString will return garbage. Use StreamReader.
The code is just horribly wrong. #usr already pinpointed two big mistakes.
Here is corrected code:
// Server Reply
if (networkStream.CanRead) {
// Buffer to store the response bytes.
byte[] readBuffer = new byte[tcpClient.ReceiveBufferSize];
string fullServerReply = null;
using (var writer = new MemoryStream()) {
while (networkStream.DataAvailable) {
int numberOfBytesRead = networkStream.Read(readBuffer, 0, readBuffer.Length);
if (numberOfBytesRead <= 0) {
break;
}
writer.Write(readBuffer, 0, numberOfBytesRead);
}
fullServerReply = Encoding.UTF8.GetString(writer.ToArray());
}
}
In my client/server application my client wiil communicate with the server for 2 functions: the client will either request data from the server or it will send data so the server will save it. I'm using one socket for both methods, and the method to be used is defined by the first byte sent. If the first byte is "1" it is requesting data. If it is "2", it will send data (data bytes are sent after the "2" byte). It works perfectly for sending data. But when I'm requesting data it works, as long as I don't read the socket stream in the client. It's like if I make the client read data after sending data, the server will have no data to read, and it just crashes when trying to read the data.
Here is my server code:
private const int BufferSize = 1024;
NetworkStream netstream = null;
byte[] RecData = new byte[BufferSize];
int RecBytes;
try {
netstream = clientSocket.GetStream();
int totalrecbytes = 0;
using (MemoryStream ms = new MemoryStream()) {
//When I get here, there is no data to read
while ((RecBytes = netstream.Read(RecData, 0, RecData.Length)) > 0) {
ms.Write(RecData, 0, RecBytes);
totalrecbytes += RecBytes;
}
byte[] bytes = ms.ToArray();
byte b = bytes[0];
switch (b) {
case 1:
//Here I gather data and put it in "stream" variable
byte[] SendingBuffer = null;
int NoOfPackets = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(stream.Length) / Convert.ToDouble(BufferSize)));
int TotalLength = (int)stream.Length, CurrentPacketLength, counter = 0;
for (int i = 0; i < NoOfPackets; i++) {
if (TotalLength > BufferSize) {
CurrentPacketLength = BufferSize;
TotalLength = TotalLength - CurrentPacketLength;
}
else
CurrentPacketLength = TotalLength;
SendingBuffer = new byte[CurrentPacketLength];
stream.Read(SendingBuffer, 0, CurrentPacketLength);
netstream.Write(SendingBuffer, 0, (int)SendingBuffer.Length);
}
netstream.Flush();
}
catch (Exception e) {
Console.WriteLine("EXCEPTION:\n" + e.ToString());
}
break;
case 2:
//Code to read data
break;
}
}
netstream.Close()
clientSocket.Close();
And here is my client code:
using (System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient()) {
string returnData = "";
IAsyncResult ar = clientSocket.BeginConnect("127.0.0.1", 8080, null, null);
System.Threading.WaitHandle wh = ar.AsyncWaitHandle;
try {
if (!ar.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5), false)) {
clientSocket.Close();
Console.WriteLine("Timeout");
return;
}
System.Net.Sockets.NetworkStream serverStream = clientSocket.GetStream();
byte b = 1;
byte[] outStream = { b };
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
//If I comment following lines, the server can read sent data, but server can't otherwise
byte[] RecData = new byte[1024];
int RecBytes;
int totalrecbytes = 0;
MemoryStream MS = new MemoryStream();
while ((RecBytes = serverStream.Read(RecData, 0, RecData.Length)) > 0) {
MS.Write(RecData, 0, RecBytes);
totalrecbytes += RecBytes;
}
serverStream.Close();
clientSocket.Close();
clientSocket.EndConnect(ar);
}
catch (Exception ex) {
Console.WriteLine("Exceção: " + ex.ToString());
}
finally {
wh.Close();
}
}
So, how can I send data to server and read the response? (I tried even putting the thread to sleep after sending data, with no luck.)
Thanks in advance.
EDIT:
With some debug messages I discovered that the server do read the "1" byte that was sent, but somehow it gets stuck inside the while loop, like, the server just stops there, no more loops and it does not leave the while loop. I saw that after writing "loop" in console inside the while loop, and writing read bytes also in console. It wrote "loop" once, and the read byte.
This code worries me:
//When I get here, there is no data to read
while ((RecBytes = netstream.Read(RecData, 0, RecData.Length)) > 0) {
ms.Write(RecData, 0, RecBytes);
totalrecbytes += RecBytes;
}
You are reading until the client closes the connection (or shuts down sending, which you don't do). But the client only closes when the server has replied. The server reply will never come. It is a deadlock.
Solution: Read a single byte to determine the requests command (b).
Unrelated to the question, your "packetised" sending (NoOfPackets, ...) does not seem to serve any purpose. Just use Stream.Copy to write. TCP does not have packets.
An even better solution would be to abandon your custom TCP protocol and use an HTTP library. All these concerns just go away. There are various smaller problems with your code that are very typical to see in TCP code.