I've got problem when I am trying to get access to form's controls from another class. My program is hanging in infinite loop. I know why, but I don't know how to write this correctly.
Here is Form1.cs (to my Form)
public Form1()
{
InitializeComponent();
Load config = new Load();
string[] data = config.readConfig("config.ini");
if (data.Length == 4) { //client
Client run = new Client();
run.startClient(data[1], Convert.ToInt32(data[2]));
}
else if (data.Length == 3) //server
{
Server run = new Server();
run.startServer(Convert.ToInt32(data[1]));
}
}
public void addLog(string dataLog){
richTextBox1.Text += dataLog;
}
and here is Client.cs file:
class Client
{
public void startClient(string ipAddr, int port)
{
Form1 form1 = new Form1();
TcpClient client = new TcpClient();
try
{
form1.addLog("Connecting...");
client.Connect(ipAddr, port);
form1.addLog("Connected to server: " + ipAddr + ":" + port.ToString());
}
catch
{
MessageBox.Show("We couldn't connect to server");
}
}
}
How can I change text value without running each time new form. Maybe There is something like run_once?
The infinite loop is here:
Form1:
//Always runs if the config file is a certain length
Client run = new Client();
Client:
Form1 form1 = new Form1();
Each constructor creates the other object, which in turn creates the first object, ad infintum.
If you need to get the form object to the client don't create a new one!. It doesn't work anyways, as your new form object knows nothing about the old one. Just pass it in:
public Client(Form1 form)
{
//Do whatever with it
}
//Form class
Client c = new Client(this);
Disclaimer: There are usually far better ways to do this, but you'll learn those as you get more familiar with design patterns/architecture.
Related
I'm trying to implement a SCS framework server with WinForms in C# but I am having issues making it work. The server standalone works fine when using it in the Main method (no WinForms). I have a client-sided and a server-sided sample code from the SCS framework that I'm trying to implement in WinForms together with some of my own code. It all went well until I decided to try and use WinForms aswell (I had to move the server code to a class of it's own).
I am not getting any errors when I am starting the server, however I can't connect with the client anymore (worked before I moved the server to a class of its own). Client gets System.Net.Sockets.SocketException. On the server-side, I get the error System.InvalidOperationException when the client is trying to connect.
This is my Form1.cs:
using System;
using System.Windows.Forms;
namespace Server_Test
{
public partial class Form1 : Form
{
public static Form1 Self;
Server server = new Server();
public Form1()
{
InitializeComponent();
Self = this;
}
public void rtb1Text(string text)
{
richTextBox1.AppendText(text);
}
public void rtb2Text(string text)
{
richTextBox2.Text = text;
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text.EndsWith("Start"))
{
button1.Text = "Stop";
server.ServerInit();
}
else if (button1.Text.EndsWith("Stop"))
{
button1.Text = "Start";
// Does nothing atm
}
}
}
}
And this is my Server.cs
using System;
using Hik.Communication.Scs.Communication.EndPoints.Tcp;
using Hik.Communication.Scs.Communication.Messages;
using Hik.Communication.Scs.Server;
namespace Server_Test
{
class Server
{
public void ServerInit()
{
// Create a server that listens 10085 TCP port for incoming connections
var server = ScsServerFactory.CreateServer(new ScsTcpEndPoint(10085));
// Register events of the server to be informed about clients
server.ClientConnected += Server_ClientConnected;
server.ClientDisconnected += Server_ClientDisconnected;
// Start the server
server.Start();
// Form1.Self.rtb1Text("Server has been started successfully.\n");
Console.WriteLine("Server has been started successfully.\n");
}
static void Server_ClientConnected(object sender, ServerClientEventArgs e)
{
Form1.Self.rtb1Text("A new client with ID: " + e.Client.ClientId + " has connected.\n");
// Register to MessageReceived event to receive messages from new client
e.Client.MessageReceived += Client_MessageReceived;
}
static void Server_ClientDisconnected(object sender, ServerClientEventArgs e)
{
Form1.Self.rtb1Text("A client is disconnected! Client Id = " + e.Client.ClientId + "\n");
}
static async void Client_MessageReceived(object sender, MessageEventArgs e)
{
var message = e.Message as ScsTextMessage; // Server only accepts text messages
if (message == null)
{
return;
}
//Get a reference to the client
var client = (IScsServerClient)sender;
Form1.Self.rtb1Text("Client (ID:" + client.ClientId + ") sent a request: " + message.Text + "\n");
switch (message.Text)
{
case "api":
HttpPost httpPost = new HttpPost();
var apiResponse = await httpPost.SendPost("robot_info");
//Send reply message to the client
client.SendMessage(
new ScsTextMessage(
apiResponse,
message.MessageId //Set first message's id as replied message id
));
break;
default:
break;
}
}
}
}
My guess is that I'm doing something wrong when creating a new instance of the Server class and how I'm initializing/starting the server. Might be something else though, I've tried debugging but it didn't make me any smarter.
Any ideas?
This Is the first form:
public partial class MainWindow : Window
{
TcpClient client = null;
public MainWindow()
{
InitializeComponent();
}
private void Connect()
{
client = new TcpClient();
client.Connect("127.0.0.1", 8826);
CreateRoomWindow menuWindow = new CreateRoomWindow(client);
menuWindow.Show();
this.Close();
}
}
This is the second form:
public partial class CreateRoomWindow : Window
{
TcpClient client;
const int createRoomMessage = 206;
public CreateRoomWindow(TcpClient loggedClient)
{
InitializeComponent();
client = loggedClient;
}
}
I tried to pass the TcpClient to the another window
But when I click on the createTheRoom Button its happen:
private void CreateTheRoom_Click(object sender, RoutedEventArgs e)
{
string message = "2060068{\"answerTimeout\":2,\"maxUsers\":22,\"questionCount\":2,\"roomName\":\"Name\"}";
int timedSize = message.Length;
string size = timedSize.ToString().PadLeft(4, '0');
message = Convert.ToString(createRoomMessage) + size + message;
Byte[] data = Encoding.ASCII.GetBytes(message);
NetworkStream stream = client.GetStream();
}
The program down with the exception System.InvalidOperationException
in the last line
So my question is How can I pass the TcpClient To make it work
The problem
Show displays the form and returns control to the caller, so in this code, Close() gets called almost immediately:
CreateRoomWindow menuWindow = new CreateRoomWindow(client);
menuWindow.Show();
this.Close(); //Executes immediately
This is a problem, because there is only one instance you're working with, and if you close it in one place, it's closed to everyone.
Solution #1. Make the child form modal.
You can delay the call until after the form is closed by using ShowDialog instead:
CreateRoomWindow menuWindow = new CreateRoomWindow(client);
menuWindow.ShowDialog();
this.Close(); //Executes after form is closed
Solution #2. Close it when the child form is closed.
Another option is to close it in the "FormClosed" handler for the child form:
CreateRoomWindow menuWindow = new CreateRoomWindow(client);
menuWindow.ShowDialog();
//Doesn't call close any more
public partial class CreateRoomWindow : Window
{
TcpClient client;
const int createRoomMessage = 206;
public CreateRoomWindow(TcpClient loggedClient)
{
InitializeComponent();
client = loggedClient;
}
private void Form1_FormClosed(Object sender, FormClosedEventArgs e)
{
client.Close(); //Closes after we're done with the form
}
}
Note: You must register the Form1_FormClosed handler, which you would typically do by double-clicking the event in the property sheet it in the form designer window in Visual Studio.
I'm creating a game in which I use TCP/IP connection. The problem is that I'm using .Invoke to help me receive and send message.
The program goes like this: I'm my first window, i'm starting and connecting to the server like this :
{
TcpListener listener = new TcpListener(IPAddress.Any, this.port);
listener.Start();
try {
this.client = listener.AcceptTcpClient();
gameWindow = new GameWindow(this.client, true);
gameWindow.StartGame();
}
}
then i'm connecting to it like this:
{
IPEndPoint ipEnd = new IPEndPoint(this.serverIP, this.port);
{
try {
client.Connect(ipEnd);
if (client.Connected) {
gameWindow = new GameWindow(this.client, false);
gameWindow.StartGame();
}
}
}
The constructor for gameWindow (which is a form) looks like this:
public GameWindow(TcpClient thisClient, bool isServer)
{
InitializeComponent();
this.client = thisClient;
this.reader = new StreamReader(thisClient.GetStream());
this.writer = new StreamWriter(thisClient.GetStream());
this.writer.AutoFlush = true;
}
I must wait for the server to send a message to the client, and then start the client ( I have a function .startGame() that uses .ShowDialog() and creates some pictureBoxs)
But nowhere I can get my handle created. I've tried to put this.createHandle() (read about it here) into GameWindow_Load but still not works. If I try to send a message with:
workerSendData.RunWorkerAsync(); I get:
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
What can I do to get my handler created? Using Thread.Sleep will sleep my whole UI, which does not work (a "solution" found on the internet)
My code for sending message :
private void workerSendData_DoWork(object sender, DoWorkEventArgs e)
{
if (client.Connected) {
this.writer.WriteLine(this.toSend); // aici trimitem datele.
// de modificat : aici vom adauga in lista noastra miscarile.
this.Invoke(new MethodInvoker(delegate () { MessageBox.Show("Me:" + this.toSend + "\n"); }));
}
else {
MessageBox.Show("Send failed");
}
workerSendData.CancelAsync();
}
My code for receiving data:
private void workerReceiveData_DoWork(object sender, DoWorkEventArgs e)
{
while (client.Connected) {
try {
this.received = this.reader.ReadLine();
this.myTurn = true;
this.Invoke(new MethodInvoker(delegate () {
MessageBox.Show("This has been received: " + this.received);
/*this.tbReceive.AppendText("You:" + this.received + "\n");*/
}));
this.received = "";
}
catch (Exception x) {
MessageBox.Show(x.Message.ToString());
}
}
}
It seems that you cannot invoke an action before the Window is fully initialized and loaded. Assuming you are working in Windows Forms, there is a solution provided by #Greg D on this question, but it doesn't be to be the safest way to go.
I would suggest that you try to find a way to start the worker only after the window is loaded (for example using the Loaded event), so that the handle is definitely ready and this situation does not occur.
I followed an example at SuperWebSocket discussions to create a tiny web socket server with echo functionality. However, my server can receive and send back only one message, when I try to send second message to it, the connection closes. I use this echo page to test my server.
Here is my code (I use WPF without MVVM here):
public partial class MainWindow : Window
{
private WebSocketServer ws;
public MainWindow()
{
InitializeComponent();
}
private void ConnectButton_Click(object sender, RoutedEventArgs e)
{
var r = new RootConfig();
var s = new ServerConfig();
s.Name = "SuperWebSocket";
s.Ip = "Any";
s.Port = 8089;
s.Mode = SocketMode.Tcp;
var f = new SocketServerFactory();
if (ws != null)
{
ws.Stop();
ws = null;
}
ws = new WebSocketServer();
ws.Setup(r, s, f);
ws.NewMessageReceived += ws_NewMessageReceived;
ws.Start();
}
private void ws_NewMessageReceived(WebSocketSession session, string e)
{
session.Send("Message: " + e);
}
}
I can send messages from the server without problems, but can't receive more than one message without closing the connection. What is the reason of this problem and how do I fix it?
Try replacing:
ws.NewMessageReceived += ws_NewMessageReceived;
with:
ws.NewMessageReceived += new SuperWebSocket.SessionEventHandler<SuperWebSocket.WebSocketSession, string>(ws_NewMessageReceived);
I am rather new to C# and Sockets however I am trying my hand at making a kind of "chat" program. What I am having an issue with at the moment is making the client able to automatically receive a message from the server if one is available.
What I have tried so far:
System.Threading.Thread myThread = new System.Threading.Thread(new
System.Threading.ThreadStart(listenThread));
public static void listenThread()
{
while(true){
Form1 form1 = new Form1();
form1.ReceiveLoop();
}
}
private void ReceiveLoop()
{
clientSocket.ReceiveTimeout = 100;
byte[] receivedBuffer = new byte[1024];
try
{
int rec = clientSocket.Receive(receivedBuffer);
byte[] data = new byte[rec];
Array.Copy(receivedBuffer, data, rec);
updateClient("Received: " + Encoding.ASCII.GetString(data));
}
catch (SocketException e)
{
//MessageBox.Show(e.ToString());
}
}
ReceiveLoop() works when I access from the GUI thread, however it will not work(Throws SocketException, due to timeout) when accessed via myThread.
Any help or suggestions on another way I could approach this would be greatly appreciated :D