I'm having trouble sending the folowing 140kb image file over the network using sockets:
The beginning of the file gets transfered correctly, then the rest gets messed up leading to the following result:
I have a server, that waits for files and a client that connects to server and starts the file tranfer. When client closes the connection, server knows that the file has been done sending and saves it.
All the data on the server side is being collected in byte list (GLOBAL.FILE_BUFFER), before written to a file:
public void OnDataReceived(IAsyncResult asyn)
{
try
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
int iRx = socketData.m_currentSocket.EndReceive(asyn);
for (int f = 0; f < iRx; f++)
{
GLOBAL.FILE_BUFFER.Add(socketData.dataBuffer[f]); // Collect all the incoming data into a buffer
}
WaitForData(socketData.m_currentSocket, socketData.socket_id, REQUEST_INDEX_ID);
}
catch (ObjectDisposedException)
{
}
catch (SocketException se)
{
}
}
This is the code I'm using to write to a file:
public static void ByteArrayToFile(string _FileName, byte[] _ByteArray, int BytesRead)
{
System.IO.FileStream _FileStream = new System.IO.FileStream(_FileName, System.IO.FileMode.Create, System.IO.FileAccess.Write);
_FileStream.Write(_ByteArray, 0, BytesRead);
_FileStream.Close();
}
I used the same ByteArrayToFile function on cient side and it saved the image correctly. It appears that client is sending the correct data, but when server recieves it, it gets messed up. I saved file on both ends, by client and by server and then compared them:
Notice the purple line/bar in the bottom center. It indicates that data doesn't match there, but notice that it matches perfectly at the beginning..
I don't understand how can this happen since I'm using list and simply keep adding to the byte list until there's nothing left to read!? If you don't have solution can you give me any tips please?! This problem has been bugging me for weeks, would love to get it figured out finaly. Thanks!
Can you tell from your data comparison what the problem is? Is the server simply dropping bytes or is the data corrupted? How much data is transferred correctly?
Consider using something like Wireshark to examine the bytes as they come off the wire. That should help you determine whether it's a network error or something in your server code.
Related
I have a server & client model. The client is supposed to take a screenshot, and then send it to the server. The problem I'm having, is part of the screenshot is missing. What I mean by this is like 3/4 of the screen is black when opened in Paint or another app. When I send the screenshot command a second time, the file doesn't open at all, it's corrupt.
Here is my client side
if (plainText.Contains("screenshot"))
{
Bitmap bitmap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
Graphics graphics = Graphics.FromImage(bitmap);
graphics.CopyFromScreen(0, 0, 0, 0, bitmap.Size);
bitmap.Save("test.bmp");
writebuffer = File.ReadAllBytes("test.bmp");
stream.Write(writebuffer, 0, writebuffer.Length);
}
As you can see, it takes a screen shot, saves the image to a bitmap file, then reads the bytes into a buffer and sends it.
Here is my server side
foreach (user c in clientList)
{
if (c.RemoteIPAddress == currentRow)
{
NetworkStream stream = c.clientTCP.GetStream();
writeBuffer = Encoding.ASCII.GetBytes("screenshot");
stream.Write(writeBuffer, 0, writeBuffer.Length);
byte[] readBuffer = new byte[0];
readBuffer = new byte[c.clientTCP.ReceiveBufferSize];
int data = stream.Read(readBuffer, 0, readBuffer.Length);
string x = new Random().Next().ToString();
FileStream f = new FileStream(new Random().Next() + ".bmp", FileMode.Create, FileAccess.Write);
f.Write(readBuffer, 0, data);
f.Close();
Process.Start(x + ".bmp");
}
}
Here, I send the command to tell the client to send a screenshot, then receive the screen, and then write the said screenshot to a bitmap file.
I cannot seem to figure out what is causing the aforementioned issues in this code.
David's "answer" is useless. "hurr muh protocol". The reason your code isn't working is because the max size of a packet is 65535, and you're assuming the image isn't going to be any bigger than that - i.e you're only reading one packet. To read all the packets, use this loop.
while (stream.DataAvailable)
{
data = stream.Read(readBuffer, 0, readBuffer.Length);
f.Write(readBuffer, 0, data);
}
It will check if data is avaliable in the stream, and read it till there's nothing left.
I cannot seem to figure out what is causing the aforementioned issues in this code.
That's because you have nothing to compare it to. There are lots of correct ways to send an image and lots of correct ways to receive an image. But if you want your code to work, the sender and receiver have to agree on how to send an image. This is called a "protocol" and they should always be documented.
If you had a document for this protocol, it would specify how the sender indicates the size of the image. And it would specify how the receiver determines when it has the entire image. You could then check to make sure both the sender and receiver do what the protocol says. If they do, then it would be the protocol that was broken.
When you're using a network connection or file and you're not using an existing protocol or format, document the protocol or format you plan to use at the byte level. That way, it is possible to tell if the sender or receiver is broken by comparing its behavior to the behavior the protocol specifies.
Without a protocol, there's no way to say who's at fault, since there's no standard of correct behavior to compare them to.
Do not ever think that your protocol is too simple to document. If it's so simple, then documenting it should be simple. And if you think it's too complex to document, then you have no prayer of implementing it correctly.
By the way, it's possible to have a protocol for which your sending code is correct. But it's very difficult. It's impossible to have a protocol for which your receiving code is correct since it has literally no possible way to know if it has the whole file or not.
I have created a simple server using socket programming in C# which will receive a file from the client side. My sample code segment is given below.
I want to add some restrictions. I want to make a limit on the file size (such as 4 KB or 2 KB) and allowable file formats (such as .doc, .txt, .cpp, etc.) which will be sent to the client as soon as the client connects to the server so that the client can send files accordingly. How will I do that?
Sample code segment:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
namespace FileTransfer
{
class Program
{
static void Main(string[] args)
{
// Listen on port 1234
TcpListener tcpListener = new TcpListener(IPAddress.Any, 1234);
tcpListener.Start();
Console.WriteLine("Server started");
//Infinite loop to connect to new clients
while (true)
{
// Accept a TcpClient
TcpClient tcpClient = tcpListener.AcceptTcpClient();
Console.WriteLine("Connected to client");
byte[] data = new byte[1024];
NetworkStream ns = tcpClient.GetStream();
int recv = ns.Read(data, 0, data.Length);
StreamReader reader = new StreamReader(tcpClient.GetStream());
//Will add some lines to add restrictions...
}
}
}
}
Which additional lines will I have to add to the code to send the restrictions to client?
Basically I think mainly you need two things:
define application protocol as suggested in other answer
and handle partial read/writes
For handling partial reads (not sure how much such function is needed for write) you may use function like below:
public static void ReadWholeArray (Stream stream, byte[] data)
{
int offset=0;
int remaining = data.Length;
while (remaining > 0)
{
int read = stream.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
Thing is traditional Stream.Read() doesn't guarantee to read as many bytes as you told it, this method on the other hand, will ensure to have read as many bytes as specified in data.Length parameter. So you can use such function to implement the desired application protocol instead.
Some relevant information about such application protocols you will find here too
Ok this is for example how the server could send file length limit and the file extension:
// Send string
string ext = ".txt";
byte [] textBytes = Encoding.ASCII.GetBytes(ext);
ns.Write(textBytes, 0, textBytes.Length);
// Now, send integer - the file length limit parameter
int limit = 333;
byte[] intBytes = BitConverter.GetBytes(limit);
ns.Write(intBytes, 0, intBytes.Length); // send integer - mind the endianness
But you will still need some kind of protocol otherwise you should let client read the "full" stream and parse these data later somehow, which isn't trivial if the data doesn't have fixed length etc - otherwise how will the client distinguish which part of the message is text, which integer?
You seem to be making the classical socket mistake. The given code and explanation seem to assume sockets handle in messages. They don't. When used this way, you're using streaming internet sockets, which provide a stream, not messages.
You don't show any code that does the actual sending, so I'm guessing that you just pump a file's data to the other side and close the connection. How else will you know you've successfully transferred an entire file?
This set of rules that client and server have to follow in order to usefully exchange data through sockets is called an application protocol. You will have to have one, otherwise you'll just be sending data to $deity knows where, and you'll have no control over it at all. This means server nor client will know what's going on, they'll just be sending and receiving data and hoping all goes well. So there's not "a few lines" you have to add to your code, you'll have to restructure it entirely.
There are many ways to define an application protocol and many options to choose from, so I'm going to show you an arbitrary one: a textual explanation of messages that are prefixed with an ID and a payload length (if applicable), both in unspecified numeric variables. You could choose little-endian four-byte unsigned integers, for example.
Messages in this format are known as "Type/Length/Value" or TLV.
So we define these messages:
ID Name Direction Description Payload
1 ServerHello Server -> Client The server sends this message None.
to every connecting client. Or maybe server or
protocol version.
2 MaxUpload Server -> Client Sent after the ServerHello. Maximum upload size
in bytes.
3 AllowedExts Server -> Client Allowed upload extensions, The allowed extensions.
comma-separated. Sent after
MaxUpload message.
10 IncomingFile Client -> Server There's a file coming. The file name.
11 FileUpload Client -> Server The file to upload. The file data.
Sent after IncomingFile.
Now all that's required is to implement this application protocol in server and client and you're done.
You also have to decide what to do if a client or server doesn't adhere to the prototol. It can for example send a message that you can't parse, an unknown message ID, a message length that you don't want to support, an out-of-order message (FileUpload before IncomingFile) or a message that isn't conform the messages sent earlier, like a client uploading a larger file than the server said it would accept or an invalid extension. You also have to think about "acknowledgement" or response messages, like the server telling the client "OK, go ahead, send the next message".
All in all, this is a very broad question and not answered easily. I tried to address that in my comment to your question, which got removed. So here you have your answer.
You can learn more about this on the web, for example Beej's Guide to Network Programming as linked to by Giorgi (be sure to read the entire guide) and Stephen Cleary's blog.
Working on some socket layers using SslStream. reference
Using the reference, I implemented a simple client. The awkward part is when you run the application, it seems the server is not replying to the client.
Going into the debug screen and setting some breakpoints, I realized it was this function that was on an endless loop.
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
byte [] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer,0,bytes)];
decoder.GetChars(buffer, 0, bytes, chars,0);
messageData.Append (chars);
// Check for EOF.
if (messageData.ToString().IndexOf("<EOF>") != -1)
{
break;
}
} while (bytes != 0);
return messageData.ToString();
}
Further investigation pointed out the real criminal here:
bytes = sslStream.Read(buffer, 0, buffer.Length);
Seems like, SslStream.Read() is not returning. Checking the byte[] buffer in debug screen reveals that the response has been written to the buffer till crlf. The function has done it's job, still it's not returning with success?!
What could be the reason for this? What steps should I take to ignore with this issue?
Also, for the skeptical ones: I used openssl to see if the server is behaving as it should, and everything is fine on the server side.
Note: I'm already aware of the SslStream.ReadTimeout property. Though it does the job by raising an exception it is not the right answer for every scenario, specially when server is responding with a large stream of data which can only be read efficiently using a while loop and a buffer.
I was struggling with the same problem with me, the program was infinite loop.
I solved through the following information
İf you using http,https protocol ? you add to header , "Connection: close\r\n"
Http Standart Article
14.10 Connection
The Connection general-header field allows the sender to specify options that are desired for that particular connection and MUST NOT be communicated by proxies over further connections.
The Connection header has the following grammar:
Connection = "Connection" ":" 1#(connection-token)
connection-token = token
HTTP/1.1 proxies MUST parse the Connection header field before a message is forwarded and, for each connection-token in this field, remove any header field(s) from the message with the same name as the connection-token. Connection options are signaled by the presence of a connection-token in the Connection header field, not by any corresponding additional header field(s), since the additional header field may not be sent if there are no parameters associated with that connection option.
Message headers listed in the Connection header MUST NOT include end-to-end headers, such as Cache-Control.
HTTP/1.1 defines the "close" connection option for the sender to signal that the connection will be closed after completion of the response. For example,
**Connection: close**
in either the request or the response header fields indicates that the connection SHOULD NOT be considered `persistent' (section 8.1) after the current request/response is complete.
HTTP/1.1 applications that do not support persistent connections MUST include the "close" connection option in every message.
A system receiving an HTTP/1.0 (or lower-version) message that includes a Connection header MUST, for each connection-token in this field, remove and ignore any header field(s) from the message with the same name as the connection-token. This protects against mistaken forwarding of such header fields by pre-HTTP/1.1 proxies. See section 19.6.2.
If the connection is still open and the server hasn't written <EOF> then it absolutely makes sense that it's just "hung". It's waiting for more data. The only way it could know there's no more data coming is for the server to have closed the connection.
Has it already managed to read all the data which the server has actually sent? What does messageData look like on the iteration before the call which isn't returning?
After some thinking I figured out a workaround.
sslStream.ReadTimeout = 100; //How much time does it takes for a processor to read and write 2048bytes to the buffer?
Had to use timeout, nothing else seemed to work. Modified the Reader to deal with exceptions.
static string ReadMessage(SslStream sslStream)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
byte[] buffer = new byte[2048];
StringBuilder messageData = new StringBuilder();
int bytes = -1;
do
{
try
{
bytes = sslStream.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
}
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.ASCII.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
decoder.GetChars(buffer, 0, bytes, chars, 0);
messageData.Append(chars);
// Check for EOF.
if (messageData.ToString().IndexOf("\r\n") != -1)
{
break;
}
}
while (bytes != -1);
return messageData.ToString();
}
Though this works, doesn't mean it's a good answer. If someone can provide a better answer, it would be great.
So i am trying to create something like a syncronized paint program by using sockets.I have a server side..and a client side and i am trying to send the inkCollection from the server to the client.This works for simple text, but i cant seem to send the inkCollection.Or it would be even cooler if you could help me send the last stroke so that the data transfers faster.Here is some code of what i've been trying:
sending strokes:
byte[] data;
using (MemoryStream ms = new MemoryStream())
{
inkcanvas.Strokes.Save(ms);
data = ms.ToArray();
}
svr.SendToAll("u getting some strokes");
svr.SendStrokes(data);
svr.SendStrokes(byte[] data):
public void SendStrokes(byte[] data)
{
for (int i = 0; i < no; i++)
{
byte[] dt = data;
accepted[i].Send(dt);
}
MessageBox.Show("dONE");
}
and this is on the clientside:
byte[] buffer=new byte[1024];
MessageBox.Show("receiving strokes");
int rec = conn.Receive(buffer, 0, buffer.Length, 0);
if (rec <= 0)
throw new SocketException();
MessageBox.Show("strokes received");
//Array.Resize(ref buffer, rec);
using (MemoryStream ms = new MemoryStream(buffer))
{
inkcanvas.Strokes = new System.Windows.Ink.StrokeCollection(ms);
ms.Close();
}
MessageBox.Show("strokes added");
these exact same methods work perfectly for string, but when i try to do it with the strokeCollection, it fails.Nothing shows up on the client and i get the following SocketException ont the serverside: An existing connection was forcibly closed by the remote host.
But if you guys got a better way on how to do this it would be great...is it something i am missing? i mean..if it works for text transformed into a byte array...why wouldint it work for this strokecollection?
Thanks!
EDIT: You think you could help me out with some sample code? i cant really seem to implement it;
You forgot to either design or implement a protocol! You can't just send a bunch of bytes over TCP and assume the receiver will be able to make sense out of it.
You have an application message that consists of a bunch of strokes which you are trying to send over TCP. But TCP is a byte stream service, not an application message service. So you need some kind of application message protocol to package the message for transport and unpackage it on receipt. But you have not written any such code. So you're basically expecting it to work by magic.
Define a protocol. For example, it might say:
Canvas strokes shall be sent by a '1' byte indicating canvas strokes, followed by 4 bytes indicating the number of bytes in the strokes object in network byte order, followed by the stroke data. The receiver will read the first byte and identify that it's a canvas strokes object. Then the receiver will read the next four bytes to determine the length. The receiver shall accumulate that number of bytes (using multiple reads if necessary) and then process the reconstructed canvas strokes object.
Do not skip the step of creating a written protocol definition.
Then, when you have problems, follow this handy troubleshooting guide:
Does the sender follow the specification? If not, stop, the sender is broken.
Does the receiver follow the specification? If not, stop, the receiver is broken.
Stop, the specification is broken.
If you want simple, you can convert the data into base64 and encode each message as a line of text. That will allow you to use a ReadLine function to grab exactly one message. Then you can just use a message format like an "S" (for "strokes") followed by the data in base64 format. Use a WriteLine function to send the text message followed by a newline.
i think you forgot to set the memorystream position. you should set the memory stream position to 0 before you send out the stream, cause after strokes.save, the position of the stream is at the end.
I'm trying creating a program that allows users to upload JAR Files for some third-party code they've written to an online server and then receive a String message back in response.
The online server is coded using Java with standard TCP Socket networking. The client uses a piece of additional software, which means I have to use C# for the File uploader. The code I've written is included below. In this program the File uploader works fine, but for some reason the client hangs when it reaches input.ReadLine() where it is supposed to receive the String message response from the server.
public static string sendFile(string filepath) {
String response = "";
// Get the file
Stream fileStream = File.OpenRead(filepath);
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, buffer.Length);
try {
// Create the connection
TCPClient client = new TCPClient("127.0.0.1", 21000);
NetworkStream stream = client.GetStream();
// Send the file to the server
stream.Write(buffer, 0, buffer.Length);
stream.Flush();
// Receive a single string response from the server
if (stream.CanRead) {
StreamReader input = new StreamReader(stream);
inputString = input.ReadLine();
}
input.Close();
stream.Close();
client.Close();
}
catch (IOException e) {
print ("Error: " + e);
}
// Return the response message string
return inputString;
}
I have also tried implementing the code above using a StreamWriter rather than writing directly from the NetworkStream itself. Unfortunately the StreamWriter class doesn't have a method for sending a byte array, (only a char array). I'm wondering whether the problem is being caused by the fact I'm calling the Write method of the NetworkStream directly rather than using a StreamWriter.
If anybody has any idea why the code above isn't working then please let me know. Alternatively, if you have a different solution that would allow me to send a file (byte array) and receive back a string message using the same TCPClient connection then please also feel free to mention it.
Regards,
Midavi.
The readline hangs because it will only return when it has successfully read a line from the server, this is the disadvantage of using blocking sockets. Please make sure your server is accually sending a line( string ending with "\n"
Is your stream terminated with an end of line?
Readline will block until the stream ends or receives the end of line character. If your uploader doesn't terminate the string it could act like you're saying.
Checkout networkComms.net, an open source C# network communication library. A short example demonstrating the most basic functionality here, hopefully not overly complex! Most of the problems you might come across will already have been solved and it might save you some time.
Readline wait for end of line \r\n but in java the end of line is by default \n only. This can be the difference that can block your comunication, but to be sure you need to snoop the network activity or you can use a different method to read data.
You can try to use the read method:
byte[] myReadBuffer = new byte[512];
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do{
numberOfBytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length);
// do something with data in myReadBuffer
}
while(stream.DataAvailable);