I wrote a small program that receives a huge string over TCP.
I am able to read from the network stream only one time and then the program crashes. How can I fix this problem or can you suggest me a better way to send big string over TCP. The client's buffer size is 202601176 (The string bytes length).
This is my code:
namespace File_Transfer_Server
{
class Program
{
static void Main(string[] args)
{
TcpListener serverListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 6666);
serverListener.Start();
TcpClient tcpClient = serverListener.AcceptTcpClient();
Console.WriteLine(">>> Receiving");
byte[] clientBuffer = new byte[1024];
Console.WriteLine(clientBuffer.Length);
using (NetworkStream clientNStream = tcpClient.GetStream())
{
int i;
string received = "";
while ((i = clientNStream.Read(clientBuffer, 0, clientBuffer.Length)) > 0) //exception
{
string data = Encoding.ASCII.GetString(clientBuffer, 0, i);
received += data;
Console.WriteLine(data);
}
File.WriteAllText(#"E:\receivedData.txt", received);
Console.WriteLine(">>>Done");
}
}
}
}
This is the exception:
Unhandled Exception: System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
--- End of inner exception stack trace ---
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
at File_Transfer_Server.Program.Main(String[] args) in D:\Files from PC\Visual Basic Projects - =&+Ivan+&=\Tesseract\Temp\File Transfer\File_Transfer_Server\Program.cs:line 29
EDIT:
Client code:
namespace File_Transfer_Client
{
class Program
{
static void Main(string[] args)
{
TcpClient client = new TcpClient();
client.Connect("127.0.0.1", 6666);
NetworkStream clientNetworkStream = client.GetStream();
string fileContent = FileBase64Encoding(#"D:\Download\AtomSetup-x64.exe");
byte[] fileBytes = Encoding.ASCII.GetBytes(fileContent);
// byte[] fileLength = Encoding.ASCII.GetBytes(fileBytes.Length.ToString());
Console.WriteLine(fileBytes.Length);
clientNetworkStream.Write(fileBytes, 0, fileBytes.Length);
Console.WriteLine("Send");
}
static string FileBase64Encoding(string path)
{
byte[] fileBytes = File.ReadAllBytes(path);
string fileBase64String = Convert.ToBase64String(fileBytes);
return fileBase64String;
}
}
}
The issue with your code is the client closes connection before the server manages to receive all the data. You have to tell the server about the length of your message. This is called "Message Framing".
Solution
Write the length of the message before it's content and read it at the server end.
Allocate the buffer at the server with the size of message length.
Read message contents.
Optional step: To make sure we won't have any Socket exceptions Make the client wait for the server until it receives all the data. Server closes the connection.
Client code:
static void Main(string[] args)
{
TcpClient client = new TcpClient();
client.Connect("127.0.0.1", 6666);
NetworkStream clientNetworkStream = client.GetStream();
string fileContent = FileBase64Encoding(#"D:\Download\AtomSetup-x64.exe");
byte[] fileBytes = Encoding.ASCII.GetBytes(fileContent);
// byte[] fileLength = Encoding.ASCII.GetBytes(fileBytes.Length.ToString());
Console.WriteLine(fileBytes.Length);
// Write the length of a message
var integerBytes = BitConverter.GetBytes(fileBytes.Length); // integer has 4 bytes
clientNetworkStream.Write(integerBytes, 0, integerBytes.Length);
// Write the contents
clientNetworkStream.Write(fileBytes, 0, fileBytes.Length);
Console.WriteLine("Send");
// Wait for server to finish receiving
clientNetworkStream.ReadByte();
Console.WriteLine("Connection closed");
}
static string FileBase64Encoding(string path)
{
byte[] fileBytes = File.ReadAllBytes(path);
string fileBase64String = Convert.ToBase64String(fileBytes);
return fileBase64String;
}
Server code:
class Program
{
static void Main(string[] args)
{
TcpListener serverListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 6666);
serverListener.Start();
TcpClient tcpClient = serverListener.AcceptTcpClient();
Console.WriteLine(">>> Receiving");
byte[] clientBuffer;
using (NetworkStream clientNStream = tcpClient.GetStream())
{
int i;
string received = "";
byte[] integerBytes = new byte[sizeof(int)];
clientNStream.Read(integerBytes, 0, integerBytes.Length); // receive message length
int messageLength = BitConverter.ToInt32(integerBytes, 0);
Console.WriteLine("Received message length: {0}", messageLength);
clientBuffer = new byte[messageLength]; // allocate buffer
clientNStream.Read(clientBuffer, 0, clientBuffer.Length); // read string contents
Console.WriteLine("Received all the data");
// we're done
Console.WriteLine("Closing connection");
tcpClient.Close();
received = Encoding.ASCII.GetString(clientBuffer, 0, clientBuffer.Length);
File.WriteAllText(#"E:\receivedData.txt", received);
Console.WriteLine(">>>Done");
}
}
}
As you can see the code for the server now is simpler.
Ideally you would want some more advanced message framing mechanism that can split your message into much smaller chunks and write directly to a file, because you will run out of memory for really big files and is always good idea to split big messages into smaller chunks.
Related
I'm trying to send requests to an API that talks JSON-RPC. I've tested the server with Curl as such:
> curl -v -d 'SOME_INVALID_REQUEST' http://erbium1.sytes.net:50001
{"jsonrpc": "2.0", "error": {"code": -32700, "message": "invalid JSON"}, "id": null}
Some of the requests that interest me are subscriptions, thus sending POST requests seem unfit for this because I don't expect the server to reply instantly, rather receive one or more notifications.
I'm trying to talk to this server using C#, but the server never replies. To send the messages I've done the following :
Created a TcpClient
Connected the client to the server (erbium1.sytes.net,50001)
Go a NetworkStream from the connected client
Wrote to the NetworkStream
When I send these messages to the server, the read callback function is never invoked thus I am never receiving replies from the server. If I were to send the messages to port 50002 (TLS) instead of 50001, the server replies with an empty string.
My guess is, doing a raw TcpClient connection does not send the messages with the HTTP headers thus the server is just dropping them? If so, what would be a suitable way of requesting subscriptions to the server and be able to receive notifications?
What would be the difference between a TcpClient connection and a WebSocket? Would WebSocket be the suitable thing to use here?
Below a sample of my code :
// State object for receiving data from remote device.
public class ReceiverState {
public ReceiverState(NetworkStream s) {
this.stream = s;
}
public NetworkStream stream;
public const int bufferSize = 1024;
public byte[] buffer = new byte[bufferSize];
public AutoResetEvent receivedEvent = new AutoResetEvent(false);
}
static AutoResetEvent received = new AutoResetEvent(false);
public static void PingServer(NetworkStream stream) {
if (stream.CanWrite) {
try {
String rpc = "INVALID_REQUEST";
Byte[] data = System.Text.Encoding.UTF8.GetBytes(rpc);
stream.Write(data, 0, data.Length);
stream.Flush();
Console.WriteLine("Pinging...");
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
} else {
// TODO Throw an exception
Console.WriteLine("Unable to write to stream");
}
}
public static void BeginAsyncRead(NetworkStream stream, ReceiverState readState) {
if (stream.CanRead) {
try {
stream.BeginRead(readState.buffer, 0, 1024,
new AsyncCallback(readCallBack),
readState);
}
catch (Exception e) {
Console.WriteLine(e.ToString());
}
} else {
// TODO Throw an exception
Console.WriteLine("Unable to read from stream");
}
}
private static void readCallBack(System.IAsyncResult ar) {
// Get the state from AsyncResult
ReceiverState state = (ReceiverState) ar.AsyncState;
if(ar.IsCompleted){
Int32 numBytes = state.stream.EndRead(ar);
Console.WriteLine("Received : ");
Console.WriteLine(Encoding.ASCII.GetString(state.buffer,0,numBytes+10));
state.receivedEvent.Set();
}
}
private static void synchronousRead(NetworkStream myNetworkStream) {
if(myNetworkStream.CanRead){
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do{
numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
}
while(myNetworkStream.DataAvailable);
// Print out the received message to the console.
Console.WriteLine("You received the following message : " +
myCompleteMessage);
}
else{
Console.WriteLine("Sorry. You cannot read from this NetworkStream.");
}
}
public static void Main(string[] args) {
// Create a new TcpClient
TcpClient client = new TcpClient();
Console.WriteLine("Connecting...");
client.Connect("electrum3.hachre.de", 50001);
NetworkStream stream = client.GetStream();
Stream inStream = new MemoryStream();
ReceiverState readState = new ReceiverState(stream);
readState.receivedEvent = received;
BeginAsyncRead(stream, readState);
PingServer(stream);
received.WaitOne();
}
I've been creating a TCPClient which is suppose to connect to a server but am getting this error:
System.InvalidOperationException: 'The operation is not allowed on
non-connected sockets.'
Here's my Code:
Public String IPAddress = "192.168.100.xxx"
Public Int32 Port = 23;
public TcpClient client = new TcpClient();
public void Connect() {
client.Connect(IPAddress, Port);
// send first message request to server
Byte[] msg_data = System.Text.Encoding.ASCII.GetBytes("Hello Server);
// uses the GetStream public method to return the NetworkStream
NetworkStream netStream = _client.GetStream();
// write message to the network
netStream.Write(msg_data, 0, msg_data.Length);
// buffer to store the response bytes
msg_data = new Byte[256];
// read the first batch of response byte from arduino server
Int32 bytes = netStream.Read(msg_data, 0, msg_data.Length);
received_msg = System.Text.Encoding.ASCII.GetString(msg_data, 0, bytes);
netStream.Close();
}
public void Send() {
// message data byes to be sent to server
Byte[] msg_data = System.Text.Encoding.ASCII.GetBytes(_sendMessage);
// uses the GetStream public method to return the NetworkStream
// ** Error Here: System.InvalidOperationException: 'The operation is not allowed on non-connected sockets.'
NetworkStream netStream = client.GetStream();
// write message to the network
netStream.Write(msg_data, 0, msg_data.Length);
// buffer to store the response bytes
msg_data = new Byte[256];
// read the first batch of response byte from arduino server
Int32 bytes = netStream.Read(msg_data, 0, msg_data.Length);
received_msg = System.Text.Encoding.ASCII.GetString(msg_data, 0, bytes);
netStream.Close(); // close Stream
}
I am getting and error when creating a new instance of NetworkStream netStream = client.GetStream();. Been struggling to find whats causing the error, I think it's somehow closing the connection above.
Everything is in a class and must be called at anyplace in the software.
client.GetStream() is implemented like:
return new NetworkStream(this.Client, true);
whereas the true means if the stream is disposed/closed it also closes/disconnects the socket/client. You should be able to avoid this by directly calling
var netStream = new NetworkStream(client.Client, false);
And even better would be instead of:
NetworkStream netStream = client.GetStream();
…
netSteam.Dlose();
to ensure that the stream is always closed even if an errors by writing:
using (var netStream = new NetworkStream(client.Client, false))
{
…
}
I'm wanting to run a little socket server in C# to be accessed by a browser. I have a socket listener up and running on a specific port, and am trying to access it via the browser:
class WebSocketServer
{
private static string output = string.Empty;
public void CreateListener()
{
TcpListener tcpListener = null;
var ipAddress = Dns.GetHostEntry("localhost").AddressList[0];
try
{
tcpListener = new TcpListener(ipAddress, 1313);
tcpListener.Start();
output = "Waiting for a connection";
}
catch (Exception e)
{
throw;
}
var socketHelper = new SocketHelper();
while (true)
{
Thread.Sleep(10);
TcpClient tcpClient = tcpListener.AcceptTcpClient();
byte[] bytes = new byte[256];
var stream = tcpClient.GetStream();
stream.Read(bytes, 0, bytes.Length);
socketHelper.ProcessMessage(tcpClient, stream, bytes);
}
}
}
class SocketHelper
{
private static int counter = 0;
public void ProcessMessage(TcpClient tcpClient, NetworkStream stream, byte[] bytesReceived)
{
// handle message received and send response to client
var msg = Encoding.ASCII.GetString(bytesReceived);
string response = string.Empty;
if (msg.Substring(0, 10) == "GET / HTTP")
{
response = "";// html page
}
byte[] bytesToSend = Encoding.ASCII.GetBytes(response);
stream.Write(bytesToSend, 0, bytesToSend.Length);
}
The browser appears to connect to it, but it never seems to display the html - what's wrong? I'm eventually wanting to be able to serve up JSON data via a REST interface. In addition, is there a much easier solution to (I assume) this common problem.
I have a problem with the Client/Server application i'm writing. It's aim is to send a specific directory's files in the Client and send them to a directory on the Server.
I get the files with a foreach, but when i put a breakpoint at the begin of the foreach and I continue until I send all the files I get all of them in my server as I want, when i delete the breakpoint and re-run my application, my server just receives part of the files, and i don't know why.
I'm unsure but I suppose it's a threading issue, but don't know how to solve it.
Server:
static void Main(string[] args)
{
try
{
TcpListener listen = new TcpListener(3003);
TcpClient client;
int bufferSize = 1024;
NetworkStream netStream = null;
int bytesRead = 0;
int allBytesRead = 0;
// Start listening
listen.Start();
// Accept client
client = listen.AcceptTcpClient();
StreamReader reader = new StreamReader(client.GetStream());
netStream = client.GetStream();
string fileName;
bool endOfSend=false;
do
{
fileName = reader.ReadLine();
// Read length of incoming data
byte[] length = new byte[4];
bytesRead = netStream.Read(length, 0, 4);
int dataLength = BitConverter.ToInt32(length, 0);
// Read the data
int bytesLeft = dataLength;
byte[] data = new byte[dataLength];
while (bytesLeft > 0)
{
int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;
bytesRead = netStream.Read(data, allBytesRead, nextPacketSize);
allBytesRead += bytesRead;
bytesLeft -= bytesRead;
}
allBytesRead = 0;
bytesLeft = 0;
bytesRead = 0;
// Save file to desktop
Console.WriteLine("File {0} received.", fileName);
File.WriteAllBytes(#"C:\Users\toto2\Desktop\" + fileName, data);
} while (!endOfSend);
netStream.Close();
client.Close();
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("File Receiving fail." + ex.Message);
}
}
Client:
static void Main(string[] args)
{
try
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
int port = 3003;
TcpClient client = new TcpClient();
//NetworkStream netStream;
// Connect to server
try
{
client.Connect(new IPEndPoint(ipAddress, port));
Console.WriteLine("Connecté.....");
SendFiles(client);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// Clean up
client.Close();
Console.Read();
}
catch (Exception ex)
{
Console.WriteLine("File Sending fail." + ex.Message);
}
}
public static void SendFiles(TcpClient cli) {
NetworkStream netS=cli.GetStream();
int bufferSize = 1024;
string[] files = Directory.GetFiles(#"C:\Users\toto\Mes Images\test");
StreamWriter writer = new StreamWriter(cli.GetStream());
foreach (string item in files)
{
writer.WriteLine(Path.GetFileName(item));
writer.Flush();
// Read bytes from image
byte[] data = File.ReadAllBytes(Path.GetFullPath(item));
// Build the package
byte[] dataLength = BitConverter.GetBytes(data.Length);
byte[] package = new byte[4 + data.Length];
dataLength.CopyTo(package, 0);
data.CopyTo(package, 4);
// Send to server
int bytesSent = 0;
int bytesLeft = package.Length;
while (bytesLeft > 0)
{
int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;
netS.Write(package, bytesSent, nextPacketSize);
bytesSent += nextPacketSize;
bytesLeft -= nextPacketSize;
}
}
writer.Close();
netS.Close();
}
Thank to anyone will try to help me.
You may want to implement and acknowledgement from the server to the client that it has recieved 1 file. Then instruct the client to send the next file. As far as I can tell you are just sending all the files at once. The following is a simple implementation of the acknowledgement. You should be able to take the relevant parts of it for your scenario.
//
/* Server Program */
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
public class serv {
public static void Main() {
try {
IPAddress ipAd = IPAddress.Parse("172.21.5.99");
// use local m/c IP address, and
// use the same in the client
/* Initializes the Listener */
TcpListener myList=new TcpListener(ipAd,8001);
/* Start Listeneting at the specified port */
myList.Start();
Console.WriteLine("The server is running at port 8001...");
Console.WriteLine("The local End point is :" +
myList.LocalEndpoint );
Console.WriteLine("Waiting for a connection.....");
Socket s=myList.AcceptSocket();
Console.WriteLine("Connection accepted from " + s.RemoteEndPoint);
byte[] b=new byte[100];
int k=s.Receive(b);
Console.WriteLine("Recieved...");
for (int i=0;i<k;i++)
Console.Write(Convert.ToChar(b[i]));
ASCIIEncoding asen=new ASCIIEncoding();
s.Send(asen.GetBytes("The string was recieved by the server."));
Console.WriteLine("\nSent Acknowledgement");
/* clean up */
s.Close();
myList.Stop();
}
catch (Exception e) {
Console.WriteLine("Error..... " + e.StackTrace);
}
}
}
---------------------------------------------------------------------------
/* Client Program */
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Net.Sockets;
public class clnt {
public static void Main() {
try {
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("172.21.5.99",8001);
// use the ipaddress as in the server program
Console.WriteLine("Connected");
Console.Write("Enter the string to be transmitted : ");
String str=Console.ReadLine();
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen= new ASCIIEncoding();
byte[] ba=asen.GetBytes(str);
Console.WriteLine("Transmitting.....");
stm.Write(ba,0,ba.Length);
byte[] bb=new byte[100];
int k=stm.Read(bb,0,100);
for (int i=0;i<k;i++)
Console.Write(Convert.ToChar(bb[i]));
tcpclnt.Close();
}
catch (Exception e) {
Console.WriteLine("Error..... " + e.StackTrace);
}
}
}
You've got a hybrid binary/text protocol. That can be painful. StreamReader buffers parts of the stream. It takes more than it returns immediately as a string. You can't mix it with other readers.
Throw all of this code away and use a higher-level communication mechanism. For example WCF with MTOM for binary streaming. Or HTTP.
If you are unwilling to to that use BinaryReader ad BinaryWriter. They are reasonably easy to use.
Note, that when you read the length you assume that all 4 bytes will arrive in one read. That assumption is false.
I want to receive message on C# client from Netty server. I use sync C# socket and protobuf.
I send message to server and it's ok. But I can't receive response.
Netty server uses ProtobufDecoder. Server ChannelInboundHandler has this part of code:
public void channelRead0(final ChannelHandlerContext ctx, Object msg) {
...
Set<String> keys = jedis.keys("tanks*");
String allKeys = "";
for(String key: keys){
allKeys+=key+";";
}
ctx.write(allKeys);
ctx.flush();
}
C# client code is:
const string server = "localhost";
const int port = 8080;
var tcpClient = new TcpClient(server, port);
_networkStream = tcpClient.GetStream();
var stream = new MemoryStream();
Serializer.Serialize(stream, tankDataObject);
var data = stream.ToArray();
_networkStream.Write(data, 0, data.Length);
data = new Byte[10000];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = _networkStream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
// Close everything.
_networkStream.Close();
tcpClient.Close();
Client doesn't receive any bytes or receive empty array if I call ctx.close() on server. Any help will be appreciated. Thank you.