Sending some constraints to client from server in C# - c#

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.

Related

Why is using StreamWriter.WriteLine, followed by StreamWriter.BaseStream.WriteByte unreliable?

I am developing a networking application.
For sending and receiving data, I am using NetworkStream that I get from TcpClient.
For sending text, I wrapped NetworkStream in a StreamWriter, and I simply call StreamWriter.WriteLine(text), followed by StreamWriter.Flush().
For sending 1 byte flags from server to client (that are required for in my own communication protocol), I am using StreamWriter.BaseStream.WriteByte(byte). So, it is all the same underlying stream and it worked great until I got into this situation. The following code is where it breaks:
// This is server sending data to client.
writer.WriteLine(text);
writer.Flush();
writer.BaseStream.WriteByte(flag);
// This is client trying to read incoming data from server.
string text = reader.ReadLine(); // This will read text successfully.
int flag = reader.BaseStream.ReadByte(); // Problem is here: It will block here as if there is no data.
However, if I put some delay between flushing data and sending the byte, everything works fine...
// This is server sending data to client.
writer.WriteLine(text);
writer.Flush();
Thread.Sleep(1000); <-------------------------------- delay
writer.BaseStream.WriteByte(flag);
// This is client trying to read incoming data from server.
string text = reader.ReadLine(); // This will read text successfully.
int flag = reader.BaseStream.ReadByte(); // Now the byte is read successfully as well.
Can someone please explain why this is happening and how I can fix it?

How to implement IPP gateway using C#?

I need to develop a Internet Printing Protocol gateway in .Net that will receive the print jobs fired from IOS using the AirPrint client. The gateway will receive the document fired and release it to the print queue. I am able to broadcast my print services using the SDK provided by Apple. However, when I listen on a port to receive network streams of a document, I am not able to detect the end of stream received as the client keeps on sending streams. My guess is we have to read the attributes and respond accordingly, but I have no idea of these attributes. Below is the code that I am currently using:
IPAddress ipAddress = IPAddress.Parse("10.0.0.13");
IPAddress tcpListener = new TcpListener(ipAddress, 631);
tcpListener.Start();
while (true)
{
TcpClient tcpClient = tcpListener.AcceptTcpClient();
byte[] bytes = new byte[2560];
NetworkStream stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
string mstrMessage = Encoding.ASCII.GetString(bytesReceived, 0, bytesReceived.Length);
string Continue = "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK\r\nCache-Control: no-cache\r\nDate: " + dateTime + "\r\nPragma: no-cache\r\nTransfer-Encoding: chunked\r\nContent-Type: application/ipp\r\n\r\nattributes-charset utf-8 attributes-natural-language en-us compression-supported none printer-is-accepting-jobs true document-format-supported application/pdf\r\n\r\n0\r\n";
bytesSent = Encoding.ASCII.GetBytes(mstrResponse);
stream.Write(bytesSent, 0, bytesSent.Length);
}
You should check the stream.Read for the returning value.
If it isn't zero you have incoming bytes from the TcpClient:
var bytesLength = 0;
do
{
bytesLength = stream.Read(bytes, 0, bytes.Length);
if (bytesLength == 0) return;
}
while(bytesLength > 0);
You need to understand the level of communication. You're not even reading or writing proper IPP messages yet. 100 continue is purley HTTP related.
Even though Apples Spec for AirPrint is not publicly available there's still a lot of information online. In short: AirPrint is based on IPP. As for the supported PDL PDF is a good choice but not the only one. iOS first checks the printers capabilities. It's up to you what kind of (virtual) print-server you offer.
(In case you have a business case and require a remote developer, don't hestitate to contact us.)

Pass the Length of uncertain Stream to WCF Service

Is there any way to pass the Length of uncertain Stream to WCF Service?
Unsertain Stream means the stream of
The stream provides its length only after process and writing the data.
e.g. GZipStream
Background
I'm making a WCF Service receiving multiple Streams from client.
As WCF Streaming only allows one stream in the message, I decided to concatenate all streams into one stream and divide it in server code.
The streams client provides will contains variable kinds of stream like FileStream, MemoryStreamwith data from DataTable serialization and
using (var fileStream = new FileStream(filePath, FileMode.Open))
using (var memoryStream = new MemoryStream())
using (var concatStream = new ConcatenatedStream(fileStream, memoryStream))
{
client.UploadStreams(concatStream);
}
ConcatenatedStream is a Stream implementation suggested in c# - How do I concatenate two System.Io.Stream instances into one? - Stack Overflow.
In server side, Length of each Streams will be needed to divide single stream to multiple streams.
As I want to save memory in client side, I decided to use PullStream.
PullStream will Write buffer on demand of Read.
But this causes a big problem. I cannot get Length of PullStream before starting streaming.
Any helps will be appreciated.
Thanks
Let's make it simple:
If you have the length of a part of the stream on client before you start pushing it to server you can append a structure before the payload and read that structure on server. That is a standard data transfer template. Doing so i.e. appending a header before each payload you give your server a hint on how long the next part is going to be.
If you do not have the length of a part of the stream on client before you start pushing it to server, you are going to have to 'insert' the header inside the payload. That's not very intuitive and not that useful but it does work. I used such a thing when I had my data prepared asynchronously on client and the first buffers were ready before the length was known. In this scenario you are going to need a so called marker i.e. a set of bytes that could not be found anywhere in the stream but before the header.
This scenario is the toughest of the 3 to implement when done for the first time. Buckle up. In order to do it right you should create an artificial structure of your stream. Such a structure is used for streaming video over network and called Network Abstraction Layer or NAL, read about it. It is also called stream format AnnexB from the h264 standard. You should abstract from the field in which the standard is described, the idea is very versatile.
In short the payload is divided into parts, so called NAL Units or NALUs, each part has a byte sequence which marks it's start, then goes the type indicator and length of the current NALU, then follows the payload of the NALU. For your purposes you would need to implement NALUs of two types:
Main data payload
Metadata
After you imagine how your stream should look like, you have to grip on the idea of "stream encoding". Those are fearsome words but do not worry. You just have to ensure that the byte sequence that is used to mark the start of the NALU is never met inside the payload of the NALU. In order to achieve that you are going to implement some replacement tactic. Browse for samples.
When you are done thinking this through and before you dive into that, think twice about it. Might be the scenario 3 would fit you easier.
In the case you are sure you will never have to process a part of the streamed data you can greatly simplify the scenario i.e. totally skip the stream encoding and implement something like this:
Client Stream principal code:
private byte[] mabytPayload;
private int mintCurrentPayloadPosition;
private int? mintTotalPayloadLength;
private bool mblnTotalPayloadLengthSent;
public int Read(byte[] iBuffer, int iStart, int iLength)
{
if (mintTotalPayloadLength.HasValue && !mblnTotalPayloadLengthSent)
{
//1. Write the packet type (0)
//3. Write the total stream length (4 bytes).
...
mblnTotalPayloadLengthSent = true;
}
else
{
//1. Write the packet type (1)
//2. Write the packet length (iLength - 1 for example, 1 byte is for
//the type specification)
//3. Write the payload packet.
...
}
}
public void TotalStreamLengthSet(int iTotalStreamLength)
{
mintTotalPayloadLength = iTotalStreamLength;
}
Server stream reader:
Public void WCFUploadCallback(Stream iUploadStream)
{
while(!endOfStream)
{
//1. Read the packet type.
if (normalPayload)
{
//2.a Read the payload packet length.
//2.b Read the payload.
}
else
{
//2.c Read the total stream length.
}
}
}
In the scenario where your upload is no-stop and the metadata about the stream is ready on client long after the payload, that happens as well, you are going to need two channels i.e. one channel for payload stream and another channel with metadata where you server will answer to the client with another question like 'what did you just started sending me' or 'what have you sent me' and the client will explain itself in the next message.
If you are ready to stick to one of the scenarios, one could give you some further details and/or recommendations.

Sending a image file using a socket in C# (Large Data)

i want to send a large data (image) approx . 1MB file though a socket connection
Question 1
following code snippet of the socket client which i currently use to send a a text message .. how i can modify this to send a file ?
NetworkStream serverStream = clientSocket.GetStream();
byte[] outStream = System.Text.Encoding.ASCII.GetBytes(richTextBox1.Text+"$");
serverStream.Write(outStream, 0, outStream.Length);
serverStream.Flush();
Question 2 : What modifications that required in both socket client and server to send and get large files ?
For large data portions you will probably need some kind of transmitting by portions. In your snippet you get all of the data in the array which couldn't be possible if file is large enough (or at least not suitable if it is not just one file to send).
Sending side will be something like that:
const int bufsize = 8192;
var buffer = new byte[bufsize];
NetworkStream ns = socket.GetStream();
using (var s = File.OpenRead("path"))
{
int actuallyRead;
while ((actuallyRead = s.Read(buffer, 0, bufsize)) > 0)
{
ns.Write(buffer, 0, actuallyRead);
}
}
ns.Flush();
Receiving site is just symmetric.
A socket doesn't care if you send text or binary data. Or how much you send. If there's any trouble then it is at the receiving end, code you didn't post. A classic mistake is to forget that a NetworkStream is a stream and not a sequence of packets. The Read() call at the receiving end can return any number of bytes. It won't be the number of bytes you wrote in the Write() call, depending on how routers in between the two machines broke up the IP packets and how much data is buffered in the receiver. You are probably getting away with calling Read only once because the string is short. That is definitely not going to work when you send a lot of data.
You need a protocol to help the receiver figure out when it received all the data. A simple way to do this is by first sending the length of the data. The receiver can then first read that length, then know how long to keep calling Read() to get the rest of the data.
You can arbitrarily extend this protocol by, say, sending the name of the file. Etcetera. Although that by the time you're done, you'd be close to having re-invented FTP.
If all you want to do is to send an image and you don't need any metadata, you can use code like this:
Server:
var listener = new TcpListener(address, port);
listener.Start();
using (var incoming = listener.AcceptTcpClient())
using (var networkStream = incoming.GetStream())
using (var fileStream = File.OpenWrite(imagePath))
{
networkStream.CopyTo(fileStream);
}
listener.Stop();
Client:
var client = new TcpClient();
client.Connect(address, port);
using (var networkStream = client.GetStream())
using (var fileStream = File.OpenRead(imagePath))
{
fileStream.CopyTo(networkStream);
}
client.Close();
If you want to compress the file, you can use GZipStream.
This code uses CopyTo() method, which is available in .Net 4, but you can write it yourself in earlier versions.

How can a C# app easily communicate and transfer files on a network?

How can a C# app easily communicate with an instance of itself present on another computer, which is on the same network, and transfer files and data?
Assuming the computers on the network have fixed local IP addresses, and they each know each others IPs. Would there also be a way to communicate if the IPs are unknown? based on some discovery protocol?
I heard the "Bonjour" service from Apple was a good protocol. Can we communicate via it from our Windows apps? Or do you have to use "sockets". I'm primarily looking for libraries or sample code that can fulfill my need easily, I don't want to develop my own TCP-based protocol or anything hardcore!
You can use System.Net.Sockets class to communicate and it have a method for sending file Socket.SendFile.
Update:
this is a good example for file sharing and sending file from C# help
The great thing about files and sockets in C# is that they're both exposed as streams. Copying a large file from one stream to another is pretty simple:
byte[] data = new byte[1024];
while(true) {
int bytesRead = filestream.read(data,0,data.Length);
if (bytesRead==0) break;
netstream.write(data,0,bytesRead);
}
Then just close the socket when you're done.
If you want to send metadata (filenames, sizes) or don't want to close the connection, you need some sort of protocol to handle this. FTP uses two seperate sockets(one for metadata, one for data; this is called out-of-band communication). If you're on a LAN with no firewalls, that can be perfectly acceptable. On the other hand, if you want to do internet transfer, getting a single port open is a difficult-enough task, and two is unbearable. If you don't care too much about performance, you could encode the bytes in base64 encoding, which makes sure that they're within a certain byte range. With base64, you can seperate messages with newlines or other non-alphanumeric characters. Then in the first message include the filename, size, or whatever, then send the data as a second message, then send a "that's the whole file" message so the client knows it's done.
Another tactic for messages is using an escape sequence. For instance, take your bytestream and replace each instance of '\0' with '\0\0'. Now use '\0\1' to signal the end-of-message, which is guaranteed not to be contained in your data message. Decode the '\0\0' back to '\0' on the receiving end. This works well enough in C, but I find that, in practice, looping through each byte can be slower than reading whole buffers in C#.
The best way is to adopt some sort of adaptive-length protocol. For instance, send the data in chunks of a certain size (say 512 bytes). Before each chunk, send a 32bit int representing the size of the chunk via System.BitConverter. So messages look like this (english):
Here's 512 bytes:
[data]
Here's 512 bytes:
[data]
Here's 32 bytes:
[data]
Here's 4 bytes:
That was the whole file
The advantage here is that you can make the copy/read buffers work for you (reading 512 bytes at a time), meaning your throughput is limited by your network stack instead of your C# code. The client reads the fixed-length 32-bit int that lets it know the size of the buffer it should use for the next [data] segment.
Here's some code to write messages like that:
logger.logger.debug("Sending message of length " + length);
byte[] clength = System.BitConverter.GetBytes(buffer.Length);
plaintextStream.Write(clength,0,clength.Length);
plaintextStream.Write(buffer,0,buffer.Length);
plaintextStream.Flush();
And here's some code to read them:
byte[] intbuf = new byte[int_32_size];
int offset = 0;
while (offset < int_32_size)
{
int read = 0;
read = d.plaintextStream.Read(intbuf,offset,int_32_size - offset);
offset += read;
}
int msg_size = System.BitConverter.ToInt32(intbuf,0);
//allocate a new buffer to fill the message
byte[] msg_buffer = new byte[msg_size];
offset = 0;
while (offset < msg_size)
{
int read = 0;
read = d.plaintextStream.Read(msg_buffer,offset,msg_size - offset);
offset += read;
}
return msg_buffer;
For transferring the files / data, you can use the TcpClient/TcpListener classes, which is nice abstractions over the grittier socket functionality. Or, you could simply have the application as a HTTP server using the HttpListener class, if that is easier/more appropiate for your application.
For discovery, if you are able to have a central server; then you could have each client connect to the server at startup, to register itself and retrieve a list of other online clients and their IP's. Subsequent communication can then take place directly between the clients.
A variation of this scheme, is to let the central server act as a proxy, which all traffic between the clients flow through. This would be mostly helpful to overcome firewall or routing issues if the clients is not on the same network (so it's propably not needed for your scenario).
To copy files you may also want to look at File Synchronization Provider that is part of Microsoft Sync Framework. http://msdn.microsoft.com/en-us/sync/bb887623.

Categories