I have a simple multithreaded C# server and client. When just one client is connected I can interact with it fine, but when two or more are connected, it seems I am using the last NetworkStream. What I'd like to be able to do is give an input command that specifies the stream to read and write to. So, for example, the first client is "Client 1" and the second client is "Client 2." I'd just type "Client 2" into my command textbox and it will get the stream for the second client.
The problem is, I don't know how to assign the text to the clients. Here is the relevant code from the server:
private void ClientThread(Object client)
{
NetworkStream networkStream = ((TcpClient)client).GetStream();
Dictionary<int, NetworkStream> myClients = new Dictionary<int, NetworkStream>(); // This didn't work.
myClients.Add(counter, ((TcpClient)client).GetStream()); // Wouldn't write.
counter = counter + 1;
streamReader = new StreamReader(networkStream);
streamWriter = new StreamWriter(networkStream);
strInput = new StringBuilder();
while (true)
{
try
{
strInput.Append(streamReader.ReadLine());
strInput.Append("\r\n");
}
catch (Exception error)
{
break;
}
Application.DoEvents();
DisplayMessage(strInput.ToString());
strInput.Remove(0, strInput.Length);
}
}
private void textBox2_KeyDown(object sender, KeyEventArgs e)
{
try
{
if (e.KeyCode == Keys.Enter)
{
//ListView.SelectedListViewItemCollection stuff = listView1.SelectedItems;
//ip is displayed in listView1, if I could also bind the stream for the ip
//to it and select it, that would be cool.
{
strInput.Append(textBox2.Text.ToString());
streamWriter.WriteLine(strInput);
streamWriter.Flush();
strInput.Remove(0, strInput.Length);
if (textBox2.Text == "cls") textBox1.Text = "";
textBox2.Text = "";
}
}
}
catch (Exception error) { }
}
So, how can I do this?
NetworkStream networkStream = myClients[2];
using(streamWriter = new StreamWriter(networkStream))
{
streamWriter.WriteLine("hello client 2"); // send something to Client 2
}
networkStream = myClients[4];
using(streamWriter = new StreamWriter(networkStream))
{
streamWriter.WriteLine("hello client 4"); // send something to Client 4
}
You are obviously storing all your client streams into a dictionary. Just load that stream into a StreamWriter and send your data.
Make your dictionary myClients class field and then just get your currently active stream like above.
Related
There are two C# projects: one project is for the client, the other one is for the server. First step is to run the server , then to choose a target folder, after that to run the client project, to choose some text.txt to send to the server's target folder.
Only client can send files to the server
Demo:
1.choosing file target 2.client sends
+------------+
| tar folder | <---------------- text.txt
+------------+
My problem: there isn't compile errors or syntax errors in both projects, the only problem is that the server doesn't receives the .txt file.
Client:
First I designed a form for the client such as:
And placed an OpenFileDialog from the ToolBox-> Dialogs-> OpenFileDialog control.
Full code:
namespace SFileTransfer
{
public partial class Form1 : Form
{
string n;
byte[] b1;
OpenFileDialog op;
private void button1_Click(object sender, EventArgs e) //browse btn
{
op = new OpenFileDialog();
if (op.ShowDialog() == DialogResult.OK)
{
string t = textBox1.Text;
t = op.FileName;
FileInfo fi = new FileInfo(textBox1.Text = op.FileName);
n = fi.Name + "." + fi.Length;
TcpClient client = new TcpClient("127.0.0.1", 8100);//"127.0.0.1", 5055
StreamWriter sw = new StreamWriter(client.GetStream());
sw.WriteLine(n);
sw.Flush();
// label2.Text = "File Transferred....";
}
}
private void button2_Click(object sender, EventArgs e) //send btn
{
TcpClient client = new TcpClient("127.0.0.1", 8100);//5050
Stream s = client.GetStream();
b1 = File.ReadAllBytes(op.FileName);
s.Write(b1, 0, b1.Length);
client.Close();
// label2.Text = "File Transferred....";
}
}
}
Server:
Created and designed a Form for Server like:
Then Placed a folderBrowserDialog from the ToolBox->Dialogs-> folderBrowserDialog.
Full code:
namespace SFileTransferServer
{
public partial class Form1 : Form
{
string rd;
byte[] b1;
string v;
int m=1;
TcpListener list;
TcpClient client;
Int32 port = 8100;//5050
Int32 port1 = 8100;//5055
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
private void button1_Click(object sender, EventArgs e) //browse button
{
if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
{
textBox1.Text = folderBrowserDialog1.SelectedPath;
list = new TcpListener(localAddr, port1);
list.Start();
client = list.AcceptTcpClient();
MessageBox.Show("ggggggg" + m.ToString());
Stream s = client.GetStream();
b1 = new byte[m];
s.Read(b1, 0, b1.Length);
MessageBox.Show(textBox1.Text);
File.WriteAllBytes(textBox1.Text+ "\\" + rd.Substring(0, rd.LastIndexOf('.')), b1);
list.Stop();
client.Close();
label1.Text = "File Received......";
}
}
private void Form2_Load(object sender, EventArgs e)
{
TcpListener list = new TcpListener(localAddr, port);
list.Start();
TcpClient client = list.AcceptTcpClient();
MessageBox.Show("Client trying to connect");
StreamReader sr = new StreamReader(client.GetStream());
rd = sr.ReadLine();
v = rd.Substring(rd.LastIndexOf('.') + 1);
m = int.Parse(v);
list.Stop();
client.Close();
}
}
}
Based on this page
I'm assuming you are a new coder because I'm seeing a lot of inefficient code.
On the Client App: Don't redeclare the TcpClient, instead declare it in the global scope and just reuse it.
Then On the server side: Always use the correct datatype IF AT ALL possible
Int16 port = 8100;
//same as
int port = 8100;
Then on to the question: First you are only reading a single byte from the received data
int m=1;
byte[] b1 = new byte[m];
s.Read(b1, 0, b1.Length);
//Then if you know the length of the byte, why do the computation of b1.Length, just use
s.Read(b1, 0, 1);
I see now you are also opeing the connection on the Load event. But that variable is not in the global scope so you are essentially creating a variable in the Load event then after the Load event finishes, sending it to the garbage collection and then declaring a variable with the same name in the button click event.
So try declaring the TcpListener object in the global scope then assign it in the load event and start listening.
Now the AcceptTcpClient() method is a blocking method so it will block your thead until it receives a connection attempt at which point it will return the client object. So try to use this instead:
https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclient(v=vs.110).aspx
using System.Threading;
TcpClient client;
TcpListener server = new TcpListener("127.0.0.1",8100)
Thread incoming_connection = new Thread(ic);
incoming_connection.Start();
private void ic() {
client = server.AcceptTcpClient();
Stream s = client.GetStream();
//And then all the other code
}
Or you can use the Pending method as suggested by MSDN (on the ref page).
EDIT: using threading like this is 'not safe', so if you don't understand threading go read up on it first, this post doesn't cover how to use multi-threading.
I have a TCP server which writes data back to the client only for certain messages which the clients sends to the server.
It is basically is a command based server for which the server responds with a string only for certain commands otherwise nothing is sent back to the client.
The code given below is an approach which assumes that if any data is sent by the server it shows it as "MESSAGE FROM SERVER" appended with the data which was sent.
class TcpEchoClient
{
static void Main(string[] args)
{
Console.WriteLine("Starting echo client...");
string ipaddress = "127.0.0.1";
TcpClient client = null;
NetworkStream netstream = null;
try
{
client = new TcpClient(ipaddress,1000);
netstream = client.GetStream();
}
catch
{
Console.ReadKey();
Environment.Exit(0);
}
while(true)
{
Console.WriteLine("Message : ");
string t = Console.ReadLine();
string readdata = null;
Console.WriteLine("\n");
if (write(t,netstream))
{
Console.WriteLine("Message sent.");
if (client.Available!=0)
{
readdata = read(netstream);
Console.WriteLine("MESSAGE FROM SERVER : "+readdata);
}
}
else
{
Console.WriteLine("Unable to send message.");
}
}
}
static bool write(string dat, NetworkStream stream)
{
try
{
StreamWriter writer = new StreamWriter(stream) { AutoFlush = true };
try{writer.WriteLine(dat);}
catch (IOException){return false;}
if (SHAHash(dat, "DATA") != SHAHash(read(stream), "DATA"))
return false;
}catch (InvalidOperationException){return false;}
return true;
}
static string read(NetworkStream stream)
{
StreamReader reader = new StreamReader(stream);
string readdata = null;
try
{
readdata = reader.ReadLine();
reader.BaseStream.Flush();
}
catch(IOException)
{
return null;
}
return readdata;
}
}
The function SHAHash is not shown in this post. Its format is SHAHash(message,salt).
The problem faced is that messages sent by the server is not always read by the client. Sometimes the data sent by the server shows up a the client console, and sometimes it does not.
What correction should I make to the above code so that I can read data from the server only when it sends it. That is I require the following code to be executed only when the server sends some data to the client otherwise it should not be executed.
readdata = read(netstream);
Console.WriteLine("MESSAGE FROM SERVER : "+readdata);
Be prudent when using flush or autoflush. Sometimes it executes before send/receive operations... but this usually happens when working with threads.
My first tip that the stream readers/writers are not destructed properly. Try packing them into a using statement.
TCP isnt synchronous so you can't write data and expect the response to be available immediately. When you do the following check
if (client.Available!=0)
there is no guarantee that the server has sent any response yet. You need to keep checking until there is data available or read the data asynchronously
I would use NetworkStream.BeginRead and callbacks to get the server response
class StreamData
{
public NetworkStream netstream;
public byte[] myReadBuffer;
}
class TcpEchoClient
{
static void Main(string[] args)
{
Console.WriteLine("Starting echo client...");
string ipaddress = "127.0.0.1";
TcpClient client = null;
NetworkStream netstream = null;
try
{
client = new TcpClient(ipaddress, 13000);
netstream = client.GetStream();
}
catch
{
Console.ReadKey();
Environment.Exit(0);
}
var streamData = new StreamData
{
netstream = netstream,
myReadBuffer = new byte[1024],
};
netstream.BeginRead(streamData.myReadBuffer, 0, streamData.myReadBuffer.Length,
new AsyncCallback(myReadCallBack),
streamData);
while (true)
{
Console.WriteLine("Message : ");
string t = Console.ReadLine();
Console.WriteLine("\n");
if (write(t, netstream))
{
Console.WriteLine("Message sent.");
}
else
{
Console.WriteLine("Unable to send message.");
}
}
}
static void myReadCallBack(IAsyncResult ar)
{
var streamData = (StreamData)ar.AsyncState;
int bytesRead = streamData.netstream.EndRead(ar);
var readdata = Encoding.ASCII.GetString(streamData.myReadBuffer, 0, bytesRead);
//Be aware that this might not be the complete message depending on the size of the message and the buffer size
Console.WriteLine("You received the following message : " + readdata);
//Start waiting for more data
streamData.netstream.BeginRead(streamData.myReadBuffer, 0, streamData.myReadBuffer.Length,
new AsyncCallback(myReadCallBack),
streamData);
}
static bool write(string dat, NetworkStream stream)
{
try
{
StreamWriter writer = new StreamWriter(stream) { AutoFlush = true };
try { writer.WriteLine(dat); }
catch (IOException) { return false; }
//if (SHAHash(dat, "DATA") != SHAHash(read(stream), "DATA"))
// return false;
}
catch (InvalidOperationException) { return false; }
return true;
}
}
good day all
I am going over some network programming to implement with a larger application, but for basics, I am creating a simple network chat server client application with the help of this link
what should happen:
When receiving the data from the client, the message box pops up showing a socket connection to my PC ip address with port, but
Problem:
the messagebox which displays the message sent is empty (aka ""), I do not understand what I am doing wrong.
Advice?
had a look at this, but I do not think this is appropriate for my situation network stream with buffers
client (sends data)
const int _PORT = 80;
public Form1()
{
InitializeComponent();
}
private void sendText(string _reciever_ip, string _MESSAGE)
{
TcpClient _sender = new TcpClient(_reciever_ip, _PORT);
try
{
Stream s = _sender.GetStream();
StreamWriter sw = new StreamWriter(s);
sw.WriteLine(_MESSAGE);
s.Close();
}
catch (Exception x)
{
MessageBox.Show(x.ToString());
}
_sender.Close();
}
private void button2_Click(object sender, EventArgs e)
{
sendText(inputT_IP.Text, inputT_Msg.Text);
}
server (recieves data)
const int _PORT = 80;
static List<string> _ipaddress_list = new List<string>();
public Form1()
{
InitializeComponent();
}
private void recieveText(string _IPADDRESS)
{
//open a listener for a tcp client, to the same for UDp client
TcpListener _reciever_client = new TcpListener(IPAddress.Parse(_IPADDRESS), _PORT); //how would you listen for a connection from any port?
_reciever_client.Start();
while (true)
{
Socket _listener_socket = _reciever_client.AcceptSocket();
try
{
MessageBox.Show("Recieving from : " + _listener_socket.RemoteEndPoint.ToString());
Stream _net_stream = new NetworkStream(_listener_socket);
StreamReader sr = new StreamReader(_net_stream);
MessageBox.Show(sr.ReadLine());
//richTextBox1.AppendText();
}
catch (Exception x)
{
MessageBox.Show(x.ToString());
}
_listener_socket.Close();
}
}
void GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
_ipaddress_list.Add(ip.ToString());
}
}
}
private void button1_Click(object sender, EventArgs e)
{
GetLocalIPAddress();
foreach (string item in _ipaddress_list)
{
(new Thread(() => recieveText(item))).Start();
}
}
A StreamWriter buffers writes, so your code;
Stream s = _sender.GetStream();
StreamWriter sw = new StreamWriter(s);
sw.WriteLine(_MESSAGE);
s.Close();
...actually writes to the StreamWriter's in memory buffer and closes the socket before the data has been passed from the StreamWriter to the network.
If you instead close the StreamWriter;
Stream s = _sender.GetStream();
StreamWriter sw = new StreamWriter(s);
sw.WriteLine(_MESSAGE);
sw.Close();
...Close() actually flushes the buffer to the underlying socket, and then closes the underlying socket after the data has been sent.
I wasn't taught networking very well. I know the basics of TCP/IP, but I'm facing an assignment where I have to manage multiple threads for multiple projects that communicate with each other in the same solution. I'm working in Window's Forms, C#, and for simplicity's sake I'll write only what needs to be written.
This is my Client class, which connects to the Server, handles messages to and from the server, and logs out from the server.
private TcpClient ClientConnection = null;
private NetworkStream CommandStream = null;
private Thread ClientThread = null;
bool isRunning = false;
public Client()
{
InitializeComponent();
try
{
//open the connection to the command port
ClientConnection = new TcpClient(address, Convert.ToInt32(port));
//Get the command stream
CommandStream = ClientConnection.GetStream();
if (CommandStream != null)
{
isConnected = true;
ClientThread = new Thread(Receive);
isRunning = true;
ClientThread.Start();
}
errorLabel.Visible = false;
}
catch (Exception ex)
{
errorLabel.Visible = true;
errorLabel.Text = "Failed to Connect";
}
}
private void Receive()
{
Byte[] data = new Byte[1024];
string message = string.Empty;
int BytesReceived = 0;
while (isRunning == true)
{
BytesReceived = CommandStream.Read(data, 0, 1024);
message = Encoding.ASCII.GetString(data, 0, BytesReceived);
//Do something with the message
}
}
private void logoutButton_Click(object sender, EventArgs e)
{
//Do logout logic here
errorLabel.Visible = false;
try
{
if (ClientConnection != null)
{
Byte[] command = new Byte[1024];
string commandStr = "SHUTDOWN";
command = Encoding.ASCII.GetBytes(commandStr);
CommandStream.Write(command, 0, command.GetLength(0));
ClientConnection.Close();
CommandStream.Close();
isRunning = false;
if (ClientThread.IsAlive == true)
{
errorLabel.Visible = true;
errorLabel.Text = "Thread still alive. Failed to Disconnect";
}
}
}
catch(Exception ex)
{
errorLabel.Visible = true;
errorLabel.Text = "Failed to Disconnect";
}
}
And this is my server class' Process Handler Function:
private void CommandProcessHandler(Socket client)
{
Byte[] data = new Byte[1024];
NetworkStream NetStream = null;
//Exception check
if(client.Connected == true)
NetStream = new NetworkStream(client);
while(bCommandListener == true)
{
//Read the command from the client
int bytes = NetStream.Read(data, 0, 1024);
string Command = Encoding.ASCII.GetString(data, 0, bytes);
//Do something with the command
if (Command == "SHUTDOWN")
{
NetStream.Close();
client.Close();
bCommandListener = false;
continue;
}
//Display the command in the command list box
string buttonPressed;
string buttonLetter;
buttonPressed = CommandDataObject.Instance.DecodeUIDFromMessage(Command);
buttonLetter = CommandDataObject.Instance.DecodeMessageFromUID(Command);
Command = ((client.RemoteEndPoint) as IPEndPoint).Address.ToString() + ">>>" + Command;
UpdateCommandsListBox(Command);
}
}
I'm sure that I'm starting up the threads perfectly fine, but the issue lies in shutting them down. This was an aspect I was never properly taught, and am having trouble learning. As it stands, I press the "log out" button, which should send a message to the server to shutdown, as well as close the TcpClient and NetworkStream. It also sets the isRunning bool, which keeps the client's Receive thread active, to false.
However, as it stands, I keep getting an error when I do this, on line 98 (during the client's Receive function, when BytesReceived = CommandStream.Read) as follows:
An unhandled exception of type 'System.IO.IOException' occurred in System.dll
Additional information: Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall.
I'm not sure what the error is referring to, but I've been stuck on this for a while and want to get it fixed. Any solutions?
When you use NetworkStream, it will throw an IOException if the underlying socket is closed while trying to read, see MSDN documentation . So your code is perfectly fine. You just have to handle the exception.
If you were to read the socket directly, you would just receive 0 when the socket is closed.
This is a program to search for strings from a file. The string required by the client is given from the client side, in my case, using telnet. The program I have written is a server side one. It accepts multiple clients.
But, the problems I am unable rectify are-
It doesn't check for strings from the file.
As soon as the client gets connected, the client cannot type in the strings they want to search in that particular file.
It doesn't send the reply back (i.e. If the string is present in the file or not) to the client. Its only shown on the server side.
How do I proceed further? Could someone tell me where am I going wrong? Could someone please help me out with the code?
This is my try at the program..
class Program
{
static void Main(string[] args)
{
IPAddress ipad = IPAddress.Parse("192.168.0.181");
TcpListener serversocket = new TcpListener(ipad, 8888);
TcpClient clientsocket = default(TcpClient);
Byte[] bytes = new Byte[256];
serversocket.Start();
Console.WriteLine(">> Server Started");
while(true)
{
clientsocket = serversocket.AcceptTcpClient();
Console.WriteLine("Accepted Connection From Client");
LineMatcher lm = new LineMatcher(clientsocket);
Thread thread = new Thread(new ThreadStart(lm.Run));
thread.Start();
Console.WriteLine("Client connected");
}
Console.WriteLine(" >> exit");
Console.ReadLine();
clientsocket.Close();
serversocket.Stop();
}
}
public class LineMatcher //I've jumbled it up here. Don't know what exactly to do..
{
public string fileName = "c:/myfile2.txt";
private TcpClient _client;
public LineMatcher(TcpClient client)
{
_client = client;
}
public void Run()
{
try
{
StreamReader sr = new StreamReader("c:/myfile2.txt");
using (var reader = new StreamReader(_client.GetStream()))
{
string line ="";
int lineNumber = 0;
while (null != (line = sr.ReadLine()))
{
lineNumber += 1;
byte[] data = new byte[1024];
NetworkStream stream = _client.GetStream();
//if (line.Equals(line))
for (int ct = stream.Read(data,0, data.Length-1); 0 < ct; ct = stream.Read(data,0,data.Length-1))
line += Encoding.ASCII.GetString(data, 0, ct);
line = line.Trim();
{
lineNumber.ToString();
data = Encoding.ASCII.GetBytes(line);
_client.Client.Send(data, data.Length, SocketFlags.None);
Console.WriteLine("Line {0} matches {1}", lineNumber, line);
}
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
Console.WriteLine("Closing client");
_client.Close();
}
}
I think you got some pieces in your Run-method swapped - here is a version that should do the job:
public void Run()
{
byte[] data;
try
{
using (var r = new StreamReader("c:/myfile2.txt"))
{
string line ="";
int lineNumber = 0;
while (null != (line = r.ReadLine()))
{
data = Encoding.ASCII.GetBytes(line + "\n");
_client.Client.Send(data, data.Length, SocketFlags.None);
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
Console.WriteLine("Closing client");
_client.Close();
}
Please note that I'm not 100% sure what you are trying to do (I think you want your textfile send line-by-line to your Terminal) - so you might have to change some bits here and there.
Don't know where the Stream-messes in your code came from but I guess you tried various tutorials/snippets and forgot to clean up ;)