I'm coding a chat application with C# Windows Froms. My problem is that, my server can send messages to each connected client but only a client that sent a message can receive a response. I tried to create a function like ReceiveLoop() with an infinite loop inside or add a timer and call Receive() function for each tick but both were unsuccessful due to the app's crash.
How to get the data in real time? I attach my client's app code.
public partial class Form1 : Form
{
private string name;
private Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private void Namebt_Click(object sender, EventArgs e)
{
if (!Test(Nametb.Text)) Nametb.Text = "Nie wpisano nazwy!";
else
{
name = Nametb.Text;
Nametb.Clear();
Nametb.Enabled = false;
Namebt.Enabled = false;
Messageslb.Enabled = true;
timer1.Start();
LoopConnect();
}
}
private void LoopConnect()
{
int attempts = 0;
while (!_clientSocket.Connected)
{
try
{
attempts++;
_clientSocket.Connect(IPAddress.Loopback, 100);
}
catch (SocketException)
{
Messageslb.Items.Clear();
Messageslb.Items.Add("Connection attempts: " + attempts.ToString());
Messageslb.Refresh();
}
}
Messageslb.Items.Add("Connected!");
Messageslb.Refresh();
Messagetb.Enabled = true;
Sendbt.Enabled = true;
}
private bool Test(string text)
{
if (text != "") return true;
return false;
}
private void Sendbt_Click(object sender, EventArgs e)
{
if (!Test(Messagetb.Text)) Messagetb.Text = "Nie wpisano zadnej wiadomosci!";
else
{
Send();
}
}
private void Receive()
{
if(_clientSocket.ReceiveBufferSize > 0)
{
byte[] receivedBuffer = new byte[1024];
int rec = _clientSocket.Receive(receivedBuffer);
byte[] data = new byte[rec];
Array.Copy(receivedBuffer, data, rec);
Messageslb.Items.Add(Encoding.ASCII.GetString(data));
}
}
private void Send()
{
string message = "[" + name + "]: " + Messagetb.Text;
Messagetb.Clear();
byte[] buffer = Encoding.ASCII.GetBytes(message);
_clientSocket.Send(buffer);
Receive();
}
private void Nametb_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Return) Namebt_Click(sender, e);
}
private void Messagetb_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Return) Sendbt_Click(sender, e);
}
private void timer1_Tick(object sender, EventArgs e)
{
Receive();
}
}
You need to run the receive method on a separate task.
_clientSocket.Receive(receivedBuffer);
should be:
private Task _receiving;
private void Receive()
{
if(_clientSocket.ReceiveBufferSize > 0)
{
if(_receiving == null || _receiving.IsCompleted)
_receiving = Task.Run(() =>
{
byte[] receivedBuffer = new byte[1024];
int rec = _clientSocket.Receive(receivedBuffer);
byte[] data = new byte[rec];
Array.Copy(receivedBuffer, data, rec);
Messageslb.Items.Add(Encoding.ASCII.GetString(data));
});
}
}
Otherwise clientSocket.Receive will hang your program.
You will want to make sure Messageslb is thread safe though, that will take further research.
If you're looking for real time communication look into RTC Client for C# https://msdn.microsoft.com/en-us/library/ms946701.aspx. Otherwise I would highly recommend Socket.IO since it is truly simple to use and set up with extremely fast rtc! https://socket.io/get-started/chat
Related
so im new in C# Programming. I have programmed a C# Forms Application in Visual Studio to communicate to/initialze a device over the Serial Port. The communication between the device is COBS coded, so there are no 0x00 bytes except on the end of each message. The messages sent and received have different length.
My problem is at the moment, that the Messages I receive are not complete or start at the middle of a message, so i cannot trigger sent messages on a specific value in the received messages. You can determine end of a message with received 0x00 (0x00 means end of message in COBS coded data)
So what i need is something to handle the complete message and put it in a byte array to analyze i.e. byte[11] for a specific value.
Here is what ive done so far:
private bool b_portopen = false;
private byte[] b_rcv_buffer = new byte[256];
private void button1_Click(object sender, EventArgs e) {
//InitTimer();
if (b_portopen == false)
{
serialPort1.PortName = comboBox1.SelectedItem.ToString();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
serialPort1.Open();
b_portopen = true;
button1.Text = "Close";
button2.Enabled = true;
Console.WriteLine("Serial Port Opened");
}
else if (b_portopen == true)
{
serialPort1.Close();
b_portopen = false;
button1.Text = "Open";
button2.Enabled = false;
Console.WriteLine("Serial Port Closed");
}
}
private async void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
serialPort1.Read(b_rcv_buffer, 0, 256);
//serialPort1.Read(b_rcv_buffer1, 11, 2);
richTextBox1_receive.Invoke(new Action(() =>
{
richTextBox1_receive.AppendText(BitConverter.ToString(b_rcv_buffer) + "\n");
richTextBox1_receive.ScrollToCaret();
}));
switch (b_rcv_buffer[10])
{
case b_state_startup:
do something
case b_state_be_start_conf:
do something
case b_state_keepalive_conf:
do something
case b_state_unprepare_conf:
do something
case b_state_prepare_conf:
do something
}
}
So, i found a solution with using ConcurrentQueue:
ConcurrentQueue<byte> b_rcv_buffer = new ConcurrentQueue<byte>();
private Timer timer2;
public void InitTimer()
{
timer2 = new System.Windows.Forms.Timer();
timer2.Tick += new EventHandler(timer2_Tick);
timer2.Interval = 1; // in miliseconds
timer2.Start();
}
private async void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
int bytes = serialPort1.BytesToRead;
byte[] buf = new byte[bytes];
serialPort1.Read(buf, 0, serialPort1.BytesToRead);
for(int i = 0; i < buf.Length; i++)
{
b_rcv_buffer.Enqueue(buf[i]); //Enqueue every received Byte in Concurrentqueue
}
}
private async void timer2_Tick(object sender, EventArgs e)
{
if (b_rcv_buffer.Contains<byte>(0x00))
{
byte[] array = b_rcv_buffer.ToArray();
richTextBox1_receive.Invoke(new Action(() =>
{
richTextBox1_receive.AppendText(BitConverter.ToString(array) + "\n");
//richTextBox1_receive.ScrollToCaret();
}));
byte ignored;
while (b_rcv_buffer.TryDequeue(out ignored));
}
I'm writing a Chat Program in C# (Windows Forms Application), the solution contains to projects which both consist of one form ( see picture ). While sending messages to each other works, I'm trying to record the conversation session in a .txt file named dateTime.txt using StreamWriter. Creating the file if it does not exist yet works, but whenever I open the text file, it only contains the last string that was written to it instead of containing the whole "conversation".
Does anybody know how to fix this?
This is the code of one of the forms, but since the forms do exactly the same, the code is the same too so i'm only posting the code of one Form. Would be great if somebody knows what I have to change so the whole conversation is recorded in the text file.
namespace Assignment3Client
{
public partial class Chat : Form
{
NamedPipeClientStream clientPipe = new NamedPipeClientStream("pipe2");
NamedPipeServerStream serverPipe = new NamedPipeServerStream("pipe1");
string msg = String.Empty;
string msgStr;
string name;
byte[] ClientByte;
public Chat()
{
InitializeComponent();
}
private void btnStartChat_Click(object sender, EventArgs e)
{
this.Text = "Waiting for a connection....";
if (txtBoxName.Text.Length == 0)
{
MessageBox.Show("please enter a name first.");
}
else
{
name = txtBoxName.Text;
clientPipe.Connect();
serverPipe.WaitForConnection();
if (serverPipe.IsConnected)
{
this.Text = "You are connected, " + name + "!";
btnStartChat.Enabled = false;
btnSend.Enabled = true;
txtBoxMsg.Enabled = true;
txtBoxMsg.Focus();
receiveWorker.RunWorkerAsync();
}
}
}
private void btnSend_Click(object sender, EventArgs e)
{
msg = "[" + name + ": " + DateTime.Now + "] " + txtBoxMsg.Text;
txtBoxChat.AppendText(msg + "\n");
FileWriter(msg);
sendWorker.RunWorkerAsync(msg); //start backgroundworker and parse msg string to the dowork method
txtBoxMsg.Clear();
txtBoxMsg.Focus();
}
private void sendWorker_DoWork(object sender, DoWorkEventArgs e)
{
Byte[] msgByte = System.Text.Encoding.GetEncoding("windows-1256").GetBytes(msg);
serverPipe.Write(msgByte, 0, msg.Length);
}
private void receiveWorker_DoWork(object sender, DoWorkEventArgs e)
{
ClientByte = new Byte[1000];
int i;
for (i = 0; i < ClientByte.Length; i++)
{
ClientByte[i] = 0x20;
}
clientPipe.Read(ClientByte, 0, ClientByte.Length);
msgStr = System.Text.Encoding.GetEncoding("windows-1256").GetString(ClientByte);
receiveWorker.ReportProgress(i, msgStr);
}
private void receiveWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if ((string)e.UserState == String.Empty)
{ MessageBox.Show("no message"); }
else
{
string message = (string)e.UserState;
txtBoxChat.AppendText(message);
FileWriter(message);
txtBoxChat.BackColor = System.Drawing.Color.DarkBlue;
txtBoxChat.ForeColor = System.Drawing.Color.White;
}
}
private void receiveWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (clientPipe.IsConnected)
{
receiveWorker.RunWorkerAsync();
}
else
{
txtBoxMsg.Enabled = false;
btnSend.Enabled = false;
MessageBox.Show("Connection lost");
}
}
private void Chat_Activated(object sender, EventArgs e)
{
txtBoxChat.BackColor = new System.Drawing.Color();
txtBoxChat.ForeColor = new System.Drawing.Color();
}
private void exitMenuStrip_Click(object sender, EventArgs e)
{
this.Close();
}
private void conMenuSrip_Click(object sender, EventArgs e)
{
}
private void errMenuStrip_Click(object sender, EventArgs e)
{
}
public void FileWriter(string message)
{
string path = #"C:\Users\selin\Documents\TAFE\Term 3\dateTime.txt";
FileStream conLog;
if (!File.Exists(path))
{
conLog = new FileStream(path, FileMode.Create);
}
else
{
conLog = new FileStream(path, FileMode.Open);
}
StreamWriter writer = new StreamWriter(conLog);
writer.WriteLine(message);
writer.AutoFlush = true;
writer.Close();
MessageBox.Show("written to file" + message);
}
}
}
in FileWriter(string message) change
conLog = new FileStream(path, FileMode.Open);
to
conLog = new FileStream(path, FileMode.Append);
I'm working on a Chat Application on Windows Form using C# Socket Programming.
Currently, my App send messages as "ME" and receives messages as "FRIEND".
What I want is to add a textbox which asks Client for username and server receives messages as that username. Following is my code:
namespace ServerClientChat
{
public partial class Form1 : Form
{
private TcpClient client;
public StreamReader STR;
public StreamWriter STW;
public string receive;
public string receive2;
public String text_to_send;
public Form1()
{
InitializeComponent();
IPAddress[] localIP = Dns.GetHostAddresses(Dns.GetHostName()); //getting own IP
foreach (IPAddress address in localIP)
{
if (address.AddressFamily == AddressFamily.InterNetwork)
{
textBox3.Text = address.ToString();
}
}
// textBox1.KeyDown += new KeyEventHandler(textBox1_KeyDown);
}
private void button3_Click(object sender, EventArgs e) //Connect to server
{
client = new TcpClient();
//set client side endpoint consisting of client's ip address and port
IPEndPoint IP_End = new IPEndPoint(IPAddress.Parse(textBox5.Text),int.Parse(textBox6.Text));
try
{
client.Connect(IP_End);
if(client.Connected)
{
textBox2.AppendText(">> Connected to Server"+ "\n");
STW = new StreamWriter(client.GetStream());
STR = new StreamReader(client.GetStream());
STW.AutoFlush = true;
backgroundWorker1.RunWorkerAsync(); //start receiving data in background (async means non-blocked communication
backgroundWorker2.WorkerSupportsCancellation = true; //ability to cancel this thread
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
private void button2_Click(object sender, EventArgs e) //Start server
{
TcpListener listener = new TcpListener(IPAddress.Any,int.Parse(textBox4.Text)); //Listens for connections from TCP network clients.
listener.Start();
client = listener.AcceptTcpClient();
STR = new StreamReader(client.GetStream()); //Implements a TextReader that reads characters from a byte stream in a particular encoding.
STW = new StreamWriter(client.GetStream());
STW.AutoFlush = true; //Setting AutoFlush to true means that data will be flushed from the buffer to the stream after each write operation, but the encoder state will not be flushed.
backgroundWorker1.RunWorkerAsync(); //start receiving data in background (async means non-blocked communication
backgroundWorker2.WorkerSupportsCancellation = true; //ability to cancel this thread
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) //to receive data
{
while(client.Connected)
{
try
{
receive = STR.ReadLine();
this.textBox2.Invoke(new MethodInvoker(delegate () { textBox2.AppendText("Friend: " + receive + "\n"); }));
receive = "";
}
catch(Exception x)
{
MessageBox.Show(x.Message.ToString());
}
}
}
private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) //to send data
{
if(client.Connected)
{
STW.WriteLine(text_to_send);
this.textBox2.Invoke(new MethodInvoker(delegate () { textBox2.AppendText("Me: " + text_to_send + "\n"); }));
}
else
{
MessageBox.Show("Send Failed");
}
backgroundWorker2.CancelAsync();
}
private void button1_Click(object sender, EventArgs e) //Send button
{
if(textBox1.Text!="")
{
text_to_send = textBox1.Text;
backgroundWorker2.RunWorkerAsync();
}
textBox1.Text = "";
}
private void Form1_Load(object sender, EventArgs e)
{
DialogResult f = MessageBox.Show("Welcome to My Chat App! Do you want to start your own Server? ", "Welcome!", MessageBoxButtons.YesNoCancel);
if (f == DialogResult.Yes)
{
button3.Enabled = false;
textBox5.Enabled = false;
textBox6.Enabled = false;
}
if (f == DialogResult.No)
{
button2.Enabled = false;
textBox3.Enabled = false;
textBox4.Enabled = false;
}
}
private void label5_Click(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
button1_Click(this, new EventArgs());
}
}
}
}
You can create something like your own protocol and send it to a server.
<username>YourName</username>
<msg>Hello World</msg>
But in real-life applications you should use database with user authorization process.
There is a server and multiple clients. The server accepts the connection requests from multiple clients. The sockets created are stored in an array. There is a list box in my application. On selecting a particular item it refers to the corresponding socket in the array of sockets (eg if I select first item, it will consider first socket in the array). But the problem is - as the clients can connect to the server in random fashion how the server keep tracks of the clients if it has to send data to a particular client.
Here is my code:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
int i=0;
TcpListener listener = new TcpListener(8888);
listener.Start();
while(true)
{
Socket soc = listener.AcceptSocket();
socarray[i] = soc;
i++;
if (i == NUMBEROFCLIENTS)
break;
}
// Thread writetodatabase = new Thread(datawrite);
// writetodatabase.Start();
Application.Run(new Form1());
}
private void button5_Click(object sender, EventArgs e)
{
if (listBox1.Text == "Reader1")
{
reader_flag = 1;
toolStripStatusLabel1.Text = "reader1 selected";
a = toolStripStatusLabel1.Text;
}
if (listBox1.Text == "Reader2")
{
reader_flag = 2;
toolStripStatusLabel1.Text = "reader2 selected";
a = toolStripStatusLabel1.Text;
}
}
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
string a;
public static int reader_flag = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
Form3 formmm = new Form3();
formmm.Show();
}
private void button4_Click(object sender, EventArgs e)
{
Form2 formm = new Form2();
formm.Show();
}
private void button1_Click(object sender, EventArgs e)
{
try
{
byte[] bytesFrom = new Byte[1000];
Program.socarray[0].Receive(bytesFrom);
char[] stuffed = System.Text.Encoding.UTF8.GetString(bytesFrom).ToCharArray();
int i;
char escape='#';
List<char> unstuffed = new List<char>();
for(i=0;i<stuffed.Length;i++)
{
if(stuffed[i]==escape)
{
i++;
unstuffed.Add(stuffed[i]);
}
else
{
unstuffed.Add(stuffed[i]);
}
}
unstuffed.RemoveAt(0);
unstuffed.RemoveAt(unstuffed.Count-1);
char[] final;
final = unstuffed.ToArray();
string foo = new string(final);
textBox1.Text = foo;
System.IO.File.WriteAllText(#"C:\Users\cdac\Desktop\server\server\TextFile2.txt", foo);
}
finally { }
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
Form4 mm = new Form4();
mm.Show();
}
private void toolStripStatusLabel1_Click(object sender, EventArgs e)
{
toolStripStatusLabel1.Text = a;
}
private void button5_Click(object sender, EventArgs e)
{
if (listBox1.Text == "Reader1")
{
reader_flag = 1;
toolStripStatusLabel1.Text = "reader1 selected";
a = toolStripStatusLabel1.Text;
}
if (listBox1.Text == "Reader2")
{
reader_flag = 2;
toolStripStatusLabel1.Text = "reader2 selected";
a = toolStripStatusLabel1.Text;
}
}
private void button6_Click(object sender, EventArgs e)
{
string MyConString = "server=localhost;" +
"database=cdac;"+
"User Id=root;"
+"password=cdac56;";
MySqlConnection connection = new MySqlConnection(MyConString);
MySqlCommand command = connection.CreateCommand();
MySqlDataReader Reader;
connection.Open();
//StreamReader reader = new StreamReader("C:\\tag_log_030610.txt");
StreamReader reader = new StreamReader("C:\\Users\\cdac\\Desktop\\server\\server\\TextFile2.txt");
string line;
while ((line = reader.ReadLine()) != null)
{
string[] parts = line.Split(';');
//command.CommandText = "insert into st_attn(rollno,Th_attn,Name) values('" + parts[0] + "','" + parts[1] + "','" + parts[2] + "')";
command.CommandText = "insert into st_attn(rollno) values('" + parts[0] + "')";
Reader = command.ExecuteReader();
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
}
}
}
Client-Server apps ALWAYS start with a request from the client to the server. No matter if it's a business app, a game, a website, a webservice or even something to tell the time, the client will always first ask the server.
This means that if you're using sockets, you will always have to let the request come from the client. He will send a request to the server. The server will then keep a record internally of where they can find the client. This is kept in the Endpoint property of the Socket. But normally, you will not have to worry about this unless you're working with push apps.
I'm having some issues trying to figure out Async communication on a serialport object and was hoping to get some help. I'm still learning Async stuff so be gentle:)
I basically need to make sure that the buffer isn't full prior to writing to the serial port and also all data has been read before any more data gets written to avoid packet collision. Unfortunately the microcontroller that I am writing to does not have any handshaking or CTSEnabled so I have to mimic it in code.
public partial class Form2 : Form
{
System.IO.Ports.SerialPort port = new System.IO.Ports.SerialPort();
System.Timers.Timer tmrPolling = new System.Timers.Timer(200);
static byte [] inputPacket = new byte[2] { (byte)255, (byte)0 }; // Byte to send to SerialPort when input polling timer is activated.
AsyncCallback callback;
IAsyncResult result;
Form2()
{
InitializeComponent();
port.PortName = "COM9";
port.BaudRate = 38400;
port.DataBits = 8;
port.StopBits = System.IO.Ports.StopBits.One;
port.Parity = System.IO.Ports.Parity.None;
port.ReadTimeout = 1;
port.ReceivedBytesThreshold = 5;
port.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(port_DataReceived);
callback = new AsyncCallback(ReadComplete);
port.ReadBufferSize = (byte)60;
port.WriteBufferSize = (byte)60;
port.Open();
tmrPolling.Elapsed += new System.Timers.ElapsedEventHandler(tmrPolling_Elapsed);
}
void WritePacket(byte[] packet)
{
result = port.BaseStream.BeginWrite(packet, 0, packet.Count(), callback, port); // writes the input packet to the SerialPort - needed to read the IO values
}
void ReadComplete(IAsyncResult result)
{
}
void tmrPolling_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
WritePacket(inputPacket); // writes the input packet to the SerialPort - needed to read the IO values
}
void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
port.BaseStream.EndWrite(result);
// Reads the data coming in from the serial port and calls the thread safe delegate to update the values on the form.
byte [] received = new byte[port.BytesToRead];
result = port.BaseStream.BeginRead(received, 0, received.Count(), callback, port);
port.BaseStream.EndRead(result);
}
private void UpdateValues()
{
byte[] b = new byte[] { (byte)255, (byte)6, (byte)hsbPan.Value, (byte)vsbTilt.Value, (byte)0,
(byte)0, (byte)0, (byte)0, (byte)13, (byte)10 };
WritePacket(b);
}
private void chkEnablePolling_CheckedChanged(object sender, EventArgs e)
{
tmrPolling.Enabled = chkEnablePolling.Checked;
test.Enabled = chkEnablePolling.Checked;
vsbInputInterval.Enabled = chkEnablePolling.Checked;
vsbInputInterval.Value = Convert.ToInt32(tmrPolling.Interval);
txtInputInterval.Enabled = chkEnablePolling.Checked;
txtInputInterval.Text = ((float)tmrPolling.Interval / ( float)1000).ToString() + " sec";
}
private void vsbInputInterval_Scroll(object sender, ScrollEventArgs e)
{
// adjusts the tick frequency of the Timer for input polling.
txtInputInterval.Text = ((float)tmrPolling.Interval / (float)1000).ToString() + " sec";
tmrPolling.Interval = vsbInputInterval.Value;
}
private void vsbTilt_Scroll(object sender, ScrollEventArgs e)
{
UpdateValues();
}
private void hsbPan_Scroll(object sender, ScrollEventArgs e)
{
UpdateValues();
}
}