Socket TCP C# more data than buffer [duplicate] - 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

How to implement list of connected users on all TCP clients

I am trying to code a way in which I can display to each tcp client a list of connected users which are connected to the server.
Things tried:
I have tried to convert the list array to a single variable which connects all items within the array using a comma ",". Then I send the variable to all clients using a broadcast method which displays a message.
My question is that if there is a better way of doing this.
The code I have tried is shown below :
Inside server :
NetworkStream stream= tcpclient.GetStream();
var bytes= stream.Read(bytes, 0, tcpclient.ReceiveBufferSize);
nameUser= Encoding.ASCII.GetString(bytes, 0, bytes);
var users= String.Join("x", userList);
Console.WriteLine("Connected users " + users);
ChatServer.Broadcast(users, nameUser, false);
The message is then broadcasted which in this case the message is "users".
On the client:
byte[] bytes = new byte[10025];
stream= Client.GetStream();
var byte= Netstream.Read(bytes, 0, (int)Client.ReceiveBufferSize);
string msg= Encoding.ASCII.GetString(bytes, 0, byte);
if (msg.Contains("x") == true)
{
elements = msg.Split(new[] { 'x' });
}
Using String.Contains for detecting a message like this is a very bad idea.
You may consider some leading bytes (or characters) in your message that correspond to the message type.
For example, if you assign 5 characters for that, you could start every message that is about online users with USERS. You should build some other conventions for that. But I guess you could also find some sort of standards for communicating these messages.
In addition, it is more usual to use byte[] instead of characters. So you could assume a standard that your first e.g., four bytes is related to message type and your e.g., second two bytes shows the user ID and so on. You may use short, int, or long for codes related to these things and use Enumeration types to organize them.
Note that when you use a first number of bytes to detect type of the message and other things, you can write your further code in a way that depends on these message characteristics. Whatever you do, do not use Contains; a message could contain anything, you can't detect its type by that!

C# Networkstream BeginRead How to obtain buffer length/size?

I have a problem to obtain the right buffer size of my application.
What i read from the site about specifying the buffer size is normally declared before reading.
byte[] buffer = new byte[2000];
And then using to get the result.
However, this method will stop once the received data contains '00', but my return code contains something like this... 5300000002000000EF0000000A00. and the length is not fixed, can be this short until 400 bytes
So the problems comes, if i define a prefixed length like above, eg 2000, the return value is
5300000002000000EF0000000A000000000000000000000000000000000000000000000000000..........
thus making me unable to split the bytes to the correct amount.
Can any1 show me how to obtain the actual received data size from networkstream or any method/cheat to get what i need?
Thanks in advance.
Network streams have no length.
Unfortunately, your question is light on detail, so it's hard to offer specific advice. But you have a couple of options:
If the high-level protocol being used here offers a way to know the length of the data that will be sent, use that. This could be as simple as the remote host sending the byte count before the rest of the data, or some command you could send to the remote host to query the length of the data. Without knowing what high-level protocol you're using, it's not possible to say whether this is even an option or not.
Write the incoming data into a MemoryStream object. This would always work, whether or not the high-level protocol offers a way to know in advance how much data to expect. Note that if it doesn't, then you will simply have to receive data until the end of the network stream.
The latter option looks something like this:
MemoryStream outputStream = new MemoryStream();
int readByteCount;
byte[] rgb = new byte[1024]; // can be any size
while ((readByteCount = inputStream.Read(rgb, 0, rgb.Length)) > 0)
{
outputStream.Write(rgb, 0, readByteCount);
}
return outputStream.ToArray();
This assumes you have a network stream named "inputStream".
I show the above mainly because it illustrates the more general practice of reading from a network stream in pieces and then storing the result elsewhere. Also, it is easily adapted to directly reading from a socket instance (you didn't mention what you're actually using for network I/O).
However, if you are actually using a Stream object for your network I/O, then as of .NET 4.0, there has been a more convenient way to write the above:
MemoryStream outputStream = new MemoryStream();
inputStream.CopyTo(outputStream);
return outputStream.ToArray();

Weird behaviour of SslStream.Read()

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.

send inkcanvas strokes via sockets

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.

write port listener

I wrote an application like a sniffer it can listen on a port and show to the user information that transferred between your computer and internet like source and destinations ip.
How can I want to change it to retrieve url address Instead of ip address is it possible?
I work with sockets.
private byte[] byUDPData = new byte[4096]; //Data carried by the UDP packet
public UDPHeader(byte [] byBuffer, int nReceived)
{
MemoryStream memoryStream = new MemoryStream(byBuffer, 0, nReceived);
BinaryReader binaryReader = new BinaryReader(memoryStream);
//The first sixteen bits contain the source port
usSourcePort = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
//The next sixteen bits contain the destination port
usDestinationPort = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
//The next sixteen bits contain the length of the UDP packet
usLength = (ushort)IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
//The next sixteen bits contain the checksum
sChecksum = IPAddress.NetworkToHostOrder(binaryReader.ReadInt16());
//Copy the data carried by the UDP packet into the data buffer
Array.Copy(byBuffer,
8,
byUDPData,
0,
nReceived - 8);
}
Usually there's no need to do that. Take a look at Wire Shark. You can write your own protocol analyzers and other add-ins if you can't find exactly what you need.
Also, take a look at WinPCap, which lets your application access the same sniffing abilities of Wire Shark.
When you say
url address Instead of ip address
do you mean the dns name? In which case you need to use the dns class and the Dns.GetHostEntry method to look up the name from the IP address.
If you're looking for URL information, well then as #David Schwartz said, you're going to have to decode all the data you've captured, which may or may not have a url in it.

Categories

Resources