I have a form that acts as a client GUI for a TCP client/server project. I have multiple User Controls that act as "pages" that the user can navigate using buttons on the main GUI form.
My issue is; each one of these user controls (as well as the main form) needs to be able to communicate with the server (ie. send messages to it).
Currently to accomplish this I'm opening a new connection every time a new user control is added, by placing the following code in my main form, as well as all of the user control "pages":
public partial class MainForm: Form
{
private IPEndPoint serverEndPoint;
private TcpClient myClient = new TcpClient();
public MainForm()
{
InitializeComponent();
serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerIP), 8888);
myClient.Connect(serverEndPoint);
}
}
private void SendMessage(string msg)
{
NetworkStream clientStream = myClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes(msg);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
What I'd like to be able to do is to have this code on my main form only, and have each user control that's added to the main form use the connection that's already open to communicate. I'm just not sure how I would accomplish this.
Wrap the connection in a static class and create static interface for connecting to the server and sending messages. You will only have to open the connection once, in you main form.
static public class ServerCommunicator
{
static private IPEndPoint serverEndPoint;
static private TcpClient myClient = new TcpClient();
static public void Connect()
{
serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerIP), 8888);
myClient.Connect(serverEndPoint);
}
static public void SendMessage(string msg)
{
NetworkStream clientStream = myClient.GetStream();
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] buffer = encoder.GetBytes(msg);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
}
And you main form class becomes:
public partial class MainForm: Form
{
public MainForm()
{
InitializeComponent();
ServerCommunicator.Connect();
// Sending a message:
ServerCommunicator.SendMessage("Hello server!");
}
}
ServerCommunicator.Connect() only needs to be called once, in the main form. The other controls can simply call SendMessage.
Related
I'm working on an Avalonia UI-based desktop app that involves a TcpListener.
The problem I'm running into is the TcpListener seems to stay open even after the app has exited.
public partial class MainWindow : Window
{
private MainWindowViewModel _vm = new MainWindowViewModel();
private TcpListener _listener;
public static ManualResetEvent tcpClientConnected =
new ManualResetEvent(false);
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
_listener = new TcpListener(IPAddress.Any, 1025);
_listener.Start();
Thread smtpThread = new Thread(new ThreadStart(StartSmtpLocal));
smtpThread.IsBackground = true;
smtpThread.Start();
}
void StartSmtpLocal()
{
Console.WriteLine("Starting smtp local");
while (true)
{
tcpClientConnected.Reset();
Console.WriteLine("Waiting for connection...");
_listener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpClientCallback), _listener);
tcpClientConnected.WaitOne();
}
}
public void DoAcceptTcpClientCallback(IAsyncResult ar)
{
TcpListener listener = (TcpListener) ar.AsyncState;
tcpClientConnected.Set();
TcpClient client = listener.EndAcceptTcpClient(ar);
}
}
Everything starts up correctly the first time it's ran, but if I close the window/stop the app...
The TcpListener appears to still be running.
Any idea what is causing this to happen?
I am writing client server app and my server is WinForm.
i am trying to create child form from static method like that
private static void OnClientConnect(IAsyncResult asyn)
{
try
{
TcpClient clientSocket = default(TcpClient);
clientSocket = _listener.EndAcceptTcpClient(asyn);
clientSocket.ReceiveBufferSize = 1024;
frmClient frmClient = new frmClient(clientSocket);
frmClient.Show(this);
}
catch (Exception se)
{
throw;
}
WaitForClientConnect();
}
but "this" is not accepted because i am using it inside static method.
i also tried to create static field holding this and use it.
but also throw cross threads exception.
any suggestions.?
You can store the MDI parent window in a static variable like
public static Form RootForm;
Then you can show a new MDI child, taking cross-threading into account like this.
public static void ShowFormForClient(TcpClient clientSocket) {
// check if we are on a different thread and redirect if so
if (RootForm.InvokeRequired) {
RootForm.Invoke((MethodInvoker) delegate { ShowFormForClient(clientSocket); });
return;
}
var frmClient = new frmClient(clientSocket);
frmClient.MdiParent = RootForm;
frmClient.Show();
}
Note that this code is not tested in any ways, only for demonstration purposes.
I have a List of Sockets that are connected and live on the Form1
List<Socket> Clients;
For Example i want to Pass on of the Socket to the next Forms as:
Form2 F2 = new Form2();
F2.ClientSocket = Clients[2];
So I want to Close the Socket :
Clients[2]
and let the F2.ClientSocket Opened
just like we're passing the connection from socket to socket !
Thanks for your Time !
use static objects:
Form1:
class Form1
{
public static List<Socket> clients = new List<Socket>();
// your codes
}
Form2:
class Form2
{
public Form2
{
var client = Form1.clients[index];
}
}
I'm kinda new to C# and I'm trying to create a Modbus-TCP slave.
All i want to do is to call an event handler when i recieve data from the TCP Master.
namespace Mark_II.Device
{
class Slave_TCP : mSlave
{
short trans_ID;
byte[] Respond;
byte[] MasterMessage;
TcpClient Client;
NetworkStream stream;
public Slave_TCP(String IP, int Port)
{
Client = new TcpClient(IP, Port);
stream = Client.GetStream();
// insert "call event handler" here<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}
private void client_OnReceiveData(object sender, SerialDataReceivedEventArgs e)
{
byte[] message = new byte[Client.ReceiveBufferSize];
stream.Read(message, 0, message.Length);
}
}
}
I have been looking around but i couldn't find anything... please help me.
tl;dr: I'm looking for a way to raise an event, when my Client receives data from a master.
In general, events in C# works like this:
public delegate void MessageHandler(string message);
public class Client
{
public event MessageHandler MessageArrived;
public void CheckForMessage() //logic to check if message is received
{
//other code to check for message
if(MessageArrived != null)
MessageArrived("message received");
}
}
public class DisplayMessage
{
public void DisplayMessage(string message)
{
Console.WriteLine("Message: {0}", message);
}
}
Code to hook up an event:
public class ProcessMessage
{
Client client = new Client();
DisplayMessage msg = new DisplayMessage();
client.MessageArrived += new MessageHandler(msg.DisplayMessage);
client.CheckForMessage();
}
I've the following problem: I created a simple HTTP server component. The server should be controlled with buttons on the GUI. I can start the server without any problems, but if I want to stop the server the whole program is killed. I think that's an error of aborting the thread but I don't know how I can solve this problem.
Here's my code:
public class HttpServer {
private int port;
public HttpServer(int port) {
this.port = port;
}
public void Listen() {
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
try {
while (true) {
TcpClient client = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(client);
Thread thread = new Thread(new ThreadStart(processor.Process));
thread.Start();
Thread.Sleep(1);
}
}
catch { }
listener.Stop();
}
}
public class HttpProcessor {
private TcpClient client;
private StreamReader reader;
private StreamWriter writer;
public HttpProcessor(TcpClient client) {
this.client = client;
this.reader = null;
this.writer = null;
}
public void Process() {
reader = new StreamReader(client.GetStream());
writer = new StreamWriter(client.GetStream());
ParseRequest();
// some method calls to process the request and generate the response
SendResponse();
client.Close();
}
}
public partial class MainForm : Form {
private HttpServer server;
private Thread servthread;
private void Form_Load(object sender, EventArgs e) {
server = new HttpServer(8080);
}
private void Button1_Click(object sender, EventArgs e) {
servthread = new Thread(new ThreadStart(server.Listen));
servthread.Start();
Thread.Sleep(1);
}
private void Button2_Click(object sender, EventArgs e) {
servthread.Abort();
}
}
Do not use Thread.Abort(), ever! Use other means of communicating to the thread that it should stop, like a WaitHandle or even a private volatile bool stopThread; flag!
If you ever feel the need to call any other methods on a Thread than Start and Join you're probably doing something wrong and you should think about your design ;-)
See this: How to: Create and Terminate Threads (C# Programming Guide)
On your comment about AcceptTcpClient being a blocking call: Yes, it is. However, as others have noted too, you could easily change your class to avoid this problem:
public class HttpServer {
private int port;
private TcpListener listener; // Make the listener an instance member
public HttpServer(int port) {
this.port = port;
this.listener = new TcpListener(IPAddress.Any, port); // Instantiate here
}
public void Listen() {
listener.Start();
try {
while (true) {
TcpClient client = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(client);
Thread thread = new Thread(new ThreadStart(processor.Process));
thread.Start();
Thread.Sleep(1);
}
}
catch { }
listener.Stop();
}
public void StopListening()
{
listener.Server.Close();
}
}
Then, instead of servthread.Abort(); you'd call server.StopListening();.
You may need to wrap the listener.Stop() line in a try/catch as well, but you'll have to try.
To make everything "kinda" work "kinda" correctly:
in HttpServer move listener variable from local var to class member
in HttpServer introduce a method:
public void Stop()
{
listener.Stop();
}
Change your Button2_Click method to:
private void Button2_Click(object sender, EventArgs e)
{
server.Stop();
servthread.Join();
}
PS: I assume that this is one of your first projects, so instead of writing a long post of how to do your stuff correctly, I suggested the changes that will allow you to continue your project. Bugs and architecture issues may come or may not come)
Happy learning.