I have a small question about multithreaded connections between one (Server) to many (Clients) relationship.
My question is:
Is my script multithreaded? Or Multi process? I have received some feedback stating that it is multiprocess?
Would each client receive their stream independently?
I'd just like to know if this is the correct approach to this.
Here you can see I start to listen to client connections and create a thread for each client connection and I pass the client :
private void StartListen()
{
//Creating a TCP Connection and listening to the port
tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666);
tcpListener.Start();
toolStripStatusLabel1.Text = "Listening on port 6666 ...";
int counter = 0;
while (true)
{
try
{
client = tcpListener.AcceptTcpClient();
clientList.Add(client);
IPEndPoint ipend = (IPEndPoint)client.Client.RemoteEndPoint;
//Updating status of connection
toolStripStatusLabel1.Text = "Connected from " + IPAddress.Parse(ipend.Address.ToString());
th_inPutStream = new Thread(delegate () { inPutStream(client, counter); });
th_inPutStream.Start();
counter++;
}
catch (Exception err)
{
Cleanup_dep();
}
}
}
This is where I handle the client input stream where i pre-process the client input stream:
private void inPutStream(TcpClient client, int clientID)
{
try
{
while (true)
{
NetworkStream networkstream = client.GetStream();
if (networkstream.DataAvailable == true)
{
int messageID = 0;
int messageSize = 0;
int bufferSize = 100;
//First retrieve size of header
byte[] fileSizeBytes = new byte[4];
int bytes = networkstream.Read(fileSizeBytes, 0, 4);
int dataLength = BitConverter.ToInt32(fileSizeBytes, 0);
//Read stream containing header with message details
byte[] header = new byte[dataLength];
int headerbyte = networkstream.Read(header, 0, dataLength);
string headerString = Encoding.ASCII.GetString(header, 0, headerbyte);
// Process Message information ie Message ID & Message Size
string[] headerSplit = headerString.Split(new string[] { "\r\n" }, StringSplitOptions.None);
Dictionary<string, string> headers = new Dictionary<string, string>();
foreach (string s in headerSplit)
{
if (s.Contains(":"))
{
headers.Add(s.Substring(0, s.IndexOf(":")), s.Substring(s.IndexOf(":") + 1));
}
}
//Fetch Message ID & Size
messageSize = Convert.ToInt32(headers["len"]);
messageID = Convert.ToInt32(headers["MsgId"]);
//Filter actions by Message ID
if (messageID == 1)//Machine info
{
string machineinfo = receiveMessage(messageSize, bufferSize, networkstream);
clientNames.Add(machineinfo.Split(new char[] { '\r', '\n' })[0]);
updateClientListUI();
}
if (messageID == 2)//CMD Commands
{
cmdres = receiveMessage(messageSize, bufferSize, networkstream);
activeForm.updateText(cmdres);
}
}
}
}
catch (Exception err)
{
Cleanup_dep();
}
}
Since you are using Thread and not Process I don't see any indication as to why this would be a multi process approach.
Without knowing how TcpClient.GetStream is exactly implemented, I would be suprised if the call on one client would have anything to do with the same call on another client. Other than obvious, unavoidable, coherences like sharing the same bandwidth if the connections are accepted on the same nic of course.
Depends on what you are trying to do. If this is for work, I would question if there isn't an existing solution you could use. If this is for education there are a few improvements that come to mind:
use async and Task instead of Thread is usually recommended
using var networkstream = client.GetStream(); will make sure the stream is disposed of correctly
Related
I might have a pretty basic question for some of you.
I have created a one(server) to many(clients) relationship where in which server selects a client to send and receive data from and to.
My question is, how can I distinguish incoming data from the different clients? Would I have to simply indicate this in a buffer?
Currently My server listens, accepts and adds clients to a list clientList:
private void StartListen()
{
//Creating a TCP Connection and listening to the port
tcpListener = new TcpListener(System.Net.IPAddress.Any, 6666);
tcpListener.Start();
toolStripStatusLabel1.Text = "Listening on port 6666 ...";
int counter = 0;
appStatus = 0;
while (true)
{
try
{
client = tcpListener.AcceptTcpClient();
counter++;
clientList.Add(client);
IPEndPoint ipend = (IPEndPoint)client.Client.RemoteEndPoint;
//Updating status of connection
toolStripStatusLabel1.Text = "Connected from " + IPAddress.Parse(ipend.Address.ToString());
appStatus = 1;
th_inPutStream = new Thread(delegate () { inPutStream(client); });
th_inPutStream.Start();
th_checkConnection = new Thread(checkConnection);
th_checkConnection.Start();
}
catch (Exception err)
{
Cleanup_dep();
}
}
}
Now i assume if I want to send a message to a specific client, id select the client by clientList[i] and send data.
My question is, when data is available in the network stream to read, how can i know which client its coming from?
This is how I manage incoming data:
private void inPutStream(TcpClient client)
{
try
{
while (true)
{
NetworkStream networkstream = client.GetStream();
if (networkstream.DataAvailable == true)
{
int messageID = 0;
int messageSize = 0;
int bufferSize = 100;
//First retrieve size of header
byte[] fileSizeBytes = new byte[4];
int bytes = networkstream.Read(fileSizeBytes, 0, 4);
int dataLength = BitConverter.ToInt32(fileSizeBytes, 0);
//Read stream containing header with message details
byte[] header = new byte[dataLength];
int headerbyte = networkstream.Read(header, 0, dataLength);
string headerString = Encoding.ASCII.GetString(header, 0, headerbyte);
// Process Message information ie Message ID & Message Size
string[] headerSplit = headerString.Split(new string[] { "\r\n" }, StringSplitOptions.None);
Dictionary<string, string> headers = new Dictionary<string, string>();
foreach (string s in headerSplit)
{
if (s.Contains(":"))
{
headers.Add(s.Substring(0, s.IndexOf(":")), s.Substring(s.IndexOf(":") + 1));
}
}
//Fetch Message ID & Size
messageSize = Convert.ToInt32(headers["len"]);
messageID = Convert.ToInt32(headers["MsgId"]);
//Filter actions by Message ID
if (messageID == 1)//Machine info
{
string machineinfo = processMessage(messageSize, bufferSize, networkstream);
clientNames.Add(machineinfo.Split(new char[] { '\r', '\n' })[0]);
updateClientListUI();
}
if (messageID == 2)//CMD Commands
{
updateText(txtCmdConsole, processMessage(messageSize, bufferSize, networkstream));
}
}
}
}
catch (Exception err)
{
{
Cleanup_dep();
}
}
}
So if networkstream.DataAvailable == true then it reads, but how can i know from which stream?
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.
static void Main(string[] args)
{
Console.Title = "Socket Server";
Console.WriteLine("Listening for client messages");
Socket serverSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
IPAddress serverIp = IPAddress.Any;
IPEndPoint serverEP = new IPEndPoint(serverIp, 8000);
SocketPermission socketPermission = new SocketPermission(NetworkAccess.Accept,
TransportType.Tcp,
"127.0.0.1", 8000);
serverSocket.Bind(serverEP);
serverSocket.Listen(2);
while(true)
{
//Socket connection = serverSocket.Accept();
connection = serverSocket.Accept();
Thread clientThread = new Thread(new ParameterizedThreadStart(MultiUser));
clientThread.Start(connection);
}
}
public static void MultiUser(object connection)
{
byte[] serverBuffer = new byte[10025];
string message = string.Empty;
int bytes = ((Socket)connection).Receive(serverBuffer, serverBuffer.Length, 0);
message += Encoding.ASCII.GetString(serverBuffer, 0, bytes);
Console.WriteLine(message);
TcpClient client = new TcpClient();
client.Client = ((Socket)connection);
IntPtr handle = client.Client.Handle;
}
I want to write a chat program which has one server and 2 clients. The problem is that, I can not direct the message sent from the client1 to client2 via the server. How can the server distinguish threads so that it can send the received message from client1 to client2?
Each client has their own handle. You can access this via the Handle property. For example:
TcpClient client = tcpListener.AcceptTcpClient();
IntPtr handle = client.Client.Handle; //returns a handle to the connection
Then all you need to do is store this in a hashtable, and iterate through it, looking for available data. When you detect data on the wire for one of the connections, then save it and retransmit it to the other clients in the table.
Remember to make sure that you make this multithreaded so a listen request on one client does not block any send or receive functions on other clients!
I've added some code here you should be able to work with (tested it out on my system)
private void HandleClients(object newClient)
{
//check to see if we are adding a new client, or just iterating through existing clients
if (newClient != null)
{
TcpClient newTcpClient = (TcpClient)newClient;
//add this client to our list
clientList.Add(newTcpClient.Client.Handle, newTcpClient);
Console.WriteLine("Adding handle: " + newTcpClient.Client.Handle); //for debugging
}
//iterate through existing clients to see if there is any data on the wire
foreach (TcpClient tc in clientList.Values)
{
if (tc.Available > 0)
{
int dataSize = tc.Available;
Console.WriteLine("Received data from: " + tc.Client.Handle); //for debugging
string text = GetNetworkString(tc.GetStream());
//and transmit it to everyone else
foreach (TcpClient otherClient in clientList.Values)
{
if (tc.Client.Handle != otherClient.Client.Handle)
{
Send(otherClient.GetStream(), text);
}
}
}
}
}
public void Send(NetworkStream ns, string data)
{
try
{
byte[] bdata = GetBytes(data, Encoding.ASCII);
ns.Write(bdata, 0, bdata.Length);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
protected string GetNetworkString(NetworkStream ns)
{
if (ns.CanRead)
{
string receivedString;
byte[] b = GetNetworkData(ns);
receivedString = System.Text.Encoding.UTF8.GetString(b);
log.Info("Received string: " + receivedString);
return receivedString;
}
else
return null;
}
protected byte[] GetNetworkData(NetworkStream ns)
{
if (ns.CanRead)
{
log.Debug("Data detected on wire...");
byte[] b;
byte[] myReadBuffer = new byte[1024];
MemoryStream ms = new MemoryStream();
int numberOfBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = ns.Read(myReadBuffer, 0, myReadBuffer.Length);
ms.Write(myReadBuffer, 0, numberOfBytesRead);
}
while (ns.DataAvailable);
//and get the full message
b = new byte[(int)ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(b, 0, (int)ms.Length);
ms.Close();
return b;
}
else
return null;
}
You will want to call HandleClients from a main thread that checks to see if there are any pending requests or not, and runs on a loop.
I'm designing an asynchronous TCP server using async-await approach making use of event-driven .NET environment with the following features:
Request and response functionality.
Server side initiated events.
Does this server design is correct in terms of async-await approach?
Code below covers only basic functionality to judge whether I'm missing something or not.
The server uses COM Type library, which is where PRMasterAutomation class comes from.
public class AsyncServer {
private const int BufferSize = 4096;
private static bool ServerRunning = true;
private List<TcpClient> clients = new List<TcpClient>();
private PRMasterAutomation automation;
public AsyncServer()
{
InitializeComponent();
automation = new PRMasterAutomation();
automation.OnMonitoringEvent += automation_OnMonitoringEvent;
if (!automation.Login("ADMIN", ""))
return;
if (automation.GetPRMasterState() == PRMasterStateEnum.StateIdle)
automation.StartMonitoring();
var tcpServer = new TcpListener(IPAddress.Any, 9001);
tcpServer.Start();
ListenForClients(tcpServer);
}
private async void ListenForClients(TcpListener tcpServer)
{
while (ServerRunning)
{
TcpClient tcpClient = await tcpServer.AcceptTcpClientAsync();
clients.Add(tcpClient);
ProcessClient(tcpClient);
}
}
private async void ProcessClient(TcpClient tcpClient)
{
var stream = tcpClient.GetStream();
var buffer = new byte[BufferSize];
int amountRead;
string xml;
while (ServerRunning)
{
if ((amountRead = await stream.ReadAsync(buffer, 0, BufferSize)) > 0)
{
var message = Encoding.ASCII.GetString(buffer, 0, amountRead);
xml = "";
if (message.Equals("get_users"))
{
xml = automation.GetUsersXml();
}
else if (message.Equals("get_events"))
{
xml = automation.GetEventsHistoryXml("");
}
if (xml.Length > 0)
{
xml += "\r\n";
await stream.WriteAsync(Encoding.ASCII.GetBytes(xml), 0, xml.Length);
}
}
}
}
async void automation_OnMonitoringEvent(DateTime date, DateTime time, int networkID, int readerID, int userID, int groupID, int eventCode, int zoneID, int TandAID, string strEvent, string strAccessPoint, string strUserSource, string strGroup, string strNetwork, string strZone, string strTandAMode)
{
foreach (var c in clients)
{
if (c != null && c.Connected)
{
var stream = c.GetStream();
StringBuilder sb = new StringBuilder();
sb.Append(date.ToString() + ";" + time.ToString() + ";" + networkID.ToString() + ";" + userID.ToString() + "\r\n");
await stream.WriteAsync(Encoding.ASCII.GetBytes(sb.ToString()), 0, sb.Length);
}
}
}
}
Does this server design is correct in terms of async-await approach?
Yes, but not in terms of sockets. ReadAsync will return any number of bytes between 1 and infinite, it will not return messages.
Stream.ReadAsync(): The result value can be less than the number of bytes requested if the number of bytes currently available is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached.
You have to create a buffer and keep reading and adding to that buffer until you know you have received a whole message. This means checking for \r\n in your case. If you encounter such a delimiter, you can extract the message from the buffer and start receiving a new message.
I am in need of a C# layer which will help to exchange the data between two TCP ports which are listening.
For example, There is a listener port # 192.168.1.2::5555 and another listener port # 192.168.1.4::6666.
I am able to establish the connection to both the listeners using socket.connect
I am getting confused during creation of 2 threads
1> Sock1.read()->convert to bytes ->sock2 .write()
2> Sock2.read()->Convert to bytes -> Sock1.write()
I think this is entering into the infinite loop. Is there any better way of exchanging packets between 2 Listening ports by establishing connection to both ports?
I have to implement a method
Private void ExchangePackets(IpEndpoint ipe1,IpEndpoint ipe2)
{
//code here
}
It can be something like this:(Not tested)
void ExchangePackets(IPEndPoint ipe1, IPEndPoint ipe2)
{
TcpClient tcp1 = new TcpClient();
tcp1.Connect(ipe1);
TcpClient tcp2 = new TcpClient();
tcp2.Connect(ipe2);
Task.Factory.StartNew(() => ByPass(tcp1, tcp2), TaskCreationOptions.LongRunning);
Task.Factory.StartNew(() => ByPass(tcp2, tcp1), TaskCreationOptions.LongRunning);
}
void ByPass(TcpClient tcp1, TcpClient tcp2)
{
using (tcp1)
using (tcp2)
{
Stream s1 = tcp1.GetStream();
Stream s2 = tcp2.GetStream();
byte[] buf = new byte[0x10000];
int len = s1.Read(buf, 0, buf.Length);
while (len > 0)
{
s2.Write(buf, 0, len);
len = s1.Read(buf, 0, buf.Length);
}
}
}
$Finally Achieved it :-) $
private void Form1_Load(object sender, EventArgs e)
{
RoutingClient=new TcpClient("127.0.0.1",5765);
ServerClient = new TcpClient("127.0.0.1", 5766);
rcStream = RoutingClient.GetStream();
ScStream = ServerClient.GetStream();
//start 2 threads
Thread t1 = new Thread(()=>ExchangePackets(rcStream,ScStream));
t1.Start();
Thread t2 = new Thread(() => ExchangePackets(ScStream, rcStream));
t2.Start();
}
private static void ExchangePackets(NetworkStream FirstStream, NetworkStream SecondStream)
{
try
{
while (true)
{
if (FirstStream.CanRead)
{
byte[] myReadBuffer = new byte[1024];
StringBuilder myCompleteMessage = new StringBuilder();
MemoryStream ms = new MemoryStream();
int numberOfBytesRead = 0;
int TotalBytesRead = 0;
// Incoming message may be larger than the buffer size.
do
{
numberOfBytesRead = FirstStream.Read(myReadBuffer, 0, myReadBuffer.Length);
ms.Write(myReadBuffer, TotalBytesRead, numberOfBytesRead);
myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));
TotalBytesRead = TotalBytesRead + numberOfBytesRead;
}
while (FirstStream.DataAvailable);
MessageBox.Show("You received the following message : " +
myCompleteMessage);
if (SecondStream.CanWrite)
{
byte[] myWriteBuffer = ms.ToArray();
SecondStream.Write(myWriteBuffer, 0, myWriteBuffer.Length);
}
else
{
MessageBox.Show("Sorry. You cannot write to this NetworkStream.");
}
}
else
{
MessageBox.Show("Sorry. You cannot read from this NetworkStream.");
}
}
}
catch (Exception ex)
{
MessageBox.Show("Routing to Server:" + ex.Message);
}
}
EDIT: On closer examination, I don't think my answer is suitable for your situation. Your question describes a number of fixed constraints that might prevent you implementing it this way. I will leave my answer here for other people though, as I do think it is generally the right approach.
Is there any better way of exchanging packets between 2 Listening
ports by establishing connection to both ports?
You don't need to reinvent the wheel :)
You can use WCF to create a full-duplex service with TCP transport?