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.
Related
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!
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.
I am in need of creating a small application which sends text chucks. So I created a server using tcpListener. On my client I use tcpClient.
Now my problem:
data = System.Text.Encoding.ASCII.GetBytes("test"); //length of "test" is 4.
now I am sending this byte array to my server application. There I am creating a buffer array with a size of 320000 bytes. (The size really doesn't matter here)
byte[] message = new byte[320000];
bytesRead = clientStream.Read(message, 0, 320000);
ASCIIEncoding encoder = new ASCIIEncoding();
string request = encoder.GetString (message);
And here is my problem now, the resulting request string has a length of 320000. Same size as my byte array. I do understand why it is like that, but what could I do to reduce the size back to it's original? Right now I use a RegEx to remove all chars that would never occur in my strings that I will send... but there must be a proper way of doing so...
Any help will be appreciated :)
Pat.
Try changing:
string request = encoder.GetString(message);
to
string request = encoder.GetString(message, 0, bytesRead);
A common practice is to use some identifier that will never appear anywhere else in your string to indicate the end of it.
System.Text.Encoding.ASCII.GetBytes("test+++++++");
Anything will work, and when you find this sequence you (string.indexof("+++++++")) just return a substring of the final result.
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);