I have a problem about network file transferring.
Socket TCP/UDP uploading is slower than native windows copy when there are more than 1 clients.
I have some PC with 1Gbps network.
Here are the Windows copy cases.
One client copy from the server, the maximum upload speed is about 100mbps. (It should be higher than 100mbps, there is bottleneck in the route.)
Two clients copy from the server, the maximum upload speed is about 200mbps.
Three clients copy from the server, the maximum upload speed is about 300mbps.
Here are the Socket cases.
One client download from the server, the maximum upload speed is about 100mbps.
Two clients download from the server, the maximum upload speed is about 100mbps.
Three clients download from the server, the maximum upload speed is about 100mbps.
As you see, the socket server upload speed does not increase while the client number increases.
We made a simple program to test this issues. It encounters the same issues.
It's just very simple send and recv calling.
Please check the codes.
class Program
{
static List<TcpClient> sessions = new List<TcpClient>();
static void Main(string[] args)
{
bool isServer = false;
Console.WriteLine("Run as a server? (Y/N)");
string answer = Console.ReadLine();
if (answer.ToUpper().StartsWith("Y")) isServer = true;
if (isServer)
{
TcpListener listener = new TcpListener(IPAddress.Any, 13579);
Console.WriteLine("Listening at: " + ((IPEndPoint)listener.LocalEndpoint).ToString());
listener.Start();
Thread workerThread = new Thread(() =>
{
while (true)
{
lock (sessions)
{
foreach (var client in sessions)
{
if (client.Available > 0)
{
byte[] buffer = new byte[client.Available];
int length = client.Client.Receive(buffer);
string filePath = Encoding.UTF8.GetString(buffer, 0, length);
if (File.Exists(filePath))
{
foreach (var receiver in sessions)
receiver.Client.SendFile(filePath, null, null, TransmitFileOptions.UseKernelApc | TransmitFileOptions.WriteBehind);
}
}
}
}
Thread.Sleep(200);
}
});
workerThread.IsBackground = true;
workerThread.Start();
while (true)
{
TcpClient client = listener.AcceptTcpClient();
lock (sessions)
{
sessions.Add(client);
}
}
}
else
{
Console.WriteLine("Enter the server IP:");
string ipAddress = Console.ReadLine();
TcpClient client = new TcpClient(ipAddress, 13579);
Console.WriteLine("Enter the file path on remote server:");
string remoteFilePath = Console.ReadLine();
if(remoteFilePath != "")
client.Client.Send(Encoding.UTF8.GetBytes(remoteFilePath));
byte[] recvBuffer = new byte[32768];
while(true)
{
try
{
client.Client.Receive(recvBuffer, 32768, SocketFlags.None);
}
catch (SocketException e)
{
Console.WriteLine("Transfer interrupted: {0}", e.Message);
break;
}
}
}
}
}
This is blocking us to implement the socket tool with better performance.
Hope stackoverflow guys could help us out.
Thanks advanced.
Of course it's slower. you are using the same thread to poll all clients.
You should switch to the asynchronous methods in the server (BeginRead/EndRead)
Related
I am trying to implement a TCP forwarder in C#. Specifically the application:
Listens to a TCP port and wait for a client,
When a client is connected, connects to a remote host,
Waits for incoming data on the both connections and exchange the data between two endpoints (acts as an proxy),
Closes one connection when the other one get closed by an endpoint.
I have adapted Simple TCP Forwader (by Garcia) to forward a range of ports so that
TCPForwarder.exe 10.1.1.1 192.168.1.100 1000 1100 2000
will forward any packets received on 10.1.1.1 on port 1000-1100 to remote host 192.168.1.100 port 2000-2100. I have employed this to expose a FTP server that is behind a NAT.
By running the above command, the client is able to connect to the FTP server and the following pattern in outputted to the console which is expected (refer to code):
0 StartReceive: BeginReceive
1 StartReceive: BeginReceive
1 OnDataReceive: EndReceive
1 OnDataReceive: BeginReceive
1 OnDataReceive: EndReceive
1 OnDataReceive: Close (0 read)
0 OnDataReceive: EndReceive
0 OnDataReceive: Close (exception)
But after successfully connecting for several times (pressing F5 in Filezilla), no further response is received from TCPForwarder (and FTP Server).
There seems to be two problems with my implementation that I cannot debug:
In that situation, the BeginReceive in StartReceive method gets called, but no data is received from the FTP server. I don't think that could be FTP server issue (it's a ProFTPD server) as it is a well-known FTP server.
Every time a connection is made and closed, the number of threads increases by 1. I don't think garbage collection would fix that. Number of threads is consistently increasing and forcing garabage collector to run doesn't decrease that either. I think there is some leak in my code that is also causing issue #1.
Edit:
Restarting the FTP server didn't fix the problem, so there is definitely a bug in the TCPForwarder.
Some issues pointed out by #jgauffin is fixed in the below code.
Here is the full code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Threading;
namespace TCPForwarder
{
class Program
{
private class State
{
public int ID { get; private set; } // for debugging purposes
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(int id, Socket source, Socket destination)
{
ID = id;
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
public class TcpForwarder
{
public void Start(IPEndPoint local, IPEndPoint remote)
{
Socket MainSocket;
try
{
MainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
MainSocket.Bind(local);
MainSocket.Listen(10);
}
catch (Exception exp)
{
Console.WriteLine("Error on listening to " + local.Port + ": " + exp.Message);
return;
}
while (true)
{
// Accept a new client
var socketSrc = MainSocket.Accept();
var socketDest = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
// Connect to the endpoint
socketDest.Connect(remote);
}
catch
{
socketSrc.Shutdown(SocketShutdown.Both);
socketSrc.Close();
Console.WriteLine("Exception in connecting to remote host");
continue;
}
// Wait for data sent from client and forward it to the endpoint
StartReceive(0, socketSrc, socketDest);
// Also, wait for data sent from endpoint and forward it to the client
StartReceive(1, socketDest, socketSrc);
}
}
private static void StartReceive(int id, Socket src, Socket dest)
{
var state = new State(id, src, dest);
Console.WriteLine("{0} StartReceive: BeginReceive", id);
try
{
src.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
catch
{
Console.WriteLine("{0} Exception in StartReceive: BeginReceive", id);
}
}
private static void OnDataReceive(IAsyncResult result)
{
State state = null;
try
{
state = (State)result.AsyncState;
Console.WriteLine("{0} OnDataReceive: EndReceive", state.ID);
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
Console.WriteLine("{0} OnDataReceive: BeginReceive", state.ID);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
else
{
Console.WriteLine("{0} OnDataReceive: Close (0 read)", state.ID);
state.SourceSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
catch
{
if (state!=null)
{
Console.WriteLine("{0} OnDataReceive: Close (exception)", state.ID);
state.SourceSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Shutdown(SocketShutdown.Both);
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
}
}
static void Main(string[] args)
{
List<Socket> sockets = new List<Socket>();
int srcPortStart = int.Parse(args[2]);
int srcPortEnd = int.Parse(args[3]);
int destPortStart = int.Parse(args[4]);
List<Thread> threads = new List<Thread>();
for (int i = 0; i < srcPortEnd - srcPortStart + 1; i++)
{
int srcPort = srcPortStart + i;
int destPort = destPortStart + i;
TcpForwarder tcpForwarder = new TcpForwarder();
Thread t = new Thread(new ThreadStart(() => tcpForwarder.Start(
new IPEndPoint(IPAddress.Parse(args[0]), srcPort),
new IPEndPoint(IPAddress.Parse(args[1]), destPort))));
t.Start();
threads.Add(t);
}
foreach (var t in threads)
{
t.Join();
}
Console.WriteLine("All threads are closed");
}
}
}
The first problem is that the code will continue on connection failure on the destination socket (the accept loop). Use a continue; in the try/catch. There is also no guarantee that the sockets are still up when you invoke the first BeginReceive. Those calls also need to be wrapped.
Always wrap callback methods in a try/catch as your application can fail otherwise (in this case OnDataRecieve).
Fix that and start to write out the exceptions. They will surely give you a hint about what went wrong.
I'm trying to setup a tcp .net socket network with Visual Studio 2010. So far it works I can connect clients up to it but currently I have no way of defining the clients that are connected. I need like an array of clients that are connected so I can be more specific about who i streamWrite to.
This is my server code I am copying all of this from http://www.codeproject.com/Articles/511814/Multi-client-per-one-server-socket-programming-in
SERVER CODE
static TcpListener tcpListener = new TcpListener(25000);
static void Listeners()
{
Socket socketForClient = tcpListener.AcceptSocket();
if (socketForClient.Connected)
{
Console.WriteLine("Client:"+socketForClient.RemoteEndPoint+" now connected to server.");
NetworkStream networkStream = new NetworkStream(socketForClient);
System.IO.StreamWriter streamWriter =
new System.IO.StreamWriter(networkStream);
System.IO.StreamReader streamReader =
new System.IO.StreamReader(networkStream);
Console.WriteLine("type your message to be recieved by client:");
string theString2 = Console.ReadLine();
streamWriter.WriteLine(theString2);
Console.WriteLine(theString2);
streamWriter.Flush();
while (true)
{
string theString = streamReader.ReadLine();
Console.WriteLine("Message recieved by client:" + theString);
if (theString == "exit")
break;
}
streamReader.Close();
networkStream.Close();
streamWriter.Close();
}
socketForClient.Close();
Console.WriteLine("Press any key to exit from server program");
Console.ReadKey();
}
public static void Main()
{
tcpListener.Start();
Console.WriteLine("************This is Server program************");
Console.WriteLine("Hoe many clients are going to connect to this server?:");
int numberOfClientsYouNeedToConnect = int.Parse(Console.ReadLine());
for (int i = 0; i < numberOfClientsYouNeedToConnect; i++)
{
Thread newThread = new Thread(new ThreadStart(Listeners));
newThread.Start();
}
}
If you want to stick to raw TCP Sockets, then you could pass a delegate/handler etc to the thread:
for (int i = 0; i < numberOfClientsYouNeedToConnect; i++)
{
Thread newThread = new Thread(new ParameterizedThreadStart(Listeners));
newThread.Start(i);
}
Your listener then needs to take an object to match the ParametrizedThreadStart see MSDN
However, if you are developing both ends (as I commented) I'd suggest something more abstract like ASP.NET SignalR or similar for such a scenario. It adds a bit overhead, but you get Server-To-Client and vice versa communication on a conveniently abstractioned level.
I am building an FTP server that will get images from a camera and store the photos in a local directory. I am having issues grabbing the file from the port given to me. Here is the conversation:
Server(me):"220 Ready!"
Client:"USER Guest"
Server: "331 Username ok, need password"
Client:"PASS "
Server: "230 user logged in"
Client: "PWD"
Server: "257 \"/\""
Client: "EPRT |1|172.22.22.103|58719|
Server:"500 IDK"
Client: "PORT 172,22,22,103,147,237"
Server:"200 Ready for Transport"
Client: "TYPE I"
Server:"200 I understand it is an image file"
Client: "STOR .TEST.RHPRYI"
I found the port by converting the last two numbers to hexadecimal (93,ED) appending them and then converting back to decimal. My final port is 37869.
I then create a new TcpClient to use as a download, but my TcpListener never picks up the camera. Also I am not sure my download method is correct.
I got lots of my code from here:
http://www.codeproject.com/Articles/380769/Creating-an-FTP-Server-in-Csharp-with-IPv-Support
Really my question comes down to: How do I get the file being sent by the client as an FTP server in C#?
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using System.Drawing;
using Microsoft.VisualBasic;
//Creates a TcpServer that is used to transfer images from the Axis Camera to a directory on a computer
//ftpdmin was chosen as the name because it was supposed to be a replica of a program in c just made in csharp
//However, it was really hard to transfer from c because all of the struct names and functions were nonintuitive
//This program is completely made from internet sources, and there are definitely bugs. For instance it does not implement
// many FTP calls.
namespace ftpdmin
{
class Server
{
//TCPListener listens to a given IP and port to wait for a connection with the camera
//Download Listener listens to the port given by the camera in the PORT command, which is the
//port at which the files needed to be downloaded are stored.
//Listen thread implements tcpListener. We do not want to be stuck in an infinite loop, but
//we always want to be listening to the camera. That is why we use another thread.
//Downlaod thread implements the downloadlistener for the same reason as above
//File name is the download files name given by the camera in the STOR command.
//direct is the directory to save the files at on the local computer. It was given in the main
// method of the console program. See Program.cs
private TcpListener tcpListener;
private TcpListener downloadListener;
private Thread listenThread;
private Thread downloadThread;
private string fileName;
private string direct;
//Initialize Ip adress and threads
public Server(string dir)
{
direct = dir;
this.tcpListener = new TcpListener(IPAddress.Parse("172.22.22.104"), 3000);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
//Start listening
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void ListenForDownloads()
{
this.downloadThread.Start();
while(true)
{
TcpClient downloadClient = this.downloadListener.AcceptTcpClient();
Thread clientDownloadThread = new Thread(new ParameterizedThreadStart(HandleClientDownload));
clientDownloadThread.Start(downloadClient);
}
}
private void HandleClientDownload(object downloadClient)
{
Console.WriteLine("IM HERE");
TcpClient downloaderClient = (TcpClient) downloadClient;
NetworkStream downloadStream = downloaderClient.GetStream();
StreamWriter downloadWriter = new StreamWriter(downloadStream, Encoding.ASCII);
StreamReader downloadReader = new StreamReader(downloadStream);
try
{
//Sets up the path to store the file
string path = Path.Combine(direct, fileName);
FileStream file = File.Create(path);
//Implements the method to download a file
CopyStream(file, downloadStream);
file.Close();
}
catch (Exception e)
{
Console.WriteLine("a socket error has occured:" + e);
}
}
private void HandleClientComm(object client)
{
//A Server is TCP has to respond to a bunch of commands from the client. The first thing it
//does when it connects is send code 220 which says it is good to continue.
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
StreamWriter writer = new StreamWriter(clientStream, Encoding.ASCII);
StreamReader reader=new StreamReader(clientStream);
writer.WriteLine("220 Ready!");
writer.Flush();
string command=reader.ReadLine().ToUpperInvariant();
int downloadPort=0;
string ipOfDownload="";
Console.WriteLine(command);
while(!command.Equals("QUIT"))
{
//USER comes with the username given to the client. Here I do not check if the cameras username
//is the same as the username in the program. I just give the command 331 which means continue.
if(command.Contains("USER"))
{
writer.WriteLine("331 Username ok, need password");
writer.Flush();
}
//PASS is the same as username. I do not check the passwords, I just give 230 which continues the FTP.
else if(command.Contains("PASS"))
{
writer.WriteLine("230 User Logged In");
writer.Flush();
}
//PWD is Print working directory. I send 257 to say I have a PWD, and I send / because that is what is saved
// in the camera. I am not actually going to save files at this directory, I just want to continue.
else if(command.Contains("PWD"))
{
writer.WriteLine("257 \"/\"");
writer.Flush();
}
//This is an important command. The client is sending an IP where it wants to do file transfers. It comes in a
//Weird format so all this function is doing is allowing me store Ip as "172.22.22.103" instead of "PORT 172,22,22,103"
//Also there is a port listed at the end, but it is given in 2 numbers. The conversion to one port number is done by
//changing the two numbers to hexadecimal, appending them, and then transforming them back to decimal.
else if(command.Contains("PORT"))
{
string portPart1 = "";
string portPart2 = "";
Console.WriteLine(command);
int numberOfCommas=0;
int i=0;
bool notPort=true;
bool isNotPortPart2=true;
while(i<command.Length && notPort)
{
if(command[i].Equals(','))
{
if(numberOfCommas==3)
{
notPort=false;
}
else
{
ipOfDownload+=".";
numberOfCommas++;
}
}
else if(Information.IsNumeric(command[i]))
{
ipOfDownload+=command[i];
}
i++;
}
while(i<command.Length && isNotPortPart2)
{
if(Information.IsNumeric(command[i]))
{
portPart1+=command[i];
}
else
{
isNotPortPart2=false;
}
i++;
}
while(i<command.Length)
{
portPart2+=command[i];
i++;
}
Console.WriteLine("IP=" +ipOfDownload);
Console.WriteLine("PortPart1="+portPart1);
Console.WriteLine("PortPart2="+portPart2);
int portPart1int = int.Parse(portPart1);
int portPart2int = int.Parse(portPart2);
string portPart1Hex = portPart1int.ToString("X");
string portPart2Hex = portPart2int.ToString("X");
string downloadPortHex = portPart1Hex + portPart2Hex;
downloadPort = Convert.ToInt32(downloadPortHex, 16);
Console.WriteLine("PortPart1Hex=" + portPart1Hex);
Console.WriteLine("PortPart2Hex=" + portPart2Hex);
Console.WriteLine("FinalPort: " + downloadPort);
this.downloadListener = new TcpListener(IPAddress.Parse(ipOfDownload), downloadPort);
this.downloadThread = new Thread(new ThreadStart(ListenForDownloads));
writer.WriteLine("200 Ready for Transport");
writer.Flush();
}
//The client sends TYPE I for image. usually an ftp would switchto binary mode because that is the only way
//a file can be transferred cleanly.
else if(command.Contains("TYPE"))
{
writer.WriteLine("200 I understand it is an image file");
writer.Flush();
}
//This command gives the name of the file being transferred. I substring to get rid of
//The STOR . that comes before the file name
else if(command.Contains("STOR"))
{
fileName = command.Substring(6);
Console.WriteLine(fileName);
}
//For all other commands sent by the client, I send 500 which means I'm not implementing those commands.
else
{
writer.WriteLine("500 IDK");
writer.Flush();
}
command=reader.ReadLine().ToUpperInvariant();
Console.WriteLine(command);
}
writer.WriteLine("221 BYE");
writer.Flush();
tcpClient.Close();
}
private static long CopyStream(Stream input, Stream output, int bufferSize)
{
byte[] buffer = new byte[bufferSize];
int count = 0;
long total = 0;
while ((count = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, count);
total += count;
}
return total;
}
private static long CopyStreamAscii(Stream input, Stream output, int bufferSize)
{
char[] buffer = new char[bufferSize];
int count = 0;
long total = 0;
using (StreamReader rdr = new StreamReader(input))
{
using (StreamWriter wtr = new StreamWriter(output, Encoding.ASCII))
{
while ((count = rdr.Read(buffer, 0, buffer.Length)) > 0)
{
wtr.Write(buffer, 0, count);
total += count;
}
}
}
return total;
}
private long CopyStream(Stream input, Stream output)
{
//if (_transferType == "I")
//{
return CopyStream(input, output, 4096);
//}
//else
//{
// return CopyStreamAscii(input, output, 4096);
//}
}
}
By the ERTP (or PORT) command, the client tells your server where the client is listening for incoming transfer connections to be initiated by the server. Instead you start listening too. So both parties are listening and nothing can happen.
So instead of TcpListener, you need to create TcpClient and actively connect.
See RFC 2428.
What you have implemented resembles passive mode (EPSV or PASV command), while the client is using an active mode.
Anyway you should better use an existing implementation of FTP server instead of trying to implement your own.
I am working on a transportation Project RailWay. Let me explain the project :there are a lot of sensors during the path of the train ,so when the train passes one of these sensors ,the sensor sends a value to CTC(A computer that manages the sensors) server the value is 1 or 0 ,1 means that the train arrive the sensor and 0 means the train left the sensor ,so every thing is ok ,now here is the scope of my project:
The CTC server send the value to MY-SERVER for example :ID=16(SENSOR-ID),state=0.it means that the train left the sensor that its id is 16 ,Note:That i know the location of sensors by id .so My problems start here : the CTC server sends its data by TCP ,so i have to create a listener to listen the data that comes from the CTC server ,(Note:Sometimes the data that comes from CTC is a lot and maybe some data be lost) ,I create a program using c# that listen the port but sometimes the data that coes fro CTC are lost why ?
So let me explain my programs:
It's the code that i wrote to get the data :
class Server
{
private TcpListener tcpListener;
private Thread listenThread;
public Server()
{
this.tcpListener = new TcpListener(IPAddress.Any, 3456);
this.listenThread = new Thread(new ThreadStart(ListenForClients));
this.listenThread.Start();
}
private void ListenForClients()
{
this.tcpListener.Start();
while (true)
{
//blocks until a client has connected to the server
TcpClient client = this.tcpListener.AcceptTcpClient();
//create a thread to handle communication
//with connected client
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
clientThread.Start(client);
}
}
private void HandleClientComm(object client)
{
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
}
tcpClient.Close();
}
}
And here i call the server class :
class Program
{
static void Main(string[] args)
{
Server obj=new Server();
}
}
The CTC code that sends data is like this(An example) :
static void Main(string[] args)
{
TcpClient client = new TcpClient();
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3456);
client.Connect(serverEndPoint);
using (NetworkStream clientStream = client.GetStream())
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes("Hello Server!");
clientStream.Write(buffer, 0, buffer.Length);
}
}
But sometimes my programs(SERVER CODE) lost data ?!!!Why ?Any idea ?
Best regards
Here is my solution of a basic Client/Server application using StreamReader and StreamWriter
Basic structure
The server will be running a TcpListener. We will use the Pending() method to check for waiting connections while not blocking the thread from exiting. When a new connection is waiting we will accept it with the AcceptTcpClient(), create a new instance of our own Client class and add it to a List<Client()> to mange it later. The class Client will store the methods to send data and hold informations like ID etc.
Code
Server Class:
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace CTCServer
{
class Server
{
//Stores the IP Adress the server listens on
private IPAddress ip;
//Stores the port the server listens on
private int port;
//Stores the counter of connected clients. *Note* The counter only gets increased, it acts as "id"
private int clientCount = 0;
//Defines if the server is running. When chaning to false the server will stop and disconnect all clients.
private bool running = true;
//Stores all connected clients.
public List<Client> clients = new List<Client>();
//Event to pass recived data to the main class
public delegate void GotDataFromCTCHandler(object sender, string msg);
public event GotDataFromCTCHandler GotDataFromCTC;
//Constructor for Server. If autoStart is true, the server will automaticly start listening.
public Server(IPAddress ip, int port, bool autoStart = false)
{
this.ip = ip;
this.port = port;
if (autoStart)
this.Run();
}
//Starts the server.
public void Run()
{
//Run in new thread. Otherwise the whole application would be blocked
new Thread(() =>
{
//Init TcpListener
TcpListener listener = new TcpListener(this.ip, this.port);
//Start listener
listener.Start();
//While the server should run
while (running)
{
//Check if someone wants to connect
if (listener.Pending())
{
//Client connection incoming. Accept, setup data incoming event and add to client list
Client client = new Client(listener.AcceptTcpClient(), this.clientCount);
//Declare event
client.internalGotDataFromCTC += GotDataFromClient;
//Add to list
clients.Add(client);
//Increase client count
this.clientCount++;
}
else
{
//No new connections. Sleep a little to prevent CPU from going to 100%
Thread.Sleep(100);
}
}
//When we land here running were set to false or another problem occured. Stop server and disconnect all.
Stop();
}).Start(); //Start thread. Lambda \(o.o)/
}
//Fires event for the user
private void GotDataFromClient(object sender, string data)
{
//Data gets passed to parent class
GotDataFromCTC(sender, data);
}
//Send string "data" to all clients in list "clients"
public void SendToAll(string data)
{
//Call send method on every client. Lambda \(o.o)/
this.clients.ForEach(client => client.Send(data));
}
//Stop server
public void Stop()
{
//Exit listening loop
this.running = false;
//Disconnect every client in list "client". Lambda \(o.o)/
this.clients.ForEach(client => client.Close());
//Clear clients.
this.clients.Clear();
}
}
}
Client Class
using System.IO;
using System.Net.Sockets;
using System.Threading;
namespace CTCServer
{
class Client
{
//Stores the TcpClient
private TcpClient client;
//Stores the StreamWriter. Used to write to client
private StreamWriter writer;
//Stores the StreamReader. Used to recive data from client
private StreamReader reader;
//Defines if the client shuld look for incoming data
private bool listen = true;
//Stores clientID. ClientID = clientCount on connection time
public int id;
//Event to pass recived data to the server class
public delegate void internalGotDataFromCTCHandler(object sender, string msg);
public event internalGotDataFromCTCHandler internalGotDataFromCTC;
//Constructor
public Client(TcpClient client, int id)
{
//Assain members
this.client = client;
this.id = id;
//Init the StreamWriter
writer = new StreamWriter(this.client.GetStream());
reader = new StreamReader(this.client.GetStream());
new Thread(() =>
{
Listen(reader);
}).Start();
}
//Reads data from the connection and fires an event wih the recived data
public void Listen(StreamReader reader)
{
//While we should look for new data
while(listen)
{
//Read whole lines. This will read from start until \r\n" is recived!
string input = reader.ReadLine();
//If input is null the client disconnected. Tell the user about that and close connection.
if (input == null)
{
//Inform user
input = "Client with ID " + this.id + " disconnceted.";
internalGotDataFromCTC(this, input);
//Close
Close();
//Exit thread.
return;
}
internalGotDataFromCTC(this, input);
}
}
//Sends the string "data" to the client
public void Send(string data)
{
//Write and flush data
writer.WriteLine(data);
writer.Flush();
}
//Closes the connection
public void Close()
{
//Stop listening
listen = false;
//Close streamwriter FIRST
writer.Close();
//Then close connection
client.Close();
}
}
}
Test code. Note: this is a console application!
using System;
using System.Net;
namespace CTCServer
{
class Program
{
static void Main(string[] args)
{
//Set title
Console.Title = "CTC-Server";
//Create new instance of the server
Server server = new Server(IPAddress.Any, 1221);
//Handle GotDataFromCTC
server.GotDataFromCTC += GotDataFromCTC;
//Start the server. We could use the autoStart in constructor too.
server.Run();
//Inform about the running server
Console.WriteLine("Server running");
//Listen for input.
while(true)
{
//Read input line from cmd
string input = Console.ReadLine();
//Stores the command itself
string command;
//Stores parameters
string param = "";
//If input line contains a whitespace we have parameters that need to be processed.
if(input.Contains(" "))
{
//Split the command from the parameter. Parte before whitespace = command, rest = parameters
command = input.Split(' ')[0];
param = input.Substring(command.Length +1);
}
else
{
//No whitespace, so we dont have any parameters. Use whole input line as command.
command = input;
}
//Process the command
switch(command)
{
//Sends a string to all clients. Everything behind "send " (Note the whitespace) will be send to the client. Exanple "send hello!" will send "hello!" to the client.
case "send":
{
//Give some feedback
Console.WriteLine("Send to all clients: {0}", param);
//Send data
server.SendToAll(param);
//Done
break;
}
//Closes connection to all clients and exits. No parameters.
case "exit":
{
//Stop the server. This will disconncet all clients too.
server.Stop();
//Clean exit
Environment.Exit(0);
//Done. We wont get here anyway.
break;
}
}
}
}
//Recived data from clien. Show it!
static void GotDataFromCTC(object sender, string data)
{
Console.WriteLine("Data from CTC-Server with ID {0} recived:\r\n{1}", (sender as Client).id, data);
}
}
}
NOTE that this application doesnt have any exception handling. I did this to show a direction, you will need to modify the code to fit to your requirements. Let me know if you need something.
Example Project (Visual Studio 2013 Pro): Download | Virustoal
I found an article about Socket programming that using StreamReader ,It using one thread ,and no information lost is happened
You can take a look here :
http://www.codeproject.com/Articles/511814/Multi-client-per-one-server-socket-programming-in
I am trying to get the standard example "echo" client/server application to run as quickly as possible and I am sure that the network is a limiting factor. I have a 1 gigabit network card and when I use resource monitor I am only getting 7 megabit out of the client.
I understand the basics of sockets, message framing and length indicators, receiving all bytes indicated by the length indicator. Keep alive packets, half open connections etc.
I started out using stock standard socket operations and then switched to using async. (I didn't change the send to async because someone [who seemed knowledgeable said it shouldn't have any impact]) I have the exact same performance and I can only think that all the material was assuming that I some other work that could be done on the same thread. But in my quick test I had dedicated 1 thread to conterminously spin in a loop sending and another completely different thread to receive.
I have tried everything and am completely lost as to where I can get more performance. I used IPerf and that reported back speeds of 1 gigabit per second and resource monitor also showed that to be chewing up the bandwidth.
Even if someone can maybe point me in the direction of a more complete example. Most of the ones I have come across are trivial or incomplete.
Here is the general code.
class Program
{
private static Socket sock;
private static BlockingCollection<string> queue;
private static int bytesReceived;
private static byte[] dataBuffer;
private static readonly byte[] lengthBuffer = new byte[4];
private static byte[] PrependLengthIndicator(byte[] data)
{
return BitConverter.GetBytes(data.Length).Concat(data).ToArray();
}
private static void Receive()
{
if (dataBuffer == null)
{
sock.BeginReceive(lengthBuffer, 0, 4, SocketFlags.None, ReceiveCallback, null);
}
else
{
sock.BeginReceive(dataBuffer, 0, bytesReceived, SocketFlags.None, ReceiveCallback, null);
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
bytesReceived += sock.EndReceive(ar);
if (dataBuffer == null)
{
// Currently receiving length indicator
if (bytesReceived >= 4)
{
var length = BitConverter.ToInt32(lengthBuffer, 0);
dataBuffer = new byte[length];
bytesReceived = 0;
}
}
else
{
if (bytesReceived == dataBuffer.Length)
{
// Finished reading
var request = Encoding.ASCII.GetString(dataBuffer);
dataBuffer = null;
bytesReceived = 0;
queue.Add(request);
}
}
ContinueReading();
}
private static void ContinueReading()
{
// Read into the appropriate buffer: length or data
if (dataBuffer != null)
{
sock.BeginReceive(dataBuffer, bytesReceived, dataBuffer.Length - bytesReceived, SocketFlags.None, ReceiveCallback, null);
}
else
{
sock.BeginReceive(lengthBuffer, bytesReceived, lengthBuffer.Length - bytesReceived, SocketFlags.None, ReceiveCallback, null);
}
}
}
Here is the server portion:
static void Main(string[] args)
{
var listenSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSock.Bind(new IPEndPoint(IPAddress.Parse(ConfigurationManager.AppSettings["LocalIp"]), 3333));
listenSock.Listen(10);
Console.WriteLine("Server started...");
sock = listenSock.Accept();
Console.WriteLine("Connection accepted.");
queue = new BlockingCollection<string>();
Receive();
var count = 0;
var sender = new Thread(() =>
{
while (true)
{
var bar = queue.Take() + "Resp";
count++;
var resp = Encoding.ASCII.GetBytes(bar);
var toSend = PrependLengthIndicator(resp);
if (count % 10000 == 0)
{
Console.WriteLine(bar);
}
sock.Send(toSend);
}
});
sender.Start();
}
Here is the client portion:
static void Main(string[] args)
{
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("Connecting...");
sock.Connect(IPAddress.Parse(ConfigurationManager.AppSettings["EndPointIp"]), 3333);
Console.WriteLine("Connected.");
Receive();
var count = 0;
while(true)
{
count++;
var foo = "Echo-" + count;
var data = Encoding.ASCII.GetBytes(foo);
var toSend = PrependLengthIndicator(data);
sock.Send(toSend);
}
}
You are sending tiny messages. Think about how many millions of them you would need to saturate a 1 Gbit/sec link. Each call to a socket burns CPU.
Send bigger messages. Also, try not to allocate new buffers all the time. Don't use Enumerable.Concat to concatenate byte buffers because that operates byte-by-byte in a horribly inefficient way. Use Array.Copy with preallocated arrays.
If you are using very few threads switch to synchronous IO as it will be faster (really! it has less overhead).
You can confirm that this answer is correct by running a dumb infinite send loop that just sends buffers of 64KB synchronously all the time. It will saturate the link.